Swift에서 XIB 파일로 사용자 정의 UIView 클래스를 초기화 / 인스턴스화하는 방법


139

나는라는 클래스가 MyClass의 서브 클래스입니다 UIView제가으로 초기화 할 것인지, XIB파일. xib 파일을 사용 하여이 클래스를 초기화하는 방법을 잘 모르겠습니다.View.xib

class MyClass: UIView {

    // what should I do here? 
    //init(coder aDecoder: NSCoder) {} ?? 
}

5
iOS9 스위프트에 대한 전체 소스 코드 예제 2.0 참조 github.com/karthikprabhuA/CustomXIBSwift 및 관련 스레드 stackoverflow.com/questions/24857986/...
karthikPrabhu Alagu

답변:


266

이 코드를 테스트했으며 훌륭하게 작동합니다.

class MyClass: UIView {        
    class func instanceFromNib() -> UIView {
        return UINib(nibName: "nib file name", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as UIView
    }    
}

뷰를 초기화하고 아래와 같이 사용하십시오.

var view = MyClass.instanceFromNib()
self.view.addSubview(view)

또는

var view = MyClass.instanceFromNib
self.view.addSubview(view())

스위프트 업데이트> = 3.x 및 스위프트> = 4.x

class func instanceFromNib() -> UIView {
    return UINib(nibName: "nib file name", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
}

1
그것은해야 var view = MyClass.instanceFromNib()& self.view.addSubview(view)반대로 var view = MyClass.instanceFromNib& self.view.addSubview(view()). 답변을 개선하기위한 작은 제안 :)
Logan

1
제 경우에는 나중에 초기화되었습니다! self.view.addSubview (view) 인 경우 view는 var view = MyClass.instanceFromNib ()
Ezimet

2
@Ezimet 해당 뷰 내의 IBAction은 어떻습니까? 처리 할 위치 내보기 (xib)에 버튼이있는 것처럼 해당 버튼의 IBAction 클릭 이벤트를 처리하는 방법은 무엇입니까?
Qadir Hussain

6
UIView 대신 "MyClass"를 반환해야합니까?
Kesong Xie

2
IBoutlets는 ... 내가 갖는이 방법에서 작동하지 않습니다 : "이 클래스는 키에 대한 코딩 호환 키 값이 아닙니다"
라덱 Wilczak

82

Sam의 솔루션은 다른 번들을 고려하지 않았음에도 불구하고 이미 훌륭합니다 (NSBundle : forClass가 구출됩니다). 코드를 입력하는 수동 로딩이 필요합니다.

Xib Outlets, 다른 번들 (프레임 워크에서 사용)을 완벽하게 지원하고 스토리 보드에서 멋진 미리보기를 얻으려면 다음을 시도하십시오.

// NibLoadingView.swift
import UIKit

/* Usage: 
- Subclass your UIView from NibLoadView to automatically load an Xib with the same name as your class
- Set the class name to File's Owner in the Xib file
*/

@IBDesignable
class NibLoadingView: UIView {

    @IBOutlet weak var view: UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        nibSetup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        nibSetup()
    }

    private func nibSetup() {
        backgroundColor = .clearColor()

        view = loadViewFromNib()
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        view.translatesAutoresizingMaskIntoConstraints = true

        addSubview(view)
    }

    private func loadViewFromNib() -> UIView {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
        let nibView = nib.instantiateWithOwner(self, options: nil).first as! UIView

        return nibView
    }

}

평소와 같이 xib를 사용하십시오. 즉, Outlet을 파일 소유자에 연결하고 파일 소유자 클래스를 자신의 클래스로 설정하십시오.

사용법 : NibLoadingView에서 자신의 View 클래스를 서브 클래스 화 하고 Xib 파일에서 클래스 이름을 File 's Owner 로 설정 하십시오.

더 이상 추가 코드가 필요하지 않습니다.

크레디트 기한이있는 크레디트 : GH에서 DenHeadless의 사소한 변경으로이를 포크했습니다. 내 요지 : https://gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8


8
(이것은 하위 뷰와 같은로드 뷰를 추가하기 때문에) 및 호출이 용액 브레이크 연결 배출구 nibSetup로부터하는 init?(coder:)매립 경우 무한 재귀 발생할 NibLoadingViewXIB있다.
redent84

3
@ redent84 의견과 downvote 주셔서 감사합니다. 두 번째 모양이있는 경우 이전 SubView를 대체해야합니다 (새 인스턴스 변수는 제공되지 않음). 무한 재귀에 대해 옳습니다. IB로 어려움을 겪고 있다면 생략해야합니다.
프레데릭 윈 켈스 도프

1
언급했듯이 "아울렛을 파일 소유자에 연결하고 파일 소유자 클래스를 자신의 클래스로 설정하십시오." 콘센트를 파일 소유자에 연결
Yusuf X

1
나는이 방법을 사용하여 xib에서보기를로드하는 것에 대해 항상 불편합니다. 우리는 기본적으로 클래스 A의 서브 클래스 인 뷰를 클래스 A의 서브 클래스 인 뷰에 추가하고 있습니다.이 반복을 막을 방법이 없습니까?
Prajeet Shrestha

1
@PrajeetShrestha .clearColor()스토리 보드에서로드 한 후 배경색을 재정의하는 nibSetup () 때문일 수 있습니다. 그러나 인스턴스화 후 코드로 수행하면 작동합니다. 어쨌든 더 우아한 접근 방식은 프로토콜 기반 접근 방식입니다. github.com/AliSoftware/Reusable 링크가 있습니다 . UITableViewCells와 관련하여 비슷한 접근법을 사용하고 있습니다 (실제로 유용한 프로젝트를 발견하기 전에 구현했습니다). hth!
Frederik Winkelsdorf

77

Swift 2.0부터는 프로토콜 확장을 추가 할 수 있습니다. 내 의견으로는, 반환 유형이 Self아닌 UIView이므로 호출자가 뷰 클래스로 캐스팅 할 필요 가 없기 때문에 더 나은 접근 방식 입니다.

import UIKit

protocol UIViewLoading {}
extension UIView : UIViewLoading {}

extension UIViewLoading where Self : UIView {

  // note that this method returns an instance of type `Self`, rather than UIView
  static func loadFromNib() -> Self {
    let nibName = "\(self)".characters.split{$0 == "."}.map(String.init).last!
    let nib = UINib(nibName: nibName, bundle: nil)
    return nib.instantiateWithOwner(self, options: nil).first as! Self
  }

}

4
이것은 캐스팅 할 필요가 없기 때문에 선택된 답변보다 더 나은 솔루션이며 나중에 생성하는 다른 UIView 하위 클래스에서도 재사용 할 수 있습니다.
user3344977

3
Swift 2.1 및 Xcode 7.2.1로 시도했습니다. 그것은 얼마 동안 작동했으며 뮤텍스 잠금 장치가있는 다른 사람들에게는 예기치 않게 끊어졌습니다. 코드에서 직접 사용 마지막 두 행은 마지막 줄에 모든 시간을 근무하는 것으로 수정var myView = nib.instantiate... as! myViewType
폴 Linsay

@ jr-root-cs 편집 내용에 오타 / 오류가 포함되어 있으므로 롤백해야했습니다. 어쨌든 기존 답변에 코드를 추가하지 마십시오. 대신 의견을 말하십시오. 또는 자신의 답변에 버전 추가하십시오 . 감사.
Eric Aya

Swift 3(XCode 8.0 베타 6)을 사용하여 프로젝트에서 열어 테스트 한 코드를 문제없이 게시했습니다 . 오타가있었습니다 Swift 2. 이 답변이 좋을 때 왜 또 다른 대답이되어야하며 사용자는 XC8을 사용할 때 변경 사항이 무엇인지 검색하는 것이 좋습니다
jr.root.cs

1
@ jr.root.cs 그렇습니다.이 답변은 좋기 때문에 아무도 바꾸지 않아야합니다. 이것이 당신의 대답이 아니라 샘의 대답입니다. 의견을 남기고 싶다면 의견을 남기십시오. 새 / 업데이트 된 버전을 게시하려면 자신의 게시물에서 작성하십시오 . 수정 사항은 다른 사람의 게시물에 버전을 추가하지 않도록 오타 / 들여 쓰기 / 태그를 수정하기위한 것입니다. 감사.
Eric Aya

37

그리고 이것은 스위프트 3.0에 프레드릭의 답변입니다

/*
 Usage:
 - make your CustomeView class and inherit from this one
 - in your Xib file make the file owner is your CustomeView class
 - *Important* the root view in your Xib file must be of type UIView
 - link all outlets to the file owner
 */
@IBDesignable
class NibLoadingView: UIView {

    @IBOutlet weak var view: UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        nibSetup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        nibSetup()
    }

    private func nibSetup() {
        backgroundColor = .clear

        view = loadViewFromNib()
        view.frame = bounds
        view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.translatesAutoresizingMaskIntoConstraints = true

        addSubview(view)
    }

    private func loadViewFromNib() -> UIView {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
        let nibView = nib.instantiate(withOwner: self, options: nil).first as! UIView

        return nibView
    }
}

31

xib에서 뷰를로드하는 보편적 인 방법 :

예:

let myView = Bundle.loadView(fromNib: "MyView", withType: MyView.self)

이행:

extension Bundle {

    static func loadView<T>(fromNib name: String, withType type: T.Type) -> T {
        if let view = Bundle.main.loadNibNamed(name, owner: nil, options: nil)?.first as? T {
            return view
        }

        fatalError("Could not load view with type " + String(describing: type))
    }
}

UIView 서브 클래스의 타입으로서의 출력 뷰로서 가장 좋은 대답
jfgrang

26

스위프트 3 답변 : 필자의 경우 사용자 정의 클래스에 수정할 수있는 콘센트가 필요했습니다.

class MyClassView: UIView {
    @IBOutlet weak var myLabel: UILabel!

    class func createMyClassView() -> MyClass {
        let myClassNib = UINib(nibName: "MyClass", bundle: nil)
        return myClassNib.instantiate(withOwner: nil, options: nil)[0] as! MyClassView
    }
}

.xib에있을 때 사용자 정의 클래스 필드가 MyClassView인지 확인하십시오. 파일 소유자를 귀찮게하지 마십시오.

사용자 정의 클래스가 MyClassView인지 확인하십시오.

또한 MyClassView의 콘센트를 레이블에 연결해야합니다. myLabel을위한 콘센트

그것을 인스턴스화하려면 :

let myClassView = MyClassView.createMyClassView()
myClassView.myLabel.text = "Hello World!"

소유자가 설정되어 있지 않으면 ""MyClas "펜촉을로드했지만 뷰 콘센트가 설정되지 않았습니다."로
돌아옵니다.

22

스위프트 4

내 경우에는 데이터를 해당 사용자 정의보기로 전달해야하므로보기를 인스턴스화하는 정적 함수를 만듭니다.

  1. UIView 확장 만들기

    extension UIView {
        class func initFromNib<T: UIView>() -> T {
            return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as! T
        }
    }
  2. MyCustomView 작성

    class MyCustomView: UIView {
    
        @IBOutlet weak var messageLabel: UILabel!
    
        static func instantiate(message: String) -> MyCustomView {
            let view: MyCustomView = initFromNib()
            view.messageLabel.text = message
            return view
        }
    }
  3. .xib 파일에서 사용자 정의 클래스를 MyCustomView로 설정하십시오. 필요에 따라 콘센트를 연결하십시오. 여기에 이미지 설명을 입력하십시오

  4. 보기 인스턴스화

    let view = MyCustomView.instantiate(message: "Hello World.")

사용자 정의보기에 단추가있는 경우 다른보기 컨트롤러에서 해당 조치를 어떻게 처리 할 수 ​​있습니까?
jayant rawat

프로토콜 위임을 사용할 수 있습니다. 여기를 살펴 stackoverflow.com/questions/29602612/... .
mnemonic23

xib에서 이미지를로드하지 못해 다음 오류가 발생했습니다. 식별자가 "test.Testing"인 번들의 펜촉에서 참조 된 " IBBrokenImage "이미지를 로드 할 수 없음
Ravi Raja Jangid

-4
override func draw(_ rect: CGRect) 
{
    AlertView.layer.cornerRadius = 4
    AlertView.clipsToBounds = true

    btnOk.layer.cornerRadius = 4
    btnOk.clipsToBounds = true   
}

class func instanceFromNib() -> LAAlertView {
    return UINib(nibName: "LAAlertView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! LAAlertView
}

@IBAction func okBtnDidClicked(_ sender: Any) {

    removeAlertViewFromWindow()

    UIView.animate(withDuration: 0.4, delay: 0.0, options: .allowAnimatedContent, animations: {() -> Void in
        self.AlertView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)

    }, completion: {(finished: Bool) -> Void in
        self.AlertView.transform = CGAffineTransform.identity
        self.AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
        self.AlertView.isHidden = true
        self.AlertView.alpha = 0.0

        self.alpha = 0.5
    })
}


func removeAlertViewFromWindow()
{
    for subview  in (appDel.window?.subviews)! {
        if subview.tag == 500500{
            subview.removeFromSuperview()
        }
    }
}


public func openAlertView(title:String , string : String ){

    lblTital.text  = title
    txtView.text  = string

    self.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
    appDel.window!.addSubview(self)


    AlertView.alpha = 1.0
    AlertView.isHidden = false

    UIView.animate(withDuration: 0.2, animations: {() -> Void in
        self.alpha = 1.0
    })
    AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)

    UIView.animate(withDuration: 0.3, delay: 0.2, options: .allowAnimatedContent, animations: {() -> Void in
        self.AlertView.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)

    }, completion: {(finished: Bool) -> Void in
        UIView.animate(withDuration: 0.2, animations: {() -> Void in
            self.AlertView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)

        })
    })


}

형식이 잘못된 코드, 설명이 없으므로 다운 보트.
J. Doe

이 모든 것이 질문과 관련이 있습니까?
Ashley Mills
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.