iOS) 커스텀 뷰 Xib 연결하기
두가지 방법을 통해서 커스텀 뷰 클래스를 Xib 와 연결해서 만들어보자!
그리고 좀 더 고민해볼 점까지 같이 알아봐요!
🧙♀️ 방법1 : File’s Owner 에서 Custom Class 설정
File's Owner
: File’s Owner 객체를 사용해서 nib 파일의 top-level 객체들을 참조할 수 있도록 한다. 우리가 IBAction, IBOutlet 에 해당하는 것을 연결할 수 있게 해주는 것은 이 참조가 가능하기 때문이다.
해당 뷰를 가져오기 위해서 UIView 클래스에서 nib 형태로 가져와야 합니다.
이때! 두가지 방법으로 가져올 수 있어요!
// CustomNavigationBar.siwft
// 역할 : 커스텀 네비게이션 바
class CustomNavigationBar: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
customInit()
//alternativeCustomInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
customInit()
//alternativeCustomInit()
}
//방법 1: loadNibNamed(_:owner:options:) 사용
func customInit() {
if let view = Bundle.main.loadNibNamed("CustomNavigationBar", owner: self, options: nil)?.first as? UIView {
view.frame = self.bounds
addSubview(view)
}
}
//방법 2: UINib 생성 후 instantiate
func alternativeCustomInit() {
if let view = UINib(nibName: "CustomNavigationBar", bundle: nil).instantiate(withOwner: self, options: nil).first as? UIView {
view.frame = self.bounds
addSubview(view)
}
}
}
📌 loadNibNamed(_:owner:options:)
receiver’s bundle 에 위치하는 nib 파일의 컨텐츠를 언아카이브 한다.
(Xib 파일이 저장되면 Xib 파일을 아카이브하는데 아카이브한 데이터로부터 객체를 다시 만드는 것이 언아카이브이다.)
- name : nib 파일의 이름
- owner : nib 의 Files’s Owner 객체로 할당할 객체
- options : nib 파일을 열 때 사용할 옵션이 포함된 dictionary.
📌 init(nibName:bundle:)
특정 번들의 nib 파일로부터 nib 객체를 반환한다.
- nibName : nib 파일의 이름.
- bundle : nil 설정시 main bundle 에서 nib 파일을 찾는다.
📌 instantiate(withOwner:options:)
nib 객체의 nib 파일에 있는 메모리내 컨텐츠를 언아카이브하고 고유한 객체 트리와 top-level 객체 집합을 만든다. (부모 객체를 가지지 않는 것들. 윈도우, 메뉴 바, 커스텀 컨트롤러 객체)
- withOwner : nib 파일의 owner 로 사용할 객체.
- options : nib 파일을 열 때 사용할 옵션이 포함된 dictionary.
❗️ Xib 에서 설정한 Custom Class 는 소유할 클래스의 타입을 설정한 것이고(이것으로 IBAction, IBOutlet 등 연결가능.) 위의 코드로 owner 의 instance 를 설정한다.
📌 .first
위의 메서드들은 반환값이 [Any]
즉, 배열입니다! Xib 가 위에처럼 여러개의 View 를 가질 수 있기 때문에 배열로 리턴을 해주죠! 그래서 우린 첫번째 View 를 가져올 것이기 때문에 .first
를 사용합니다.
(✨ 첫번째가 아닌 원하는 타입의 객체를 가져오기 위해서 is 연산자로 찾을 수 있다.)
스토리보드에서 커스텀뷰를 설정해주면 끝!
⁉️ 초기화 함수 init
를 호출하지 않았는데 어떻게 load 된 것일까요?
스토리보드에서 커스텀 클래스를 설정했기 때문에 nib 파일을 unarchive 하고 커스텀 뷰의 init(coder: NSCoder)
를 호출한다고 해요! 그러면 customInit()
메서드가 실행되겠죠?
🧙♀️ 방법2 : View 에서 Custom Class 설정
View 의 Custom Class 로만 변경하고 실행했을 때는 앱이 갑작스럽게 죽게 됩니다..!
customInit()
과 init(coder:)
의 반복으로 앱이 죽게되었는데요. 과정을 살펴봅시다!
1.뷰컨트롤러가 로딩된 후 커스텀 뷰의 init(coder: NSCoder)
호출
2.커스텀뷰의 customInit()
호출
loadNibName
으로 Xib 로드
4.Xib 의 first View 의 Custom Class 는 커스텀뷰로 설정된 상태. 커스텀뷰의 init(coder: NSCoder
재호출.
5.2번으로 간다.
이렇게 무한루프를 돌면서 앱이 죽게됩니다! 🥲
해결
init 메서드에서 같은 클래스의 Xib 를 로드하지 마세요!
그렇다면 어디서 하면 될까요? 부모클래스나 뷰컨트롤러에서 Xib 를 로드하면 됩니다.
// ViewController.swift
@IBOutlet weak var customNavigationBar: UIView!
// ...
override func viewDidLoad() {
super.viewDidLoad()
// ...
// ✅ 이번에는 nib 파일 이름을 String(describing:) 으로 가져와봤어요!
guard let loadedNib = Bundle.main.loadNibNamed(String(describing: CustomNavigationBar.self), owner: self, options: nil) else { return }
guard let navigationBar = loadedNib.first as? CustomNavigationBar else { return }
navigationBar.frame = CGRect(x: 0, y: 0, width: customNavigationBar.frame.width, height: customNavigationBar.frame.height)
customNavigationBar.addSubview(navigationBar)
}
이렇게 되면 스토리보드에서 커스텀뷰를 설정해주는 단계는 건너뛰어도 되겠죠?
출처:
[iOS - swift] nib, xib, Placeholders, Files’s Owner, First Responder, Responder Chain 개념
[Swift] 커스텀 뷰 xib 연결하기 : File’s Owner vs Custom Class
고민해볼 점이 있어요! 바로..!
UIView 는 File’s Owner 가 될 수 없다…?
커스텀뷰를 Xib 로 만드는 방법을 찾아보다가 아래 출처의 글을 읽어보게 되었는데요!
요약해보자면
- 애플개발자 문서에서는 File’s Owner 에 대한 설명으로 nib 파일의 top-level 객체를 참조한다고 하는데! UIView 는 top-levle 의 조건을 만족하지 못한다!
- Xib 는 다양한 UIView 객체를 가질 수 있게 만들어졌다. 근데 File’s Owner 는 하나만 을 가질 수 있다. 그러면 다양한 UIView 객체를 Xib 가 가질 때 File’s Owner 는 한개의 UIView 클래스이면서 다수의 UIView 클래스 타입이라는 말인가? 아이디어가 모순적이다!
라는 내용을 가지고 있는데요! 충분히 저희가 생각해볼만 한 것 같아요! 더 자세한 내용은 출처를 참고해주세요!
출처 :