관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] nib, xib, Placeholders, Files's Owner, First Responder, Responder Chain 개념 본문

iOS 기본 (swift)

[iOS - swift] nib, xib, Placeholders, Files's Owner, First Responder, Responder Chain 개념

jake-kim 2021. 1. 23. 02:44

nib, xib란?

  • nib(NeXT Interface Builder): 뷰의 layout, display등의 요소들을 object graph로 만들어서 직렬화한 파일
  • 인터페이스 빌더에서 구성한 모든 정보는 .xib파일(XML Interface Builder)라는 파일로 저장
  • 프로젝트 컴파일 시 바이너리파일인 .nib파일이 되는 것

* Interface Builder란? 코딩과 상반되는 개념인 그래픽 사용자 인터페이스

nib파일

  • 앱에 nib 파일이 로드되면 Cocoa는 Xcode에서 만든 객체들의 전체 객체 그래프(뷰, 컨트롤, 셀, 메뉴, 객체 모두 포함)를 재생성
  • top-level객체 - 부모 객체를 가지지 않는 것들 (윈도우, 메뉴 바, 커스텀 객체) 단, Placeholder객체와 File's Owner객체는 다른 것을 주의

nib파일의 loading 과정

  • 메모리에 Nib 파일의 컨텐츠와 참조된 모든 리소스 파일(객체 그래프)를 로드: 이미지와 사운드는 Cocoa 캐시에 추가됨
  • nib 객체 그래프 데이터를 Unarchive하고 객체들을 초기화
  • nib 파일의 객체들간에 모든 연결(Action, Oulet, Binding)이 재생성 (File's Owner와 다른 Placeholder객체들간의 연결 포함)
    - Cocoa는 outlet이름과 일치하는 인스턴스 변수를 찾음
    - outlet을 설정하면 등록된 모든 observer에 대한 Key-value observing(KVO) Notification이 생성
    - awakeFromNib 메서드가 불려지기 전에 발생
  • nib 파일에 "Vislable at launch time" 속성을 사용하도록 설정한 모든 윈도우를 표시

nib 로드 방법

* 로드한다는 의미: 코드에서 .xib파일들을 생성하여 참조한다는 의미

 

아래 1번과 2번 중 2번(객체를 사용한 방법) 사용 권장 (2번은 Unarchive -- 캐시되고 있는 형태)

 

1) Bundle 클래스의 loadNibNamed(_:owner:options:)로 로드방법

- nib 파일을 이름으로 찾아서 메모리에 로드하여, nib파일 내의 top-level 객체들을 [Any]? 타입으로 반환

- loadNib에서 first로 호출 하는이유는, placholder에서 여러개의 view를 가질 수 있기 때문

// 편리하게 UIView를 extension하여 사용

extension UIView {

    // nib 로부터 view 객체 생성하는 함수  
    private func loadView(nibName: String) -> UIView? {
        let loadNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)
        guard let view = loadNib?.first as? UIView else { return nil }
        return view
    }
    
    // nib파일을 얻은 view를 세팅하는 함수
    public func xibSetup(nibName: String) {
        guard let view = loadView(nibName: nibName) else {
            return
        }
        view.translatesAutoresizingMaskIntoConstraints = false
        view.frame = bounds
        addSubview(view)
        view.fillToSuperview() // anchor로 layout지정하는 custom 함수
    }
}

2) UINib 클래스 사용

extension: UIView {
    func loadViewFromNib(nib: String) -> UIView? {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: nib, bundle: bundle)
        return nib.instantiate(withOwner: self, options: nil).first as? UIView
    }
    
    func xibSetup() {
        guard let view = loadViewFromNib(nib: type(of: self).className) else {
            return
        }
        view.translatesAutoresizingMaskIntoConstraints = false
        view.frame = bounds
        addSubview(view)
        view.fillToSuperview()
    }
}

// className으로 이름 얻는 extension

public extension NSObject {
    var className: String {
        return String(describing: type(of: self))
    }
    
    class var className: String {
        return String(describing: self)
    }
}

nib 초기화 관련 메소드

  • awakeFromNib(): nib파일의 내용을 읽어서 object graph를 복원할 때 모든 복원이 완료된 후 nib 파일 내의 모든 객체가 받게되는 메세지 (IBOutlet등의 참조가 완료되었다는 의미)
  • init(nibName:bundle:) - nib 파일로부터 뷰 컨트롤러를 생성할 때 사용, nib파일은 아직 로딩 x
  • init(coder:) - nib 파일을 읽어들여서 그 데이터로부터 뷰 컨트롤러, 내부 뷰 들을 생성할 때 사용
  • prepareForInterfaceBuilder() - IB에서 속성을 변경할 때 해당 코드가 내부적으로 호출되며, 실시간으로 변경된 값을 확인할 수 있는 요소
  • cf) init(frame:) - nib가 아닌 코드를 통해 객체를 초기화할 때 layout과 함께 초기화 해주는 코드

Placeholders, File's Owner 객체, First Responder 객체

  • Placeholders: 의미 그대로 UIView처럼 보여지는 주요 요소가 아닌, 빠져있는 것을 대신하여 연결해주는 역할을 의미

  • File's Owner 객체: nib 파일을 앱코드와 연결시켜주는 객체 (nib파일의 내용을 책임지는 컨트롤러)
    - nib파일(customView생성 시 .xib파일)을 로드할 때 File's Owner객체를 보고 지정한 대체 객체를 생성하여 nib파일에서 해당 객체들을 참조 할 수 있도록 하는 개념

    - File's Owner객체에서 nib파일에 저장된 top-level 객체의 참조를 유지하기 위해서 사용하는 것이 @IBOutlet

  • First Responder 객체: Responder 객체가 이벤트를 받으면 이를 처리하거나 다른 Responder객체에게 처리할 수 있도록 넘겨야할 의무가 존재, UIKit은 적절한 Responder를 지정해서 이벤트를 넘겨서 처리하는데, 처음으로 이벤트를 받는 Responder를 First Responder로 지칭
    - first responder 연관된 Hitest개념: 참고
  • Responder Chain란:
    - 앱은 reponder객체를 사용하여 이벤트를 받고 그 이벤트를 다룸

    - responder 객체는 UIResponder 클래스의 인스턴스이며 UIView, UIViewController를 포함한 서브 클래스

    - 앱이 이벤트를 받을 경우 UIKit은 first responder라고 불리는 적절한 응답자 객체에게 해당 이벤트를 전송

    - UIKit은 정의된 규칙에 의해 다뤄지지 않은 이벤트들을 앱의 리스폰더 객체인 Responder Chain에 의하여, 이벤트를 처리할 수 있는 Responder가 나올때까지 responder끼리 전달

    - Responder Chain을 다루는 예시) dataTextField.becomeFirstResponder()

 

  • Responder Chain의 전환
    - UIView: view가 View Controller의 rootView인 경우, 다음 reponder는 ViewController
    - UIViewController: ViewController가 윈도우의 root view라면 다음 responder는 window 객체
    - UIWindow: UIWindow의 다음 responder는 UIApplication 객체
    - UIApplication: 다음 responder는 AppDelegate

* 참고문서:

developer.apple.com/documentation/uikit/uinib

developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW8

Comments