[iOS 4주차] 과제 IL

황석범·2024년 11월 21일
0

내일배움캠프_iOS_5기

목록 보기
27/76

트러블 슈팅

문제해결: 레이아웃이 깨졌을 때 콘솔창 버그

문제해결: 중간에 Xcode 프로젝트 이름 변경하기


스토리 보드 날리기

1. 메인 스토리보드 삭제

2. 삭제한 스토리보드와 연결되어 있는 부분 다 제거하기

  1. info plist에서 삭제
  2. 프로젝트 타겟 -> Buld Settings에서 삭제

3. sceneDelgate에서 코드 수정해주기

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    // 윈도우. 앱에 반드시 한 개는 필요한 가장 근본이 되는 뷰. 이 위에 뷰가 쌓이기 시작.
    var window: UIWindow?

    // 앱을 시작할 때 세팅해줄 코드들을 작성해주는 곳.
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        //guard let _ = (scene as? UIWindowScene) else { return }
        
        // UIWindowScene 객체 생성.
        guard let windowScene = scene as? UIWindowScene else { return }
        let window = UIWindow(windowScene: windowScene)
        // window 에게 루트 뷰컨트롤러 지정.
        window.rootViewController = CalculatorViewController()
        // 이 메서드를 반드시 작성해줘야만 윈도우가 활성화가 됨
        window.makeKeyAndVisible()
        self.window = window
    }

UIStackView의 addArrangedSubview

  • 스택 뷰에 뷰(예: 버튼, 레이블, 이미지 등)를 추가하는 데 사용됩니다. 스택 뷰는 내부에 있는 모든 뷰를 가로 또는 세로로 자동 정렬하여 배치할 수 있기 때문에, addArrangedSubview를 통해 쉽게 레이아웃을 관리할 수 있습니다.
calculatorView.buttonStack.arrangedSubviews
    .compactMap { $0 as? UIStackView } // 중첩된 UIStackView만 필터링
    .flatMap { $0.arrangedSubviews }  // 중첩된 StackView의 하위 arrangedSubviews 병합
    .compactMap { $0 as? UIButton }  // UIButton만 남김
    .forEach { $0.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside) }
  1. arrangedSubviews에서 UIStackView만 필터링합니다.
  2. 각 UIStackView의 arrangedSubviews 배열을 펼쳐 하나의 배열로 만듭니다.
  3. 펼쳐진 배열에서 UIButton만 남깁니다.
  4. 최종적으로 UIButton에 이벤트를 추가합니다.

추가 검토

buttonStack 자체에 UIButton이 있다면?
만약 buttonStack에 UIStackView뿐 아니라 UIButton도 직접 추가되어 있다면, 코드에서 이를 처리하도록 flatMap 이전에 한 번 더 필터링해야 합니다.

calculatorView.buttonStack.arrangedSubviews
    .flatMap { subview -> [UIView] in
        if let stackView = subview as? UIStackView {
            return stackView.arrangedSubviews
        } else {
            return [subview] // UIButton처럼 직접 추가된 경우 배열로 반환
        }
    }
    .compactMap { $0 as? UIButton }  // UIButton만 남김
    .forEach { $0.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside) }
  1. UIStackView는 arrangedSubviews를 병합하여 배열로 반환.
  2. 나머지 뷰는 배열에 포함시키도록 보장.
  3. 최종적으로 UIButton만 필터링.

Swift에서 StackView와 Button을 “static(정적)“으로 만들 때와 그렇지 않을 때(동적 생성 시)의 차이점

1. Static (정적 생성) vs. Dynamic (동적 생성) 차이점

  • Static: 스토리보드에서 StackView와 Button을 직접 배치하고 레이아웃을 미리 지정합니다. 뷰는 컴파일 시 결정되어 런타임에 변경되지 않습니다.
  • Dynamic: 코드로 StackView와 Button을 런타임에 생성하여 추가합니다. 조건에 따라 UI 요소가 달라질 수 있습니다.

Static의 장점

  1. 더 직관적인 UI 설계: 스토리보드에서 요소를 바로 확인할 수 있어 배치가 직관적입니다. 따라서 간단한 레이아웃은 정적으로 설정하는 것이 빠르고 이해하기 쉽습니다.
  2. 디자인 수정의 용이성: 레이아웃을 눈으로 확인하며 바로 수정할 수 있어 배치와 위치를 조정하기 쉽습니다.
  3. 코드 간결화: UI 요소 생성을 위한 코드가 필요 없으므로 코드가 간단해집니다.

Dynamic의 장점

  1. 유연한 레이아웃: 조건에 따라 버튼 개수나 StackView 구성을 바꿀 수 있어 유동적인 UI가 가능합니다.
  2. 재사용성: 같은 StackView나 버튼을 코드에서 반복적으로 생성하고 사용할 수 있습니다. 이를 통해 코드 재사용이 쉬워집니다.
  3. 런타임 조정: 런타임에서 특정 조건에 따라 UI를 동적으로 변경할 수 있어 복잡한 요구사항을 쉽게 해결할 수 있습니다.

어느 것이 더 좋은가?
간단하고 고정적인 UI라면 static 방식이 더 좋습니다. 반면, UI가 유동적이거나 상황에 따라 요소가 변해야 한다면 dynamic 방식을 선택하는 것이 효율적입니다.


private(set)

  • private(set)는 Swift에서 읽기 전용으로 외부에 공개하되, 내부에서만 수정 가능하도록 설정할 때 사용됩니다.

이유

  • 외부에서 값 변경을 막기 위해

displayText는 현재 값을 보여주는 역할을 합니다. private(set)을 사용하면 외부 코드에서 displayText를 읽을 수는 있지만 직접 변경할 수는 없습니다. 값의 변경은 클래스 내부의 로직에 의해서만 이루어져야 한다는 의도를 명확히 표현합니다.

외부에서 읽기 전용

class Calculator {
    private(set) var displayText: String = "0"
    
    func updateDisplayText(with newValue: String) {
        displayText = newValue
    }
}

let calculator = Calculator()

// 읽기 가능
print(calculator.displayText) // 출력: "0"

// 직접 변경 불가능
// calculator.displayText = "123" // 오류 발생: Cannot assign to property: 'displayText' setter is inaccessible

내부에서 변경 가능

class Calculator {
    private(set) var displayText: String = "0"
    
    func updateDisplayText(with newValue: String) {
        // 내부 로직으로만 값을 변경
        displayText = newValue
    }
}

let calculator = Calculator()
calculator.updateDisplayText(with: "123")
print(calculator.displayText) // 출력: "123"

왜 setter를 제한해야 하나요?

  1. 데이터 무결성 보장
  • displayText를 외부에서 직접 수정할 수 있다면, 예상치 못한 값으로 변경될 수 있습니다. 예를 들어, 계산기의 displayText가 “0.0.1” 같은 잘못된 상태가 될 수 있습니다.
  • private(set)을 사용하면 값 변경은 클래스 내부의 로직에서만 이루어지므로 이런 문제를 방지할 수 있습니다.
  1. 명확한 API 설계
  • 이 필드는 외부에서 읽기 전용 속성으로 설계되어야 합니다. 이를 통해 클래스가 외부에 공개하는 데이터와 내부적으로 관리하는 데이터를 명확히 구분할 수 있습니다.
  1. 유지보수 용이
  • 값 변경은 항상 클래스 내부의 특정 메서드를 통해 이루어지므로, 변경 로직을 쉽게 추적하거나 변경 사항을 관리할 수 있습니다.
profile
iOS 공부중...

0개의 댓글

관련 채용 정보