SSAC_iOS_Day 14 | TIL

린다·2021년 10월 18일
0

SSAC_iOS_Dev

목록 보기
10/33
post-thumbnail

👩‍💻 수업 & 추가 스터디

📂 Protocol

  • 클래스, 구조체 기반의 청사진
  • Swift의 Class는 단일상속이라서 다른 클래스를 추가적으로 상속받고 싶어도 불가능하다. 하지만 이외에도 확장이 하고 싶을 때 프로토콜을 사용할 수 있다.
  • 프로토콜 내에서 실질적인 구현은 하지 않는다. 이름만 명시!

Property Requirements

  • 프로퍼티의 타입과 get, set만 명시.
  • 연산 프로퍼티/저장 프로퍼티로 사용할지는 중요하지 않음.
protocol NavigationUIProtocol {
    var titleString: String { get set }
    var mainTintColor: UIColor { get }
}

class JackViewController: UIViewController, NavigationUIProtocol {
    var titleString: String = "나의 일기"
    var mainTintColor: UIColor = .black // get만 가능하다고 했는데 왜 set도 가능? -> get만 가능하다고 한 경우, get은 필수인데 set은 선택. 그니까 구현해도 ㄱㅊ음!!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = titleString
        view.backgroundColor = mainTintColor
    }
}

class Jack2ViewController: UIViewController, NavigationUIProtocol {
    var titleString: String {
        get {
            return "나의 일기"
        }
        set {
            title = newValue
        }
    }
    var mainTintColor: UIColor {
        get {
            return .black
        }
        set {
            view.backgroundColor = newValue
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        titleString = "새로운 일기"
    }
}

Method Requirements

protocol OrderSystem {
    func recommendEventMenu()
    func askStampcard(count: Int) -> String
}

class StarBucksMenu {
    
}

class Smoothie: StarBucksMenu, OrderSystem {
    func recommendEventMenu() {
        print("스무디 이벤트 안내")
    }
    
    func askStampcard(count: Int) -> String {
        return "\(count)잔 적립 완료"
    }
}

class Coffee: StarBucksMenu, OrderSystem { 
    func recommendEventMenu() {
        print("커피 이벤트 안내")
    }
    
    func askStampcard(count: Int) -> String {
        return "\(count * 2)잔 적립 완료"
    }
}

Optional Requirements

optional을 사용하려면 반드시 @objc를 작성해줘야한다.

@objc
protocol 프로토콜이름 {
	@objc optioanl func 메서드이름{}
}

Iniializer Requirements

  • 초기화 구문: 구조체가 멤버와이즈 구문이 있더라도 프로토콜을 사용하면 초기화를 무조건 해줘야함(원래 구조체는 초기화구문이 따로 필요없었음!!)
protocol OrderSystem {
    func recommendEventMenu()
    init()
}

class Smoothie: StarBucksMenu, OrderSystem {
    func recommendEventMenu() {
        print("스무디 이벤트 안내")
    }
    
    // 부모에서도 init이 있을 수 있기 때문에 구별을 위해서 required를 붙임
    required init() {
    }
}

AnyObjects

프로토콜을 클래스에서만 사용하게 제한하고 싶은 경우에는 AnyObject를 상속해주면 된다. 해당 키워드가 작성된 프로토콜은 Struct에서 사용할 수 없다.

protocol OrderSystem: AnyObject {
    func recommendEventMenu()
}

📂 Enumeration CaseIterable

  • Enum을 잘 활용하면 tableView의 SectionTitle과 row별 title을 코드로 쉽게 적용시킬 수 있다.
enum SettingSection: Int, CaseIterable {
    case authorization
    case onlineShop
    case question
    
    var description: String {
        switch self {
        case .authorization:
            return "알림 설정"
        case .onlineShop:
            return "온라인 스토어"
        case .question:
            return "Q&A"
        }
    }
}
class SettingViewController: UIViewController {
    @IBOutlet var setttingTableView: UITableView!
    
    let settingList = [
        ["위치 알림 설정", "카메라 알림 설정"],
        ["교보 문고", "영풍 문고", "반디앤루니스"],
        ["앱스토어 리뷰 작성하기", "문의하기"]
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setttingTableView.delegate = self
        setttingTableView.dataSource = self

        let nibName = UINib(nibName: DefaultTableViewCell.identifier, bundle: nil)
        setttingTableView.register(nibName, forCellReuseIdentifier: DefaultTableViewCell.identifier)
    }
    
}
extension SettingViewController: UITableViewDelegate, UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        // CaseIterable 프로토콜 사용해서 가능한 코드!
        return SettingSection.allCases.count
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return settingList[section].count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: DefaultTableViewCell.identifier, for: indexPath) as? DefaultTableViewCell else {
            return UITableViewCell()
        }
        
        cell.iconImageView.backgroundColor = .blue
        cell.titleLabel.text = settingList[indexPath.section][indexPath.row]
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return SettingSection.allCases[section].description
    }
}

📂 Pass Data Between ViewControllers

  1. 보여줄 화면에서 데이터를 받을 공간 만들기
var movieData: Movie?
  1. 값을 받은 후 화면에 적용시키기(어디에 사용할지 표시하기)
titleTextField.text = movieData?.title
overviewTextView.text = movieData?.overview
  1. 이전 화면에서 값 전달하기
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let sb = UIStoryboard(name: "Movie", bundle: nil)
        
        let vc = sb.instantiateViewController(withIdentifier: "detailBoxOfficeViewController") as! detailBoxOfficeViewController
        
        let data = movieInformation.movie[indexPath.row]
        vc.movieData = data <<
        self.navigationController?.pushViewController(vc, animated: true)
    }

➡️ 이 방식은 넘어간 화면에서 다시 이전 화면으로 돌아갈 때는 값을 전달하기 어렵다는 단점이 있음

📂 XIB

  • 중복되는 셀 디자인을 모듈로 빼서 디자인하는 방식.
    자주 사용되는 디자인의 경우 XIB을 사용하면 훨씬 편리하다.
  • Table View Cell 파일 생성과 동시에 Also create XIB file에 체크하면 XIB 파일을 생성할 수 있다.
  • 원하는 UI 컴포넌트 배치 후 오토레이아웃을 설정해준다. 잊지않고 인스펙터 창에서 Reuse Identifier도 설정해준다. 자동으로 생성한 tableViewCell 클래스와 연결이 돼있기 때문에 Assistance를 활용하여 IBOutlet들 또한 연결시켜준다.
class DefaultTableViewCell: UITableViewCell {
    static let identifier = "DefaultTableViewCell"
    
    @IBOutlet var iconImageView: UIImageView!
    @IBOutlet var titleLabel: UILabel!
}
  • tableViewCell 클래스 내에 static let identifier = "DefaultTableViewCell" 다음과 같이 코드를 작성해주면 tableViewController에서 identifier를 직접 작성하는 것이 아니라 tableViewCell 클래스 내의 프로퍼티를 활용하여 적용해줄 수 있다.
let nibName = UINib(nibName: DefaultTableViewCell.identifier, bundle: nil)
        setttingTableView.register(nibName, forCellReuseIdentifier: DefaultTableViewCell.identifier)
  • viewController의 viewDidLoad() 메서드 내에 위의 코드를 작성해줘야 만들어둔 XIB 파일을 테이블뷰에 등록하여 사용할 수 있다.

📂 기타

  • Using UserDefaults to save Custom Struct or Class
    UserDefaults는 애초에 '설정'과 같은 간단한 정보를 저장하는데 특화된 기능이다.
    따라서 우리가 직접 만들어서 사용하는 struct나 class와 같은 경우에는 저장하는데 적합하지 않다.
    UserDefaults가 정보를 저장하는 과정은 요청 ➡️ Struct ➡️ Data ➡️ Plist 순으로 진행된다.
    기본 자료형의 경우에는 struct ➡️ data로 변환하는 코드가 이미 다 준비돼있기 때문에 변환하는 코드를 추가적으로 작성해줄 필요가 없다. 하지만 struct/class의 경우에는 변환 코드가 준비돼있지 않기 때문에 직접 변환하는 코드를 작성해줘야한다. 지난 시간에 saveData, loadData 코드를 통해 해당 과정을 구현했었다.
    이러한 과정을 거치지 않고 struct/class를 저장하고자 할 때는 NSKeyedArchiver를 사용하면 된다고 한다.(추후 학습 예정)
  • Swift File remove/add
    ➡️ 파일 삭제
    Move to trash vs Remove Reference
    : Move to trash를 권장!
    참조만 지우는 경우에는 완벽하게 삭제되지 않기 때문에 파일이 삭제된다기 보다는 Xcode 프로젝트 중 목록에서 보이는 것만 지워진다. 추후에 동일한 이름의 스위프트 파일을 추가하는 경우 문제가 발생할 수 있다.
    ➡️ 파일 추가
    Destination, Create folder references
    : 외부에서 파일 추가할 때는 Copy items if needed, Create groups, add to targets 총 3가지 옵션을 꼭 선택해주는 것이 좋다.
    파일을 Reference로 추가하는 경우 추후에 해당 파일을 삭제하는 경우 Xcode에서도 삭제되어 파일을 찾을 수 없어 빌드되지않는 상황에 닥칠 수 있다.
  • Storyboard References
    Main 스토리보드에서 reference 앞에 navigation controller embed 해주는 것이 더 편함.
    이동할 스토리보드 파일에 가서 해당 뷰 컨트롤러에 embed 해줘도 되지만 tab bar 아이콘을 스토리보드에서 직접 수정하고자 한다면 Main 스토리보드에서 embed해주는 것이 편리함!

👩‍💻 Mission

📂 Protocol Oriented Programming in Swift

0개의 댓글