3주차 정리

lyoodong·2023년 7월 31일

3주차 학습 내용

  1. collectionview
  2. VC + table, collectionview
  3. Transition
  4. 계산 속성
  5. 프로토콜, 델리게이트, 옵저버
  6. 싱글톤

collectionview

  • 기본적으로 tableview와 동일한 로직이나 size에 대한 고려가 되어야 한다.
CGSize(width: Double, height: Double) 
  • vertical일 경우, 가로로 인덱스가 부여
  • horizontal일 경우, 세로로 인덱스가 부여

XIB(XML Interface Builder)

  • XIB를 사용해, 스토리 보드와 같은 인터페이스 디자인 및 개발을 수행할 수 있다.
  • XIB는 XML 기반의 파일 형식으로 ‘NIB 파일’로 컴파일되어 iOS 디바이스에서 로드
  1. cell 타입 파일 생성
  2. Xib도 같이 생성하는 옵션 체크
  3. Xib의 IDF 등록
  4. tableview 또는 collectionview에 register()
let nib = UINib(nibName: SampleTableViewCell.IDF, bundle: nil)
tableView.register(nib, forCellReuseIdentifier: SampleTableViewCell.IDF)

동적인 높이를 구성하기 위한 조건

  1. automaticDimension

    tableView.rowHeight = UITableView.automaticDimension
  2. layout(절대적인 값을 주는 것이 아니라, 상대적인 위치로 잡아줄 것)

  3. numOfLines(label이 있을 경우에만)

사용자가 편집가능한 cell 제작

//1. 시스템 편집, 주어진 cell에 대해서 편집가능하게 user가 사용하겠다.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true 
}
    
//2. editing style 지정, default로 삭제UI 탑제
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        
        list.remove(at: indexPath.row)
        tableView.reloadData()
        
}

스토리보드에서 코드 기반 화면전환

  1. 스토리보드 파일찾기
  2. 스토리보드 파일 내 뷰컨트롤러 찾기
    *옵션으로 네비게이션 컨트롤러가 있는 형태로 present하고 싶은 경우, view에 네비게이션 컨트롤러 추가한다.(이때 dismiss()로 backbutton 따로 제작해줘야 한다.)
  3. 화면 전환 방식 설정
  4. 화면 띄우기
//1. 스토리보드 파일찾기
let storyboard = UIStoryboard(name: "Main", bundle: nil)

//2. 스토리보드 파일 내 뷰컨트롤러 찾기
guard let viewController = storyboard.instantiateViewController(withIdentifier: "AddViewController") as? AddViewController else { return }

//2-1(옵션). 네비게이션 컨트롤러가 있는 형태로 present하고 싶은 경우, view에 네비게이션 컨트롤러 추가
let nav = UINavigationController(rootViewController: viewController )

//3. 화면 전환 방식 설정
nav.modalTransitionStyle = .coverVertical
nav.modalPresentationStyle = .fullScreen

//4. 화면 띄우기
present(nav, animated: true)

UILabel.appearance().textColor = .white // 일괄적으로 기본 텍스트 색상 결정하는 것.

계산 속성

in addition to stored properties, classes, structures, and enumerations can define computed properties, which don’t actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly. [애플 공식 문서]
→ 실제로 값을 저장하지는 않지만, getter와 setter를 제공해서 다른 프로퍼티와 값을 간접적으로 검색하고 설정할 수 있다.

struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get { //get 키워드 입력 후 함수처럼 블럭을 생성하고 블럭 내부에 원하는 로직을 작성한다.
							//이후 반드시 리턴(계산 속성의 타입으로)
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
    size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
// initialSquareCenter is at (5.0, 5.0)
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// Prints "square.origin is now at (10.0, 10.0)"

옵저버

앱에서 user가 데이터를 추가하거나 삭제하는 시점에 매번 데이터를 reload 하는 것은 번거로울 수 있다. 이를 해결하기 위해서 사용하는 개념이 옵저버이다. 데이터 자체의 변화를 감지하고, 그에 따라 대응하는 코드를 작성할 수 있다.

  • tableview.reloadata → 데이터가 달라질 때 마다 갱신을 해줘야 한다.
    observer
    var todo = TodoInfomation() {
            didSet { //변수가 달라짐을 감지! -> 프로퍼티 옵저버
                tableView.reloadData()
                print("didset 실행")
            }
        }

화면 간 데이터 이동 로직

  1. 화면 전환이 일어나는 메서드에서 로직 수행
  2. receiver VC에 데이터를 저장할 공간 생성(변수나 구조체 활용)
  3. sender에서 receiver의 인스턴스를 생성
  4. sender에서 보낼 데이터를 receiver 의 저장 공간에 할당
  5. receiver의 저장 공간에 할당된 데이터를 receiver에서 활용


구조체 타입으로 선언해 전체 데이터를 한번에 넘겨주자.
아울렛으로 다이렉트하게 데이터를 이동시킬 수 없다.(중간 다리가 필요)

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        guard let vc = self.storyboard?.instantiateViewController(identifier: DetailViewController.IDF) as? DetailViewController else {return}

        vc.data = todo.list[indexPath.row]
        present(vc, animated: true)
        print("전환")
    }

Type 속성의 초기화

타입 속성은 lazy한 성격을 가지고 있다. 즉, 지연 저장 속성 처럼 속성에 접근하는 순간 초기화가 진행되며 1회만 진행된다. 이는 다수의 스레드에서 해당 속성에 접근하려고 하더라도 보장된다.

<문제상황> : 클래스나 구조체에서 인스턴스를 초기화할 때, 내부의 메서드를 활용해 초기화 하고 싶다. 하지만, 인스턴스 내부 요소들은 동시에 초기화가 진행되기 때문에 이는 불가능하다.
(즉, 메서드를 사용하고 싶으면 메서드 사용 시점에 메서드가 초기화되어 있어야한다.)
→ 이를 해결할 수 있는 것이 메서드를 타입메서드로 선언하면 된다.

struct TodoInfomation {
    //인스턴스 프로퍼티
    var list = [
        toDo(main: "장보기", sub: "셀렉", like: true, done: false, color: randomColor()),
        toDo(main: "고", sub: "0122", like: true, done: false, color: randomColor()),
        toDo(main: "청소", sub: "111", like: true, done: false, color: randomColor()),
    ]
    //인스턴스 메서드 -> 타입 메서드
    static func randomColor()-> UIColor {
        let red = CGFloat.random(in: 0...1)
        let green = CGFloat.random(in: 0...1)
        let blue = CGFloat.random(in: 0...1)
        
       return UIColor(red: red, green: green, blue: blue, alpha: 1)
        
    }
}

데이터

모델에 들어가는 코드는 파운데이션을 임포트했을 때 오류가 나는 코드는 사용하면 관점으로 접근
→ UI요소를 배제해야하기 때문.

awakeFromNib()

nib 파일에 기반한 객체들이 초기화된 다음에 실행되는 메서드. awakeFromNib()은 초기화 과정 이후 view에 속한 아울렛 객체들이 모두 초기화되고 접근 가능한 상태에 호출된다.

스위프트에서 cell은 재사용큐 구조를 활용해, 끊임없이 초기화된다. 이때, 고정된 속성의 경우 매번 초기화할 필요가 없기 때문에 이를 방지할 수 있는 코드가 필요하다.

따라서, awakeFromNib()을 사용해, cell 객체의 공통 설정 등을 한번에 초기화할 수 있다.

cell의 데이터 reload

전체 데이터 리로드, 특정 item(컬렉션 뷰) 이나 row에 대해서만 초기하기 등 다양한 reload 옵션이 존재한다.

유사한 UI를 가진 화면에서 전달되는 데이터만 다를 경우

  1. Bool 타입으로 분기 -> 화면이 3개 이상일 경우 대응 불가
  2. enum 타입으로 case를 분기해서 case에 따라 데이터를 달리 전달한다.
enum TransitionType {
    case add
    case edit
}

override func viewDidLoad() {
        super.viewDidLoad()
     
        switch tpye {
        case .add:
            title = "추가"
            let xmark = UIImage(systemName: "xmark")

            let leftButton = UIBarButtonItem(image: xmark, style: .plain, target: self, action: #selector(closedButtonTapped))
            navigationItem.leftBarButtonItem = leftButton
            navigationItem.leftBarButtonItem?.tintColor = .cyan
        case .edit:
            title = "수정화면"
        }
    }

연산 프로퍼티

//계산 속성 
//get, set -> 기존 데이터의 검증 및 간단한 관리를 위해 사용
//willSet, didSet -> 데이터의 변화를 감지 후 추가적인 액션이 필요한 경우, 옵저버로 활용
//newValue, oldValue 기본적으로 사용할 수 있다. 

class Chicken {
	let name = "치킨"
	var totalOrder = 10

	var newOrder {
		get {
			return totalOrder * 100000
			}
		set {
			totalOrder += newValue
			}
		}
}

// 고정값을 줄 프로퍼티 선언
// will/did set 선언
// 해당 값을 set(변화)할 프로퍼티 선언 

class ChickenPrice {
    
    var price: Int = 20000 {
        willSet { // 데이터 변화 전에 로직 처리
            print("\(price)에서 \(newValue)로 변화")
        }
        didSet { // 데이터 변화 후에 로직 처리
            print("\(oldValue)에서 \(price)로 변화")
        }
    }
    
    var priceChange: Int = 0 {
        didSet {
            price += priceChange
        }
    }
}

protocol

//프로토콜은 명세만 한다. 블럭 내부의 기능, 로직은 프로토콜을 채택한 객체 내부에서 구현한다.
protocol VeiwPresentableProtocol {
    초기화할 수 없음. 
    //    var naviagtionTitle = "네비게이션 이름"
    //    var backgroundColor = .red
    //    var identifier = "identifier"

    //최소 요구사항 이후에 추가하는 것은 본인 마음. 즉 set하는 것은 개발자가 선택적으로 할 수 있는 것이다.
    var naviagtionTitle:String { get } 
    var backgroundColor:UIColor { get }
    var identifier:String { get }
    
    func configureView()
    func configureLabel()
    func configureTextLabel()
}

0개의 댓글