이 내용은 4월24일(입문과정)에 만들었던 코드를 6월12일(실무과정)에서 Navigation의 데이터 전달에 대해서 내용을 발전시킨과정이다.
당시에는 새로운 swift파일을 만들고 클래스를 생성하고, static을 활용하여 다음 뷰컨트롤러 간 데이터를 전달해줬다.
하지만, 이번엔 방식을 바꿔 Protocol을 활용하여 뷰컨 간의 데이터를 전달받았다.
struct Message{
static var message = ""
static var isOn = true
}
두개의 ViewController 사이에서
static var message를 활용하여, textField의 텍스트를 주고 받을 것이다.
static var isOn 을 활용하여, 전구의 꺼지고 켜진 상태값을 주고 받을 것이다.
class ViewController: UIViewController {
@IBOutlet weak var tfMessage: UITextField!
@IBOutlet weak var imgView: UIImageView!
let imgOn = UIImage(named: "lamp_on.png")
let imgOff = UIImage(named: "lamp_off.png")
var isOn = true
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
imgView.image = imgOn
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let editViewController = segue.destination as! EditViewController
if segue.identifier == "editButton"{
editViewController.textWayValue = "Segue: Use Button!"
}else{
editViewController.textWayValue = "Segue: Use BarButton!"
}
Message.message = tfMessage.text!
}
override func viewWillAppear(_ animated: Bool) {
tfMessage.text = Message.message
if Message.isOn{
imgView.image = imgOn
}else{
imgView.image = imgOff
}
}
}// ViewController
import UIKit
class EditViewController: UIViewController {
@IBOutlet weak var lblWay: UILabel!
@IBOutlet weak var tfMessage: UITextField!
@IBOutlet weak var lblOnOff: UILabel!
@IBOutlet weak var swIsOn: UISwitch!
var textWayValue: String = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
lblWay.text = textWayValue
tfMessage.text = Message.message
swIsOn.isOn = Message.isOn
if Message.isOn{
lblOnOff.text = "On"
}else{
lblOnOff.text = "Off"
}
}
@IBAction func btnDone(_ sender: UIButton) {
Message.message = tfMessage.text!
navigationController?.popViewController(animated: true)
}
@IBAction func swImageOff(_ sender: UISwitch) {
if sender.isOn{
Message.isOn = true
lblOnOff.text = "On"
}else{
Message.isOn = false
lblOnOff.text = "Off"
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
} // EditViewController
didMessageEditDone 메서드 : EditViewController에서 함수를 호출하며, message를 전달해줌.
didImageOnOffDone 메서드 : EditViewController에서 함수를 호출하며, isOn 상태를 전달해줌.
위 두가지 약속을 준수하는 Message Protocol을 선언해준다.
protocol MessageProtocol {
func didMessageEditDone(_ controller : EditViewController, message :String)
func didImageOnOffDone(_ controller : EditViewController , isOn : Bool)
}
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tfMessage: UITextField!
@IBOutlet weak var imgView: UIImageView!
let imgOn = UIImage(named: "lamp_on.png")
let imgOff = UIImage(named: "lamp_off.png")
var VisOn = true
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
imgView.image = imgOn
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let editViewController = segue.destination as! EditViewController
if segue.identifier == "barButtonItem" {
editViewController.textWayValue = "Seuge : Use BarbuttonItem"
}else{
editViewController.textWayValue = "Segue : Use EditButton"
}
editViewController.textMessage = tfMessage.text!
editViewController.delegate = self
editViewController.EisOn = VisOn
}
}
extension ViewController : MessageProtocol {
func didImageOnOffDone(_ controller: EditViewController, isOn: Bool) {
if isOn == true{
imgView.image = imgOn
self.VisOn = true
}else{
imgView.image = imgOff
self.VisOn = false
}
}
func didMessageEditDone(_ controller: EditViewController, message: String) {
tfMessage.text = message
}
}
import UIKit
class EditViewController: UIViewController {
@IBOutlet weak var lblWay: UILabel!
@IBOutlet weak var tfMessage: UITextField!
@IBOutlet weak var lblOnOff: UILabel!
@IBOutlet weak var swIsOn: UISwitch!
var textWayValue = ""
var textMessage = ""
var EisOn = true
var delegate : MessageProtocol?
override func viewDidLoad() {
super.viewDidLoad()
lblWay.text = textWayValue
tfMessage.text = textMessage
swIsOn.isOn = EisOn
if EisOn == true {
lblOnOff.text = "On"
}else{
lblOnOff.text = "Off"
}
// Do any additional setup after loading the view.
}
@IBAction func btnDone(_ sender: UIButton) {
delegate?.didImageOnOffDone(self, isOn: EisOn)
delegate?.didMessageEditDone(self, message: tfMessage.text!)
navigationController?.popViewController(animated: true)
}
@IBAction func swImageOnOff(_ sender: UISwitch) {
if sender.isOn {
EisOn = true
lblOnOff.text = "On"
}else{
EisOn = false
lblOnOff.text = "Off"
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
btnDone을 눌렀을 때, MessageProtocol의 didImageOnOffMessage의 isOn 값에 EditViewController 의 EisOn 값이 저장되도록 한다.
btnDone을 눌렀을 때, MessageProtocol의 didMessageEditDone의 message에 EditViewController 의 tfMessage 값이 저장되도록 한다.
스위치 sender 값에 isOn을 줬을 때, EisOn = true 으로 주고, 라벨이 On이 될 수 있도록 한다. false 조건일 때는 EisOn = false으로 주고, 라벨이 Off되도록 한다.
프로토콜은 특정 역할을 하기 위한 method, property 기타 요구사항 등의 청사진을 정의함. (?)
구조체, 클래스, 열거형은 프로토콜을 채택해서 특정 기능을 실행하기 위한 프로토콜의 요구사항을 구현 할 수 있음.
어떤 프로토콜의 요구사항을 모두 따르는 타입은 '해당 프로토콜을 준수한다'라고 표현함. 하지만,
프로토콜은 정의를 하고 제시를 할 뿐이지 스스로 기능을 구현하진 않음.
struct SomeStruct : AProtocol, AnotherProtocol {
// 구조체 정의
}
class SomeClass : AProtocol, AnotherProtocol {
// 클래스 정의
}
enum SomeEnum : Aprotocol, AnotherProtocol {
// 열거형 정의
}
각 타입은 Aprotocol과 AnotherProtocol을 채택 한 것임. 만약, 클래스가 다른 클래스를 상속받는다면 상속 받을 클래스 이름 다음에 채택할 프로토콜을 나열해준다.
class SomeClass : SuperClass, AProtocol, AnotehrProtocol
SomeClass는 SuperClass를 상속받기와 동시에 AProtocol과 AnotherProtocol 프로토콜을 채택한 클래스가 되는것임.
즉, 한마디로 통신규약 약속 (우리 이렇게 하자!)(보통, -able, -ing를 붙여서 많이 표현함.)
protocol Naming {
// 우리는 이런 변수(property)를 가지고 있을겁니다 라고 약속!
var name : String { get set } // get set <- 값을 가져올 수 도 있고 값을 넣을수도 있다.
// 우리는 이런 메소드(method)를 가지고 있을겁니다 라고 약속!
func getName() -> String
}
이것을 사용하려면 아래와 같다. (*Friend 는 Naming Protocol을 준수한다.)
struct Friend : Naming {
var name : String
func getName() -> String {
return "내친구" + self.name
}
}
var myFriend = Freind(name : "정대리")
print(myFreind.getName())
//출력 "내친구 정대리"
Naming이라는 프로토콜과 Aging이라는 프로토콜을 만들어보자. 이는 각각의 프로퍼티와 메서드를 갖는다. 이 두가지 프로토콜을 상속하여 준수하는 UserNotifiable이라는 프로토콜을 만들어보자.
protocol Naming {
var name : String { get set }
func getName() -> String
}
protocol Aging {
var age : Int { get set }
}
protocol UserNotifiable : Naming, Aging {
// 이 프로토콜은 Naming과 Aging protocol 을 준수하고 있다.
}
만약, 여기서 UserNotifiable 을 준수하는 struct 혹은 class를 만들어준다면?
struct MyFriend : UserNotifiable {
var name : String
func getName() -> String {
}
var age : Int
}
위와같이 Naming과 Aging Protocol이 갖고있는 프로퍼티와 메서드를 표기하지 않는다면 struct내 오류가 발생하게된다. 즉, 약속(프로토콜)을 준수하지 않으면 실행이 불가하다.
protocol Naming {
var lastname : String { get set }
var firstname : String { get set }
func getName() -> String
}
extension Naming {
func getFullname() -> String{
return self.lastname + "," + self.firstname
}
}
struct Friend : Naming {
var lastname : String
var firstname : String
func getName() -> String {
return self.lastname
}
}
let myFriend = Friend(lastname : "정", firstname : "대리")
myFriend.getName() // 출력 "정"
myFriend.getFullName // 출력 "정,대리"
즉, 프로토콜만을 사용하여 정의했을 때는 lastname, firstname 프로퍼티 + getName 메서드를 사용할 것이다라고 선언만 할 수 있을 뿐이지 로직을 집어넣거나 할 순 없다.
하지만, extension 으로 프로토콜을 확장 시켜주었을 때에는 로직을 넣을 수 있다.
여기선 getFullname() 메서드를 활용했을 때, lastname + "," + firstname 이 되도록 로직을 만들어 줬다.
Protocol을 활용하여 수업에서 처음보고 화면 간 데이터를 주고받았을 때 굉장히 당황스러웠다.
왜냐하면, 문법에 대한 개념강의를 하고 수업을 한게 아니라 사용부터 했기에..이러한 이유로 인해 SeSac iOS 면접 당시 Protocol에 대해 언급했다가 피를 봤다.. 모르는 개념이였지만, 배우고 있는 것을 말해주다가 면접관님이 내게 'Protocol이 뭔지 설명해주실 수 있을까요?'라는 질문에 나는 무너졌다. 하지만 이젠 설명해줄 수 있을 것 같다.
내가 Protocol에 대해서 완벽하게 개념을 이해한 것은 아닌 것 같다. { get set } 을 프로토콜이 갖고있는 property에 표기해주는 이유?
그리고, 유튭 정대리 문법의 Protocol의 Associated Type에 대해선 내가 개념에 대해서 전혀 모르고있어서 정리를 못했다.
이는 공부하면서 Velog에 더 자세하게 정리해보도록 하자.