SSAC_iOS_Day 13 | TIL

린다·2021년 10월 15일
0

SSAC_iOS_Dev

목록 보기
9/33
post-thumbnail

👩‍💻 수업 & 추가 스터디

📂 ViewController Transition

ViewController Identity

한 스토리보드 내에 여러 viewController를 가질 수 있는데 이때 각각의 viewController를 identify할 수 있는 기준은 인스펙터창의 Identity - Storyboard ID값이다.

modalPrsentationStyle

  • fullScreen
  • automatic
  • currentContext
  • custom
  • formSheet
  • none
  • overCurrentContext
  • overFullScreen
  • pageSheet
  • popover

modalTransitionStyle

  • flipHorizontal
  • partialCurl
  • coverVertical
  • crossDissolve

Push - Pop(우측에서 등장)

  • Navigation Controller가 embed된 상황에서 화면을 띄우는 것: Push
  • back 버튼을 눌러서 화면을 없애고 이전 화면으로 돌아가는 것: Pop
//let storyBoard = UIStoryboard(name: "Main", bundle: nil)
        
// self를 작성해주면 자기 자신이 가지고 있는 스토리보드가 뭔지 바로 알 수 있음!
let vc = self.storyboard?.instantiateViewController(withIdentifier: "BoxOfficeTableViewController") as! BoxOfficeTableViewController

// 3. Push
// navigation controller가 Embed 돼있는 경우에만 작동~
// 즉 스토리보드에서의 구조와 코드에서 작동하는 구조가 동일해야함~
self.navigationController?.pushViewController(vc, animated: true)

Present - Dismiss(아래 ➡️ 위)

  • UIView class를 상속받은 클래스에서의 화면 전환 방법
// 1. 스토리보드 특정
let storyBoard = UIStoryboard(name: "Main", bundle: nil)

// 2. 어떤 뷰 컨트롤러로 전환을 해줄지, 스토리보드 내 많은 뷰 컨트롤러 중, 전환하고자 하는 뷰 컨트롤러 가져오기
// 스토리보드랑 클래스 파일을 매칭한거!
// 이때 vc의 타입은 UIViewController
let vc = storyBoard.instantiateViewController(withIdentifier: "MemoTableViewController") as! MemoTableViewController

// 2-1. 네비게이션 컨트롤러 임베드 시키기!
let nav = UINavigationController(rootViewController: vc)

// (옵션) 스타일 설정이 가능함
nav.modalPresentationStyle = .fullScreen // 탭바는 살아있는 풀스크린
nav.modalTransitionStyle = .flipHorizontal

// 3. 어떻게 화면 전화할지 선택하면 됨! (present)
self.present(nav, animated: true, completion: nil)

Adding backButton Programmatically

  • 버튼을 눌러서 pop해야되는 경우 스토리보드에서 직접 추가하기 어렵기 때문에 코드로 추가해줘야한다.
        navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .plain, target: self, action: #selector(closeButtonClicked))
        
        @objc func closeButtonClicked() {
        self.dismiss(animated: true, completion: nil)
    }
    // 이 경우에는 present로 화면을 띄웠기 때문에 dismiss로 화면을 없애줘야한다.

📂 Storyboard

만약 첫 화면으로 띄우고싶은 스토리보드 파일을 변경하고 싶다면
1. Main Interface에서 파일 이름을 변경해준다.

2. Info.plist에서 Storyboard Name까지 변경해주면 시작하는 파일을 변경할 수 있다.

📂 Target - Action Design Pattern

override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .plain, target: self, action: #selector(closeButtonClicked))
}
    
@objc func closeButtonClicked() {
    self.dismiss(animated: true, completion: nil)
}
  • 한 객체에 target을 연결해두고 지정한 이벤트가 발생하면 타겟이 액션을 취하는 디자인 패턴
  • target: 누가
  • action: 무엇을 수행할지
  • for: 어떤 사건이 발생하면

@Objc

  • selector란 메서드를 식별할 수 있는 고유한 이름.
  • UIKit 내부의 Objective-C 런타임으로 실행되는 메서드가 selector를 파라미터로 전달받을 때 전달에 필요한 selector instance를 생성하기 위해 사용한다.
let selector = #selector(selectorName)
  • selector를 생성할 메서드는 @objc를 붙여야한다.
  • #selector()에 순수 swift 함수를 전달하면 인식하지 못한다. Objective-C에 인식시키위해서 메서드 앞에 @objc를 붙여줘야함.

📂 Protocol Pattern

참고 링크: 추후에 심화학습 후 다시 읽어볼 예정

  • 특정 기능을 수행하기 위한 요구사항
  • 구조체, 클래스, 열거형은 프로토콜을 채택하여 요구사항을 실제로 구현할 수있다.
  • 프로토콜 내부에서는 정의만 하고 실제로 구현하지 않는다.
class detailBoxOfficeViewController: UIViewController, UITextViewDelegate, UIPickerViewDelegate, UIPickerViewDataSource
  • 위의 코드는 viewController class를 정의하는 코드다. 이 중, UIViewController는 상속한 class, 나머지 UITextViewDelegate 등은 모두 프로토콜이다! (몰랐음)

  • 프로토콜의 기본 형태

protocol 프로토콜이름 {
	프로토콜 내용
}
  • 프로토콜 채택: 프로토콜을 채택하는 방법은 class가 상속하는 방법과 동일하다. 하지만 이미 class를 상속한 subClass의 경우 상속한 superClass를 가장 앞에 명시해야한다. ( + 다른 얘기지만 swift에서 상속은 단일 상속만 가능하다!! )
  • 프로토콜에서는 프로퍼티가 저장 프로터인지 연산 프로퍼티인지 명시하지 않는다. 이름, 타입, gettable, settable 한지 명시한다. 항상 변수로 선언해야함!
protocol Introduce {
	var name: String { get set }
    var age: Int { get }
    
    func introduct()
}

class Human {}

class Jack: Human, Introduce {
	var name: String = "jack"
    var age: Int = 25
    
    func introduce() {
    	print("자기소개하기")
    }
}
  • TableView를 사용하는 경우 채택하는 UITableViewDataSource, UITableViewDelegate 모두 프로토콜이다. 이를 채택했을 때 반드시 정의해야하는 numberOfRowsInSection, cellForRowAt 메서드 모두 프로토콜 내부에 정의되어있다.
  • UITableViewDataSource 내부를 들여다보면 다음과 같이 코드가 구현돼있다. optional이 붙어있지 않은 가장 위의 두 메서드가 항상 우리가 프로토콜을 채택했을 때 구현해야하는 메서드임을 볼 수 있다. 내부 코드를 통해 내용은 정의되어있지 않음을 확인할 수 있다.
public protocol UITableViewDataSource : NSObjectProtocol {

    
    @available(iOS 2.0, *)
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int

    
    // Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
    // Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
    
    @available(iOS 2.0, *)
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

    
    @available(iOS 2.0, *)
    optional func numberOfSections(in tableView: UITableView) -> Int // Default is 1 if not implemented

    
    @available(iOS 2.0, *)
    optional func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? // fixed font style. use custom view (UILabel) if you want something different

📂 Delegate Pattern

  • 클래스, 구조체의 인스턴스에 특정 행위에 대한 책임을 넘기는 디자인 패턴
  • Delegate Pattern 구현을 위해서는 3가지가 필요하다.
  1. 수신자(Sender) - 사장
  2. 대리자(Receiver) - 비서
  3. 해야할 일의 목록(Protocol) -> 결국 이 일들을 하는 주체: 사장(수신자)
  • Delegate는 프로토콜을 반드시 동반한다.

📂 Xcode 실습

UIViewController + UITableView

UIPickerView

  • 위 사진처럼 wheel 형태의 선택지가 제공되는 view가 UIPickerView다.
  • pickerView도 사용하려면 dataSource, delegate 설정해줘야한다.
@IBOutlet weak var titleTextField: UITextField!

override func viewDidLoad() {
// IBOutlet으로 연결된 textField를 클릭했을 때 pickerView가 뜨도록 설정하는 코드
	let pickerView = UIPickerView()
        pickerView.dataSource = self
        pickerView.delegate = self
        titleTextField.inputView = pickerView
}

let pickerList: [String] = ["감자", "고구마", "파인애플", "자두", "복숭아"]

func numberOfComponents(in pickerView: UIPickerView) -> Int {
// 총 돌아가는 Wheel, 구역을 몇 개로 설정할 것 인지
        return 1
    }
    
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
// 몇 개의 선택지를 제공할건지
    return 5
}

// 위의 두 메서드가 pickerView 프로토콜의 필수 메서드

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
// 각 row별 title 설정하는 메서드
    return "\(pickerList[row])"
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// 각 row별 선택했을 때 사용되는 메서드
    print("\(row)")
}

UITextField

  • InputView
    : TextField에는 custom InputView를 설정해줄 수 있다. 설정해준 InputView는 TextField가 first responder가 됐을 때 보여진다.
  • UITextFieldDelegate

UITextView

  • TextView는 TextField와 달리 place holder를 직접적으로 설정해줄 수 없다. 따라서 원한다면 코드로 설정해주어야 한다.
@IBOutlet weak var overviewTextView: UITextView!

override func viewDidLoad() {
	overviewTextView.delegate = self
    
    // 초기에 어떤 모양으로 세팅해줄건지
    // 내용, 색상 설정
    overviewTextView.text = "overviewTextView"
    overviewTextView.textColor = .lightGray
}

// 커서 시작
func textViewDidBeginEditing(_ textView: UITextView) {
// 아직 글자가 써지지 않은 상태, place holder가 있는 상태를 글자색으로 구분
// 여기서 textView로 설정하면 뷰컨트롤러 내의 delegate가 연결된 모든 textView에 설정이 적용된다. 
// 만약 특정 textView에만 설정하고 싶은 경우에는 IBOutlet 이름으로 설정해주면 됨
	if textView.textColor == .lightGray {
    	textView.text = nil
        textView.textColor = .black
    }
}

// 커서 끝
func textViewdidEndEditing(_ textView: UITextView) {
// editing이 끝났는데 내용이 비어있다면 다시 Placeholder 있던 상태로 설정해줌
	if textView.text.isEmpty {
    	textView.text = "이 곳에 줄거리를 남겨보세요."
        textView.textColor = .lightGray
    }
}
  • 카카오톡에서 채팅 치다가 해당 채팅방 나갔다와도 내용이 남아있을 수 있는 이유
    ➡️ textViewDidEndEditing 시점 전에는 사용자가 save 버튼을 누르지 않았어도 저장시켜주는 메서드를 실행해서!

👩‍💻 Mission

📂 SheetViewController

WWDC: 보고 내용 요약할 예정

0개의 댓글