02 - 1. LED 전광판 앱

곰주·2022년 8월 3일
2
post-thumbnail

이전 공부 내용

프로젝트 들어가기 전 알아야 할 개념들

1. UINavigation Controller
2. 화면 전환 방법
3. View Controller Life Cycle
4. 화면 간 데이터를 전달하는 방법
5. 에셋 카탈로그 (Asset Catalog)


3. View Controller Life Cycle

앱이 복잡해질수록 Controller들을 잘 관리해 주어야 하며, 알맞은 타이밍에 내가 원하는 코드를 작성하는 것이 중요하다! 이를 위해서 View Controller Life Cycle을 잘 이해해야 한다,,,

UIViewController 객체에는 View 객체를 관리하는 메서드들이 정의되어 있는데, 이 메서드들은 각자 자신들이 불려져야 하는 타이밍일 때 iOS 시스템에 의해 자동으로 호출된다. UIViewController의 자식 클래스를 생성할 때 UIViewController에 정의된 메서드들을 override 하여 Life Cycle 상황에 맞게 적절한 로직들을 메서드에 추가할 수 있다!

Appearing : 뷰가 화면에 나타나는 중
Apperad : 뷰가 화면에 나타나는 것이 완료된 상태
Disappeaing : 뷰가 화면에서 사라지는 중
Disappeared : 뷰가 화면에서 사라지는 것이 완료된 상태

viewWillAppear()
-> 뷰가 뷰 계층에 추가되고, 화면에 보이기 직전에 매번 호출
-> 다른 뷰로 이동하였다가 돌아오면 재호출
-> 뷰와 관련된 추가적인 초기화 작업을 이 메서드에 정의

viewDidAppear()
-> 뷰 컨트롤러의 뷰가 뷰 계층에 추가된 후 호출됨
-> 뷰를 나타낼 때 필요한 추가 작업이나
-> 애니메이션을 시작하는 작업을 이 메서드에 정의

viewWillDisappear()
-> 뷰 컨트롤러의 뷰가 뷰 계층에서 사라지기 전에 호출됨
-> 뷰가 생성된 뒤 작업한 내용을 되돌리는 작업이나
-> 최종적으로 데이터를 저장하는 작업들을 이 메서드에 정의

viewDidDisappear()
-> 뷰 컨트롤러의 뷰가 뷰 계층에서 사라진 뒤에 호출
-> 뷰가 사라지는 것과 관련된 추가 작업을 이 메서드에 정의

예제 Code

ViewController.swift

override func viewDidLoad() {
        super.viewDidLoad()
        print("ViewController 뷰가 로드되었음.")
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("ViewController 뷰가 나타날 것임.")
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("ViewController 뷰가 나타났음.")
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("ViewController 뷰가 사라질 것임.")
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("ViewController 뷰가 사라졌음.")
    }

예제 Code의 실행 결과 화면

이렇게~!! View Controller Life Cycle은 View의 상태 변화에 따라 시스템에 의해 특정 메서드를 호출한다는 것을 알 수 있따!


4. 화면 간 데이터를 전달하는 방법

(1) 코드로 구현된 화면 전환 방법에서의 데이터 전달 방법

CodePresentViewController.swift

mport UIKit

class CodePresentViewController: UIViewController {
    
    @IBOutlet weak var nameLabel: UILabel!
    var name: String?

    override func viewDidLoad() {
        super.viewDidLoad()
        // 전달받은 name 프로퍼티를 nameLabel에 표시.
        if let name = name {
            self.nameLabel.text = name
            self.nameLabel.sizeToFit()
        }
    }

    @IBAction func tabBackButton(_ sender: UIButton) {
        self.presentingViewController?.dismiss(animated: true)
    }
}

ViewController.swift

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func tabCodePushButton(_ sender: UIButton) {
        // 우선 스토리 보드에 있는 뷰 컨트롤러를 인스턴스화 해 주어야 함.
        
        // 이 메서드에 전환되는 화면의 뷰 컨트롤러 클래스 타입으로 다운 캐스팅 해줌
        // => 아까 CodePush / CodePresentViewController에 정의한 name 프로퍼티에 접근 가능. 그러면 다른 화면으로 푸쉬와 프레젠트 되기 전에 네임 프로퍼티에 값을 넘겨주면 전환된 화면으로 데이터를 전달할 수 있음
        guard let viewController = self.storyboard?.instantiateViewController(identifier: "CodePushViewController") as? CodePushViewController else { return } // Identifier => 전환할 스토리보드 아이디
        // 옵셔널로 반환하기 때문에 가드문으로 처리해야 함.
        
        viewController.name = "Sohee"
        self.navigationController?.pushViewController(viewController, animated: true)
        // 액션 함수 안에서 네비게이션 스택에 코드푸쉬뷰컨트롤러가 푸쉬 되게 코드 작성
    }
    
    @IBAction func tabCodePresentButton(_ sender: UIButton) {
        // 마찬가지로 스토리 보드에 있는 뷰 컨트롤러를 인스턴스화 해 주어야 함.
        guard let viewController = self.storyboard?.instantiateViewController(identifier: "CodePresentViewController") as? CodePresentViewController else { return }
        viewController.name = "Sohee"
        self.present(viewController, animated: true, completion: nil)
        
    }
}

CodePushViewController.swift

mport UIKit

class CodePushViewController: UIViewController {

    @IBOutlet weak var nameLabel: UILabel!
    var name: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 전달받은 name 프로퍼티를 nameLabel에 표시.
        if let name = name {
            self.nameLabel.text = name
            self.nameLabel.sizeToFit()
        }
    }
    
    @IBAction func tapBackButton(_ sender: UIButton) {
        self.navigationController?.popViewController(animated: true)
    }
} 

(2) 델리게이트 패턴을 이용하여 이전 화면으로 데이터 전달하기

`델리게이트 패턴`은 iOS에서 자주 사용되는 디자인 패턴이다! 여기서 델리게이트는 위임자라고 생각하면 된다. 즉! 위임자를 갖고 있는 객체가 다른 객체에게 자신의 일을 위임하는 디자인 형태인 것이다

ViewController.swift

import UIKit

class ViewController: UIViewController, SendDataDelegate {
    
    @IBOutlet weak var nameLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func tabCodePushButton(_ sender: UIButton) {
        // 우선 스토리 보드에 있는 뷰 컨트롤러를 인스턴스화 해 주어야 함.
        
        // 이 메서드에 전환되는 화면의 뷰 컨트롤러 클래스 타입으로 다운 캐스팅 해줌
        // => 아까 CodePush / CodePresentViewController에 정의한 name 프로퍼티에 접근 가능. 그러면 다른 화면으로 푸쉬와 프레젠트 되기 전에 네임 프로퍼티에 값을 넘겨주면 전환된 화면으로 데이터를 전달할 수 있음
        guard let viewController = self.storyboard?.instantiateViewController(identifier: "CodePushViewController") as? CodePushViewController else { return } // Identifier => 전환할 스토리보드 아이디
        // 옵셔널로 반환하기 때문에 가드문으로 처리해야 함.
        
        viewController.name = "Sohee"
        self.navigationController?.pushViewController(viewController, animated: true)
        // 액션 함수 안에서 네비게이션 스택에 코드푸쉬뷰컨트롤러가 푸쉬 되게 코드 작성
    }
    
    @IBAction func tabCodePresentButton(_ sender: UIButton) {
        // 마찬가지로 스토리 보드에 있는 뷰 컨트롤러를 인스턴스화 해 주어야 함.
        guard let viewController = self.storyboard?.instantiateViewController(identifier: "CodePresentViewController") as? CodePresentViewController else { return }
        viewController.name = "Sohee"
        viewController.delegate = self  // self로 초기화하면 델리게이트를 위임받게 됨.
        self.present(viewController, animated: true, completion: nil)
    }
    
    func sendData(name: String) {
        self.nameLabel.text = name
        self.nameLabel.sizeToFit()  // 텍스트에 맞게 Label 사이즈 조정.
    }
}

CodePresentViewController.swift

import UIKit

protocol SendDataDelegate: AnyObject {
    func sendData(name: String)
}

class CodePresentViewController: UIViewController {
    
    @IBOutlet weak var nameLabel: UILabel!
    var name: String?
    weak var delegate: SendDataDelegate?  // weak 키워드, 델리게이트 패턴 사용할 땐 delegate 변수 앞에 붙여줘야함. 메모리 누수 방지를 위해서!
    // 델리게이트 선언 완료 -> 델리게이트 변수는 이를 위임할 준비를 완료한 것

    override func viewDidLoad() {
        super.viewDidLoad()
        // 전달받은 name 프로퍼티를 nameLabel에 표시.
        if let name = name {
            self.nameLabel.text = name
            self.nameLabel.sizeToFit()
        }
    }

    @IBAction func tabBackButton(_ sender: UIButton) {
        self.delegate?.sendData(name: "HanSohee")  // 데이터를 전달받은 뷰 컨트롤러에서 sendData 델리게이트 Protocol을 채택하고, 델리게이트를 위임받게 되면 채택하기 이전 화면에서 뷰 컨트롤러에서 정의된 센드데이터함수가 실행됨.
        self.presentingViewController?.dismiss(animated: true)
    }
}

(3) 세그웨이로 구현된 화면 전환 방법에서 데이터 전달하는 방법

ViewController.swift
기존 코드에 추가

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // SeguePushViewController로 다운 캐스팅
        if let viewController = segue.destination as? SeguePushViewController {
            viewController.name = "sohee"
        }
    }

SeguePushViewController.swift

import UIKit

class SeguePushViewController: UIViewController {

    @IBOutlet weak var nameLabel: UILabel!
    
    var name: String?
    
    // 전환되는 화면에 값을 전달하기 위한 젤 좋은 위치 : 전처리 prepare 메서드(오버라이드 하면 세그웨이를 실행 직전에 시스템에 의해 자동 호출됨)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        if let name = name {
            self.nameLabel.text = name
            self.nameLabel.sizeToFit()
        }
    }
    
    @IBAction func tapBackButton(_ sender: UIButton) {
        self.navigationController?.popViewController(animated: true)
        /*
        self.navigationController?.popToRootViewController(animated: true)  // Back Button을 눌렀을 때 네비게이션 컨트롤러의 첫 화면인 Root View Controller로 이동하게 됨. */
    }
}

실행 화면 및 스토리 보드


5. 에셋 카탈로그 (Asset Catalog)

Xcode에서 프로젝트를 생성하면 Assets.xcassets 폴더가 자동으로 만들어진다. 이 폴더는 앱에서 사용될 다양한 에셋을 관리해 주는 역할을 한다.

Image Set에 1x, 2x, 3x 세 가지 이미지 리소스를 추가하는 이유는 다양한 해상도에서 화질이 깨지지 않는 이미지 리소스를 사용하기 위해서이다!


LED 전광판 앱 완성~!

Code

ViewController.swift

import UIKit

class ViewController: UIViewController, LEDBoardSettingDelegate {
    
    @IBOutlet weak var contentsLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.contentsLabel.textColor = .yellow
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let settingViewController = segue.destination as? SettingViewController {
            settingViewController.delegate = self
            settingViewController.ledText = self.contentsLabel.text
            settingViewController.textColor = self.contentsLabel.textColor
            settingViewController.backgroungColor = self.view.backgroundColor ?? .black
        }
    }
    
    func changedSetting(text: String?, textColor: UIColor, backgroundColor: UIColor) {
        if let text = text {
            self.contentsLabel.text = text
        }
        self.contentsLabel.textColor = textColor
        self.view.backgroundColor = backgroundColor
    }
}

settingViewController.swift

import UIKit

protocol LEDBoardSettingDelegate: AnyObject {
    // 설정값을 전달하는 함수
    func changedSetting(text: String?, textColor: UIColor, backgroundColor: UIColor)
}

class SettingViewController: UIViewController {
    
    @IBOutlet weak var textField: UITextField!
    
    @IBOutlet weak var yellowButton: UIButton!
    @IBOutlet weak var purpleButton: UIButton!
    @IBOutlet weak var greenButton: UIButton!
    
    @IBOutlet weak var blackButton: UIButton!
    @IBOutlet weak var blueButton: UIButton!
    @IBOutlet weak var oragneButton: UIButton!
    
    weak var delegate: LEDBoardSettingDelegate?
    var ledText: String?
    
    // 설정된 값을 델리게이트에 정의한 changedSetting 메서드에 전달하기 위해 이 텍스트컬러와 백그라운드컬러 프로퍼티를 추가하겠음
    var textColor: UIColor = .yellow
    var backgroungColor: UIColor = .black
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.configureView()
    }
    
    // 다시 설정 뷰로 돌아갔을 때도 설정한 값들이 유지되도록 하는 함수
    private func configureView() {
        if let ledText = self.ledText {
            self.textField.text = ledText
        }
        self.changeTextColor(color: self.textColor)
        self.changeBackgroundColorButton(color: self.backgroungColor)
    }
    
    @IBAction func tabTextColorButton(_ sender: UIButton) {
        if sender == self.yellowButton
        {
            self.changeTextColor(color: .yellow)
            self.textColor = .yellow
        }
        else if sender == self.purpleButton
        {
            self.changeTextColor(color: .purple)
            self.textColor = .purple
        }
        else if sender == self.greenButton
        {
            self.changeTextColor(color: .green)
            self.textColor = .green
        }
    }
    
    @IBAction func tabBackgroundColorButton(_ sender: UIButton) {
        if sender == self.blackButton
        {
            self.changeBackgroundColorButton(color: .black)
            self.backgroungColor = .black
        }
        else if sender == self.blueButton
        {
            self.changeBackgroundColorButton(color: .blue)
            self.backgroungColor = .blue
        }
        else if sender == self.oragneButton
        {
            self.changeBackgroundColorButton(color: .orange)
            self.backgroungColor = .orange
        }
    }
    
    @IBAction func tabSaveButton(_ sender: UIButton) {
        self.delegate?.changedSetting(
            text: self.textField.text,
            textColor: textColor,
            backgroundColor: backgroungColor
        )
        self.navigationController?.popViewController(animated: true)
    }
    
    private func changeTextColor(color: UIColor) {
        self.yellowButton.alpha = color == UIColor.yellow ? 1 : 0.2
        self.purpleButton.alpha = color == UIColor.purple ? 1 : 0.2
        self.greenButton.alpha = color == UIColor.green ? 1 : 0.2
    }
    
    private func changeBackgroundColorButton(color: UIColor) {
        self.blackButton.alpha = color == UIColor.black ? 1 : 0.2
        self.blueButton.alpha = color == UIColor.blue ? 1 : 0.2
        self.oragneButton.alpha = color == UIColor.orange ? 1 : 0.2
    }
}

실행 화면


참고 사이트 및 강의 : 패스트 캠퍼스

profile
아기코쟁이 🧑🏻‍💻

0개의 댓글