🦜 Swift

[TIL] UIView, UIViewController, Delegate

ji-hyun 2023. 5. 5. 00:03

본 블로그 글은 chatGPT 가 준 코드와 iOS 개발 기초 개념들을 chatGPT 를 통해 공부한 글입니다.

ChatGPT 90%, 기타 블로그 10% 참고하여 틀린 내용이 있을 수도 있습니다.

틀린 내용 있을 시, 지적해주시면 감사하겠습니다.

 

 

iOS 니 넘 공부해보자..!

 

 


UIView 와 UIViewController 에 대한 정의를 살펴봅시다.

 

UIView 와 UIViewController 

UIView

이는 화면을 구성하는 요소들의 기본 클래스이며, 위치와 크기를 갖는 사각형으로, 배경색을 가지고 있고 문자나 이미지 등의 컨텐츠를 가지는 것이 가능합니다.

 

UIView 와 하위 클래스 관계도

 

• Label, Image, Button, 기타 인터페이스 요소를 표시하려면, UIKit Framework가 제공하는 뷰 하위클래스를 사용하면 됩니다.

 

 

 

 

 

 

UIViewController

이는 앱의 근간을 이루는 객체로 모든 앱은 최소한 하나 이상의 뷰 컨트롤러를 가지고 있는데, 한 마디로 사용자가 화면을 보는 것에 대한 관리기능을 제공하는 객체입니다. (xcode에서 프로젝트를 생성하면 기본적으로 ViewController 파일 하나는 이미 존재함)

 

 

UIViewController와 UIView는 일대다(one-to-many) 관계입니다. UIViewController는 일반적으로 여러 개의 UIView 객체를 관리합니다.

 

 

 

 

 

 

 

예시 코드

다은은 UIViewController 를 생성하여 UIView 를 참조시키는 방법입니다.

 

class MyViewController: UIViewController {
    let myView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.addSubview(myView)
        // ...
    }
    // ...
}

 

UIViewController에서 UIView 를 참조하는 경우에는 보통 UIViewController 클래스의 인스턴스 변수로 UIView 를 생성하고, 이를 UIViewController의 view 계층 구조에 추가하여 화면을 구성합니다.

 

예를 들어, 위와 같이 UIViewController 클래스의 인스턴스 변수로 UIView 를 생성하고, 이를 view 계층 구조에 추가하여 화면을 구성할 수 있습니다.

 

 

<코드 해석을 덧붙이자면..>

위 코드에서 MyViewController 클래스는 myView 라는 인스턴스 변수로 UIView를 생성하고, viewDidLoad 메서드에서 myView 를 UIViewController 의 view 계층 구조에 추가합니다.

이렇게 함으로써 UIViewController는 myView 를 참조하며, myView는 UIViewController에 의해 소유됩니다. 이때 myView 변수는 강한 참조(strong var)입니다.

 

 

 

이렇게 UIViewController의 view 위에서 UIView, UIButton, UITextField 등을 붙입니다.

 

 


Delegate 패턴.... Protocol 을 먼저 알아보자.

프로토콜을 먼저 알아야 한다.

 

프로토콜이란?
작업의 설계도와 같습니다
프로토콜에서는 직접 구현을 하지 않습니다. delegate는 프로토콜로 구현되어 있습니다.

 

 

 

 

 

Delegate 패턴

delegate : 위임

내가 해야할 일을 다른 누군가에게 시킨다.

 

객체지향 프로그래밍(OOP)에서 하나의 객체가 모든 일을 처리하는 것이 아니라 일들 중 일부를 다른 객체에 넘기는 것
유명한 싱글턴, 팩토리, 어댑터, 옵저버 등의 디자인 패턴도 이러한 '패턴' 인 것이다.

 

 

 

이론보다 코드를 통해 좀 더 쉽게 이해할 수 있을 것 같습니다.

 

 

import UIKit

protocol MyButtonDelegate: AnyObject {
    func buttonTapped(_ button: MyButton)
}

class MyButton: UIButton {
    weak var delegate: MyButtonDelegate?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
      //  Interface Builder에서 쓰이지 않고, 코딩(Programmatically)으로 UIView를 상속받은 클래스를 만들 때 사용
      //  ex) let sampleButton = UIButton(frame: CGRect(x: 3, y: 0, width: 100, height: 200))
        
        self.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
    }
    
    required init?(coder: NSCoder) {
	// Interface Builder에서 생성되는 초기화 구문
    // Interface Builder란 간단하게 말해서는 xib, nib, storyboard를 사용하여 VIew를 작업하는 것!
        fatalError("init(coder:) has not been implemented")
    }
    
    @objc private func buttonTapped(_ sender: UIButton) {
        guard let delegate = delegate else { return }
        guard let myButton = sender as? MyButton else { return }
        delegate.buttonTapped(myButton)
    }
}

class MyViewController: UIViewController, MyButtonDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myButton = MyButton(type: .system)
        myButton.frame = CGRect(x: 100, y: 100, width: 100, height: 50)
        myButton.setTitle("Tap me", for: .normal)
        myButton.delegate = self // delegate 프로퍼티에 self 할당
        
        self.view.addSubview(myButton)
    }
    
    func buttonTapped(_ button: MyButton) {
	    print("Button tapped!")
	}
}

 

위의 코드를 해석하면 MyViewController 에서
MyButton 을 강한 참조 (let myButton = MyButton(type: .system)) 를 하였고

self.view.addSubview 를 통해 MyViewController 의 view 에 추가하고 있습니다  

 

 

위의 코드는 전체 코드이지만 이 정도만 분석하고 밑에서 자세히 코드를 분할하여 알아보겠습니다.  

 

 

 

 

여기서 delegate 를 살펴보겠습니다

 

protocol MyButtonDelegate: AnyObject {
    func buttonTapped(_ button: MyButton)
}

 

delegate 는 protocol 로 구현되어 있습니다.

그래서 구현 내용은 없고 선언만 되어 있는 것을 확인할 수 있습니다.

아까 얘기한 protocol 정의를 다시 리마인드하면 "작업의 설계도"와 같습니다.

 

 

아무튼 위 delegate 는 메세지를 전달하기 위해 사용되는 프로토콜입니다.

 

 

 

 

 

대리자 -> 프로토콜 채택

 

class MyViewController: UIViewController, MyButtonDelegate { 
// Delegate 채택함!!!

        let myButton = MyButton(type: .system)
        
        ....

        myButton.delegate = self // delegate 는 나임!!!


	//채택한 프로토콜 준수하기 위한 메소드 구현!!!
    func buttonTapped(_ button: MyButton) {
	    print("Button tapped!")
	}
 }

 

여기서 아까 정의한 MyButtonDelegate 라는 프로토콜을 채택한 것을 볼 수 있습니다.

아까 MyButtonDelegate 프로토콜에는 buttonTapped 라는 메서드가 선언되어 있었는데 MyViewController 대리자는 이 메서드를 구현해야 합니다.

 

 

 

 

뷰 -> MyButton

 

import UIKit

protocol MyButtonDelegate: AnyObject {
    func buttonTapped(_ button: MyButton)
}

class MyButton: UIButton {
    weak var delegate: MyButtonDelegate?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
      //  Interface Builder에서 쓰이지 않고, 코딩(Programmatically)으로 UIView를 상속받은 클래스를 만들 때 사용
      //  ex) let sampleButton = UIButton(frame: CGRect(x: 3, y: 0, width: 100, height: 200))
        
        self.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
    }
    
    required init?(coder: NSCoder) {
	// Interface Builder에서 생성되는 초기화 구문
    // Interface Builder란 간단하게 말해서는 xib, nib, storyboard를 사용하여 VIew를 작업하는 것!
        fatalError("init(coder:) has not been implemented")
    }
    
    @objc private func buttonTapped(_ sender: UIButton) {
        guard let delegate = delegate else { return }
        guard let myButton = sender as? MyButton else { return }
        delegate.buttonTapped(myButton)
    }
}

class MyViewController: UIViewController, MyButtonDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myButton = MyButton(type: .system)
        myButton.frame = CGRect(x: 100, y: 100, width: 100, height: 50)
        myButton.setTitle("Tap me", for: .normal)
        myButton.delegate = self // delegate 프로퍼티에 self 할당
        
        self.view.addSubview(myButton)
    }
    
    func buttonTapped(_ button: MyButton) {
	    print("Button tapped!")
	}
}

 

전체 코드를 다시 가져왔습니다.

 

MyButton 클래스에서 buttonTapped 함수를 구현한 이유는 해당 버튼이 탭되었을 때 실행되어야 하는 로직을 처리하기 위해서입니다.

 

그리고 MyButtonDelegate 프로토콜을 선언하고, weak var delegate: MyButtonDelegate? 라는 delegate 프로퍼티를 선언한 것은 MyButton 클래스 내에서 해당 버튼이 탭되었을 때 실행되어야 하는 로직을 MyViewController 클래스 내에서 구현하도록 위임하기 위해서입니다.

 

즉, MyButton 클래스는 버튼이 탭되었을 때 실행되어야 하는 로직을 구현하고, MyViewController 클래스는 이를 구현하기 위해 MyButtonDelegate 프로토콜을 준수하고, MyButton 클래스의 인스턴스의 delegate 프로퍼티에 자기 자신(self)을 할당하여 버튼 탭 이벤트가 발생하면 MyViewController 클래스 내의 buttonTapped 함수가 실행되도록 위임하고 있습니다.

 

 

 

 

 

 

이렇게 간단히 개념을 한 번 훑어보았습니다.

아무것도 모르는 상태라 쉽지 않았지만 이렇게 전체적으로 크게 훑어본 후에 세부 개념들을 자세히 훑어보면 더 잘 기억되지 않을까 싶습니다.

다음에는 protocol, 옵셔널 바인딩.. 세부 개념을 파헤쳐 보겠습니다.

 

 

 

'🦜 Swift' 카테고리의 다른 글

[iOS] 뷰 컨트롤러의 수명 주기, 팝업창 띄우기 - 2  (0) 2023.05.13
[iOS] Navigation Controller 공부 기록 - 1  (0) 2023.05.13
[iOS] weak var  (0) 2023.05.04
[Swift] 인스턴스의 생성과 소멸  (0) 2022.11.20
[Swift] 상속  (0) 2022.11.20