programing

Swift의 닙에서 UIV뷰 로드

yellowcard 2023. 5. 25. 21:41
반응형

Swift의 닙에서 UIV뷰 로드

다음은 맞춤형으로 사용할 펜촉을 로드하는 데 사용하는 목표-C 코드입니다.UIView:

-(id)init{

    NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"myXib" owner:self options:nil];
    return [subviewArray objectAtIndex:0];

}

스위프트에서 해당 코드는 무엇입니까?

나의 기여:

extension UIView {
    class func fromNib<T: UIView>() -> T {
        return Bundle(for: T.self).loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
    }
}

그런 다음 이렇게 부릅니다.

let myCustomView: CustomView = UIView.fromNib()

..또는 심지어:

let myCustomView: CustomView = .fromNib()

원본 솔루션

  1. XIB와 SomeView라는 클래스를 만들었습니다(편리성과 가독성을 위해 같은 이름을 사용했습니다).저는 둘 다 UI 뷰를 기반으로 했습니다.
  2. XIB에서 "파일 소유자" 클래스를 (ID 검사기에서) SomeView로 변경했습니다.
  3. 저는 SomeView.swift에 UIView 아울렛을 만들어 XIB 파일의 최상위 뷰에 연결했습니다(편리하게 "뷰"라고 명명됨).그런 다음 필요에 따라 XIB 파일의 다른 컨트롤에 다른 콘센트를 추가했습니다.
  4. SomeView.swift에서 "init with code" 이니셜라이저 안에 XIB를 로드했습니다."자신"에게 어떤 것도 할당할 필요가 없습니다.XIB가 로드되는 즉시 최상위 보기를 포함한 모든 출구가 연결됩니다.유일하게 누락된 것은 보기 계층 구조에 최상위 보기를 추가하는 것입니다.

.

class SomeView: UIView {
   required init(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      NSBundle.mainBundle().loadNibNamed("SomeView", owner: self, options: nil)
      self.addSubview(self.view);    // adding the top level view to the view hierarchy
   }
   ...
}

이렇게 하면 닙에서 자체 로드되는 클래스를 얻을 수 있습니다.그러면 프로젝트에서 (인터페이스 빌더 또는 프로그래밍 방식으로) UIView를 사용할 수 있을 때마다 SomeView를 클래스로 사용할 수 있습니다.

업데이트 - Swift 3 구문 사용

다음 확장에서 xib를 로드하는 것은 인스턴스 메소드로 작성되며, 위와 같은 이니셜라이저에서 이 메소드를 사용할 수 있습니다.

extension UIView {

    @discardableResult   // 1
    func fromNib<T : UIView>() -> T? {   // 2
        guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else {    // 3
            // xib not loaded, or its top view is of the wrong type
            return nil
        }
        self.addSubview(contentView)     // 4
        contentView.translatesAutoresizingMaskIntoConstraints = false   // 5 
        contentView.layoutAttachAll(to: self)   // 6 
        return contentView   // 7
    }
}
  1. 모든 출구가 이미 연결되어 있는 경우에는 반환된 보기가 대부분 호출자에게 관심이 없기 때문에 폐기 가능한 반환 값을 사용합니다.
  2. UIView 유형의 선택적 개체를 반환하는 일반 메서드입니다.보기를 로드하지 못하면 0을 반환합니다.
  3. 현재 클래스 인스턴스와 이름이 같은 XIB 파일을 로드하려고 합니다.실패하면 0이 반환됩니다.
  4. 보기 계층에 최상위 수준 보기를 추가합니다.
  5. 이 선은 제약 조건을 사용하여 뷰를 레이아웃하는 것으로 가정합니다.
  6. 이 방법은 상단, 하단, 선행 및 후행 제약 조건을 추가합니다. - 모든 측면의 "자체"에 뷰를 부착합니다(자세한 내용은 https://stackoverflow.com/a/46279424/2274829 참조).
  7. 최상위 보기 반환

호출자 방법은 다음과 같습니다.

final class SomeView: UIView {   // 1.
   required init?(coder aDecoder: NSCoder) {   // 2 - storyboard initializer
      super.init(coder: aDecoder)
      fromNib()   // 5.
   }
   init() {   // 3 - programmatic initializer
      super.init(frame: CGRect.zero)  // 4.
      fromNib()  // 6.
   }
   // other methods ...
}
  1. SomeClass는 SomeClass.xib 파일에서 내용을 로드하는 UIView 하위 클래스입니다."최종" 키워드는 선택 사항입니다.
  2. 보기가 스토리보드에서 사용되는 경우의 이니셜라이저입니다(일부 클래스를 스토리보드 보기의 사용자 정의 클래스로 사용).
  3. 보기가 프로그래밍 방식으로 생성될 때 사용하는 이니셜라이저(예:"let myView = SomeView()".
  4. 이 보기는 자동 레이아웃을 사용하여 레이아웃되므로 모두 0 프레임을 사용합니다.init(프레임: CGRect) {..자동 검색은 프로젝트에서 독점적으로 사용되기 때문에 메소드는 독립적으로 생성되지 않습니다.
  5. 6. 확장자를 사용하여 xib 파일 로드.

크레딧: 이 솔루션에서 일반적인 확장 기능을 사용한 것은 아래의 Robert의 답변에서 영감을 얻었습니다.

"보기"를 "내용 보기"로 변경하여 혼동을 방지합니다.또한 배열 첨자를 ".first"로 변경했습니다.

이제 돌아올 수 있습니다.-> Selfinswift는 이것을 조금 단순화하는 데 도움이 됩니다.스위프트 5에서 마지막으로 확인되었습니다.

extension UIView {
    class func fromNib(named: String? = nil) -> Self {
        let name = named ?? "\(Self.self)"
        guard
            let nib = Bundle.main.loadNibNamed(name, owner: nil, options: nil)
            else { fatalError("missing expected nib named: \(name)") }
        guard
            /// we're using `first` here because compact map chokes compiler on
            /// optimized release, so you can't use two views in one nib if you wanted to
            /// and are now looking at this
            let view = nib.first as? Self
            else { fatalError("view of type \(Self.self) not found in \(nib)") }
        return view
    }
}

만약 당신이.xib파일 및 하위 클래스는 동일한 이름을 공유하며 다음을 사용할 수 있습니다.

let view = CustomView.fromNib()

사용자 지정 이름이 있는 경우 다음을 사용합니다.

let view = CustomView.fromNib(named: "special-case")

참고:

"유형 보기를 찾을 수 없습니다." 오류가 발생하는 경우...그러면 보기의 클래스를 설정하지 않았습니다..xib

에서 합니다..xib하고 를 누릅니다.cmd + opt + 4그리고 그 안에class를 합니다.

Swift 4 - 5.1 프로토콜 확장

public protocol NibInstantiatable {
    
    static func nibName() -> String
    
}

extension NibInstantiatable {
    
    static func nibName() -> String {
        return String(describing: self)
    }
    
}

extension NibInstantiatable where Self: UIView {
    
    static func fromNib() -> Self {
        
        let bundle = Bundle(for: self)
        let nib = bundle.loadNibNamed(nibName(), owner: self, options: nil)
        
        return nib!.first as! Self
        
    }
    
}

입양

class MyView: UIView, NibInstantiatable {

}

이 구현에서는 Nib의 이름이 UIView 클래스와 동일하다고 가정합니다.예. MyView.xib.MyView에서 nibName()을 구현하여 이 동작을 수정하여 기본 프로토콜 확장 구현과 다른 이름을 반환할 수 있습니다.

xib에서 파일 소유자는 MyView이고 루트 보기 클래스는 MyView입니다.

사용.

let view = MyView.fromNib()

코드를 따르도록 해보세요.

var uiview :UIView?

self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView

편집:

import UIKit

class TestObject: NSObject {

     var uiview:UIView?

    init()  {
        super.init()
       self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
    }


}

프로젝트에 많은 사용자 정의 뷰가 있는 경우 다음과 같은 클래스를 만들 수 있습니다.UIViewFromNib

스위프트 2.3

class UIViewFromNib: UIView {
    
    var contentView: UIView!
    
    var nibName: String {
        return String(self.dynamicType)
    }
    
    //MARK:
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        loadViewFromNib()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        loadViewFromNib()
    }
    
    //MARK:
    private func loadViewFromNib() {
        contentView = NSBundle.mainBundle().loadNibNamed(nibName, owner: self, options: nil)[0] as! UIView
        contentView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

스위프트 5

class UIViewFromNib: UIView {
    
    var contentView: UIView!
    
    var nibName: String {
        return String(describing: type(of: self))
    }
    
    //MARK:
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        loadViewFromNib()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        loadViewFromNib()
    }
    
    //MARK:
    func loadViewFromNib() {
        let bundle = Bundle(for: UIViewFromNib.self)
        contentView = UINib(nibName: nibName, bundle: bundle).instantiate(withOwner: self).first as? UIView
        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

그리고 모든 수업에서 그냥 상속받습니다.UIViewFromNib또한 당신은 무시할 수 있습니다.nibName if 산의경 우재▁if경..xib파일 이름이 다릅니다.

class MyCustomClass: UIViewFromNib {
    
}

Swift를 사용하여 다음 코드로 이를 달성했습니다.

class Dialog: UIView {
    @IBOutlet var view:UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.frame = UIScreen.mainScreen().bounds
        NSBundle.mainBundle().loadNibNamed("Dialog", owner: self, options: nil)
        self.view.frame = UIScreen.mainScreen().bounds
        self.addSubview(self.view)
    }

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

XIB 뷰 콘센트를 연결하여 신속하게 정의된 콘센트를 볼 수 있도록 하십시오.또한 First Responder를 사용자 정의 클래스 이름으로 설정하여 추가 콘센트 연결을 시작할 수 있습니다.

이것이 도움이 되길 바랍니다!

Xcode 7 베타 4, Swift 2.0 및 iOS9 SDK에서 테스트되었습니다.다음 코드는 xib를 uview에 할당합니다.스토리보드에서 이 사용자 지정 xib 보기를 사용할 수 있으며 IOutlet 개체에도 액세스할 수 있습니다.

import UIKit

@IBDesignable class SimpleCustomView:UIView
{
    var view:UIView!;

    @IBOutlet weak var lblTitle: UILabel!

   @IBInspectable var lblTitleText : String?
        {
        get{
            return lblTitle.text;
        }
        set(lblTitleText)
        {
            lblTitle.text = lblTitleText!;
        }
    }

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadViewFromNib ()
    }
    func loadViewFromNib() {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: "SimpleCustomView", bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        self.addSubview(view);



    }


}

프로그래밍 방식으로 사용자 정의 보기 액세스

self.customView =  SimpleCustomView(frame: CGRectMake(100, 100, 200, 200))
        self.view.addSubview(self.customView!);

소스 코드 - https://github.com/karthikprabhuA/CustomXIBSwift

위의 솔루션을 기반으로 합니다.

이것은 모든 프로젝트 번들에서 작동하며 Nib()에서 전화를 걸 때 제네릭이 필요하지 않습니다.

스위프트 2

extension UIView {

    public class func fromNib() -> Self {
        return fromNib(nil)
    }

    public class func fromNib(nibName: String?) -> Self {

        func fromNibHelper<T where T : UIView>(nibName: String?) -> T {
            let bundle = NSBundle(forClass: T.self)
            let name = nibName ?? String(T.self)
            return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
        }
        return fromNibHelper(nibName)
    }
}

스위프트 3

extension UIView {

    public class func fromNib() -> Self {
        return fromNib(nibName: nil)
    }

    public class func fromNib(nibName: String?) -> Self {
        func fromNibHelper<T>(nibName: String?) -> T where T : UIView {
            let bundle = Bundle(for: T.self)
            let name = nibName ?? String(describing: T.self)
            return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
        }
        return fromNibHelper(nibName: nibName)
    }
}

다음과 같이 사용할 수 있습니다.

let someView = SomeView.fromNib()

이런 식으로.

let someView = SomeView.fromNib("SomeOtherNibFileName")

스위프트 4

".first as?"라고 쓰는 것을 잊지 마세요.사용자 정의 보기".

if let customView = Bundle.main.loadNibNamed("myXib", owner: self, options: nil)?.first as? CustomView {    
    self.view.addSubview(customView)
    }

어디서나 사용하고 싶은 경우

최고의 해결책은 로버트 검메슨의 대답입니다.

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

그런 다음 이렇게 부릅니다.

let myCustomView: CustomView = UIView.fromNib()

저는 이 솔루션을 선호합니다(@GK100일 경우 답변 기준).

  1. XIB와 SomeView라는 클래스를 만들었습니다(편리성과 가독성을 위해 같은 이름을 사용했습니다).저는 둘 다 UI 뷰를 기반으로 했습니다.
  2. XIB에서 "파일 소유자" 클래스를 (ID 검사기에서) SomeView로 변경했습니다.
  3. 저는 SomeView.swift에 UIView 아울렛을 만들어 XIB 파일의 최상위 뷰에 연결했습니다(편리하게 "뷰"라고 명명됨).그런 다음 필요에 따라 XIB 파일의 다른 컨트롤에 다른 콘센트를 추가했습니다.
  4. 에서 SomeView.swift의 XIB 했습니다.init또는init:frame: CGRect이니셜라이저"자신"에게 어떤 것도 할당할 필요가 없습니다.XIB가 로드되는 즉시 최상위 보기를 포함한 모든 출구가 연결됩니다.유일하게 누락된 것은 보기 계층 구조에 최상위 보기를 추가하는 것입니다.

    class SomeView: UIView {
      override init(frame: CGRect) {
        super.init(frame: frame)
        NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil)
        self.addSubview(self.view);    // adding the top level view to the view hierarchy
      }
    
      required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil)
        self.addSubview(self.view);    // adding the top level view to the view hierarchy
      }
    
    
      ...
    }
    

Swift를 사용하는 좋은 방법은 열거형을 사용하는 것입니다.

enum Views: String {
    case view1 = "View1" // Change View1 to be the name of your nib
    case view2 = "View2" // Change View2 to be the name of another nib

    func getView() -> UIView? {
        return NSBundle.mainBundle().loadNibNamed(self.rawValue, owner: nil, options: nil).first as? UIView
    }
}

그런 다음 코드에서 다음을 간단히 사용할 수 있습니다.

let view = Views.view1.getView()

에 대해 됨:Swift 5

다음과 같이 정의합니다.

extension UIView {
    public class func fromNib<T: UIView>() -> T {
        let name = String(describing: Self.self);
        guard let nib = Bundle(for: Self.self).loadNibNamed(
                name, owner: nil, options: nil)
        else {
            fatalError("Missing nib-file named: \(name)")
        }
        return nib.first as! T
    }
}

위의 내용은 다음과 같습니다.

let view: MyCustomView = .fromNib();

은 다과같번검다니색과 합니다.MyCustomView 로드 그음에다.MyCustomView.nib파일(파일이 존재하고 프로젝트에 추가된 경우).

let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
return subviewArray[0]

Swift 5 - 깔끔하고 사용하기 쉬운 익스텐션

[제작 프로젝트에서 붙여넣기 복사]

//
//  Refactored by Essam Mohamed Fahmi.
//

import UIKit

extension UIView
{
   static var nib: UINib
   {
      return UINib(nibName: "\(self)", bundle: nil)
   }

   static func instantiateFromNib() -> Self?
   {
      return nib.instantiate() as? Self
   }
}

extension UINib
{
   func instantiate() -> Any?
   {
      return instantiate(withOwner: nil, options: nil).first
   }
}

사용.

let myCustomView: CustomView = .instantiateFromNib()

저는 그냥 이런 식으로 합니다.

if let myView = UINib.init(nibName: "MyView", bundle: nil).instantiate(withOwner: self)[0] as? MyView {
    // Do something with myView
}

이 샘플은 메인 번들의 "MyView.xib" 니브에 있는 첫 번째 보기를 사용합니다.그러나 인덱스, 니브 이름 또는 번들(기본적으로 주)을 변경할 수 있습니다.

저는 위에서 제안한 답변처럼 뷰 init 메소드에 대한 견해를 깨우치거나 제네릭 메소드를 만들곤 했지만, 사용 사례가 종종 다르고 모든 경우를 커버하기 위해 제네릭 메소드가 UINib.instant 메소드를 사용하는 것만큼 복잡해진다는 것을 알아차렸기 때문에 더 이상 하지 않습니다.

일반적으로 뷰를 사용할 ViewController와 같은 팩토리 개체를 사용하거나 뷰를 여러 위치에서 사용해야 하는 경우 전용 팩토리 개체 또는 뷰 확장을 사용하는 것을 선호합니다.

이 예에서는 ViewController가 nib에서 보기를 로드합니다.동일한 뷰 클래스에 대해 서로 다른 레이아웃을 사용하도록 nib 파일을 변경할 수 있습니다. (이 코드는 좋은 코드가 아닙니다. 아이디어만 설명합니다.)

class MyViewController {
    // Use "MyView-Compact" for compact version
    var myViewNibFileName = "MyView-Standard"

    lazy var myView: MyView = {
        // Be sure the Nib is correct, or it will crash
        // We don't want to continue with a wrong view anyway, so ! is ok
        UINib.init(nibName: myViewNibFileName, bundle: nil).instantiate(withOwner: self)[0] as! MyView
    }()
}

Logan의 답변의 신속한 3가지 버전

extension UIView {
    public class func fromNib(nibName: String? = nil) -> Self {
        return fromNib(nibName: nibName, type: self)
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
        return fromNib(nibName: nibName, type: T.self)!
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String

        if let nibName = nibName {
            name = nibName
        } else {
            name = self.nibName
        }

        if let nibViews = Bundle.main.loadNibNamed(name, owner: nil, options: nil) {
            for nibView in nibViews {
                if let tog = nibView as? T {
                    view = tog
                }
            }
        }

        return view
    }

    public class var nibName: String {
        return "\(self)".components(separatedBy: ".").first ?? ""
    }

    public class var nib: UINib? {
        if let _ = Bundle.main.path(forResource: nibName, ofType: "nib") {
            return UINib(nibName: nibName, bundle: nil)
        } else {
            return nil
        }
    }
}

프로토콜 및 프로토콜 확장(Swift 4.2)을 사용하여 보기를 프로그래밍 방식으로 로드하는 깨끗하고 선언적인 방법은 다음과 같습니다.

protocol XibLoadable {
    associatedtype CustomViewType
    static func loadFromXib() -> CustomViewType
}

extension XibLoadable where Self: UIView {
    static func loadFromXib() -> Self {
        let nib = UINib(nibName: "\(self)", bundle: Bundle(for: self))
        guard let customView = nib.instantiate(withOwner: self, options: nil).first as? Self else {
            // your app should crash if the xib doesn't exist
            preconditionFailure("Couldn't load xib for view: \(self)")
        }
        return customView
    }
}

다음과 같이 사용할 수 있습니다.

// don't forget you need a xib file too
final class MyView: UIView, XibLoadable { ... }

// and when you want to use it
let viewInstance = MyView.loadFromXib()

가지 추가 고려 사항:

  1. 의 xib 파일이 합니다.Custom Class파일 소유자의 설정이 아닌 설정(및 거기서 설정된 아웃렛/액션).
  2. 이 프로토콜/확장 기능은 사용자 정의 보기 또는 내부에서 외부로 사용할 수 있습니다.보기를 초기화할 때 다른 설정 작업이 있는 경우 내부적으로 사용할 수 있습니다.
  3. 사용자 정의 보기 클래스와 xib 파일의 이름이 같아야 합니다.

당신이 해야 할 일은 당신의 방법에서 init 메소드를 호출하는 것입니다.UIView학생들

다음과 같은 방법으로 수행:

class className: UIView {

    @IBOutlet var view: UIView!

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

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

    func setup() {
        UINib(nibName: "nib", bundle: nil).instantiateWithOwner(self, options: nil)
        addSubview(view)
        view.frame = self.bounds
    }
}

이제 뷰 컨트롤러에서 이 뷰를 하위 뷰로 추가하려면 viewcontroller.swift 파일에서 다음과 같이 하십시오.

self.view.addSubview(className())

위의 일부 답변과 유사하지만 보다 일관된 Swift3 UIView 확장 기능:

extension UIView {
    class func fromNib<A: UIView> (nibName name: String, bundle: Bundle? = nil) -> A? {
        let bundle = bundle ?? Bundle.main
        let nibViews = bundle.loadNibNamed(name, owner: self, options: nil)
        return nibViews?.first as? A
    }

    class func fromNib<T: UIView>() -> T? {
        return fromNib(nibName: String(describing: T.self), bundle: nil)
    }
}

이는 자체 명명된 nib뿐만 아니라 다른 nib/bundle에서도 클래스를 로드할 수 있는 편리성을 제공합니다.

스토리보드를 통해 이 작업을 수행할 수 있습니다. 보기에 적합한 제약 조건을 추가하기만 하면 됩니다.당신은 당신 자신의 관점을 하위 분류함으로써 이것을 쉽게 할 수 있습니다.BaseView:

목표-C

BaseView.h


/*!
 @class BaseView
 @discussion Base View for getting view from xibFile
 @availability ios7 and later
 */
@interface BaseView : UIView

@end


BaseView.m


#import "BaseView.h"

@implementation BaseView

#pragma mark - Public

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self prepareView];
    }
    return self;
}

#pragma mark - LifeCycle

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self prepareView];
    }
    return self;
}

#pragma mark - Private

- (void)prepareView
{
    NSArray *nibsArray = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
    UIView *view = [nibsArray firstObject];

    view.translatesAutoresizingMaskIntoConstraints = NO;
    [self addSubview:view];
    [self addConstraintsForView:view];
}

#pragma mark - Add constraints

- (void)addConstraintsForView:(UIView *)view
{
    [self addConstraints:@[[NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeBottom
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeBottom
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeTop
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeTop
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeLeft
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeLeft
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeRight
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeRight
                                                       multiplier:1.0
                                                         constant:0]
                           ]];
}

@end

스위프트 4

import UIKit

class BaseView : UIView {

    // MARK: - LifeCycle

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

        prepareView()
    }

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

        prepareView()
    }

    internal class func xibName() -> String {
        return String(describing: self)
    }

    // MARK: - Private
    fileprivate func prepareView() {
        let nameForXib = BaseView.xibName()
        let nibs = Bundle.main.loadNibNamed(nameForXib, owner: self, options: nil)
        if let view = nibs?.first as? UIView {
            view.backgroundColor = UIColor.clear
            view.translatesAutoresizingMaskIntoConstraints = false
            addSubviewWithConstraints(view, offset: false)
        }
    }
}

UIView+Subview


public extension UIView {
    // MARK: - UIView+Extensions

    public func addSubviewWithConstraints(_ subview:UIView, offset:Bool = true) {
        subview.translatesAutoresizingMaskIntoConstraints = false
        let views = [
            "subview" : subview
        ]
        addSubview(subview)

        var constraints = NSLayoutConstraint.constraints(withVisualFormat: offset ? "H:|-[subview]-|" : "H:|[subview]|", options: [.alignAllLeading, .alignAllTrailing], metrics: nil, views: views)
        constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: offset ? "V:|-[subview]-|" : "V:|[subview]|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views))
        NSLayoutConstraint.activate(constraints)
    }
}

저는 제약 조건을 추가하는 두 가지 변형 - 공통적인 것과 시각적 형식 언어 내 - 원하는 것을 선택하는 방법을 제공합니다 :)

또한 기본적으로 다음과 같이 가정합니다.xib 클래스.name 의 이름과 .아니오인 경우 - 그냥 변경xibName매개 변수

는에서 당신의 를 하위 한다면,BaseView당신은 IB에 어떤 뷰든 쉽게 넣을 수 있고 클래스를 지정할 수 있습니다.

Swift UIView 하위 클래스를 완전히 자체적으로 포함하고 Nib 사용의 구현 세부사항을 노출하지 않고 init 또는 init(frame:)를 사용하여 인스턴스화할 수 있는 기능이 있으면 프로토콜 확장을 사용하여 이를 달성할 수 있습니다.이 솔루션은 다른 많은 솔루션에서 제안하는 것처럼 중첩된 UIView 계층 구조를 방지합니다.

public class CustomView: UIView {

    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var valueLabel: UILabel!

    public convenience init() {
        self.init(frame: CGRect.zero)
    }

    public override convenience init(frame: CGRect) {
        self.init(internal: nil)
        self.frame = frame
    }

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

    fileprivate func commonInit() {
    }
}

fileprivate protocol _CustomView {
}

extension CustomView: _CustomView {
}

fileprivate extension _CustomView {

    // Protocol extension initializer - has the ability to assign to self, unlike
    // class initializers. Note that the name of this initializer can be anything
    // you like, here we've called it init(internal:)

    init(internal: Int?) {
        self = Bundle.main.loadNibNamed("CustomView", owner:nil, options:nil)![0] as! Self;
    }
}
class func loadFromNib<T: UIView>() -> T {
    let nibName = String(describing: self)
    return Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)![0] as! T
}
    let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
    let shareView = nibs![0] as! ShareView
    self.view.addSubview(shareView)

이 클래스를 수퍼 뷰로 사용

import UIKit

class ViewWithXib: UIView {

func initUI() {}

private func xibSetup() {
    let view = loadViewFromNib()
    view.frame = bounds
    view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
    addSubview(view)
    initUI()
}

private func loadViewFromNib() -> UIView {
    let thisName = String(describing: type(of: self))
    let view = Bundle(for: self.classForCoder).loadNibNamed(thisName, owner: self, options: nil)?.first as! UIView
    return view
}


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

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

}

용도:

class HeaderView: ViewWithXib {
}


let header = HeaderView() // No need to load the view from nib, It will work

Logan의 답변을 기반으로 한 더욱 강력한 버전

extension UIView {
    public class func fromNib(nibName: String? = nil) -> Self {
        return fromNib(nibName: nibName, type: self)
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
        return fromNib(nibName: nibName, type: T.self)!
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String

        if let nibName = nibName {
            name = nibName
        } else {
            name = self.nibName
        }

        if let nibViews = nibBundle.loadNibNamed(name, owner: nil, options: nil) {
            if nibViews.indices.contains(nibIndex), let tog = nibViews[nibIndex] as? T {
                view = tog
            }
        }

        return view
    }

    public class var nibName: String {
        return "\(self)".components(separatedBy: ".").first ?? ""
    }

    public class var nibIndex: Int {
        return 0
    }

    public class var nibBundle: Bundle {
        return Bundle.main
    }
}

그리고 다음과 같이 사용할 수 있습니다.

class BaseView: UIView {
    override class var nibName: String { return "BaseView" }
    weak var delegate: StandardStateViewDelegate?
}

class ChildView: BaseView {
    override class var nibIndex: Int { return 1 }
}

가장 편리한 구현입니다.여기서는 UIView가 아닌 클래스의 개체로 직접 돌아가려면 두 가지 방법이 필요합니다.

  1. 클래스로 표시된 viewId, 재정의 허용
  2. .xib에는 최상위 수준의 보기가 둘 이상 포함될 수 있으며, 이 상황도 올바르게 처리됩니다.

extension UIView {

class var viewId: String {
    return String(describing: self)
}

static func instance(from bundle: Bundle? = nil, nibName: String? = nil,
                    owner: Any? = nil, options: [AnyHashable : Any]? = nil) -> Self? {

    return instancePrivate(from: bundle ?? Bundle.main,
                           nibName: nibName ?? viewId,
                           owner: owner,
                           options: options)
}

private static func instancePrivate<T: UIView>(from bundle: Bundle, nibName: String,
                                              owner: Any?, options: [AnyHashable : Any]?) -> T? {

    guard
        let views = bundle.loadNibNamed(nibName, owner: owner, options: options),
        let view = views.first(where: { $0 is T }) as? T else { return nil }

    return view
}
}

예:

guard let customView = CustomView.instance() else { return }

//Here customView has CustomView class type, not UIView.
print(customView is CustomView) // true
  let bundle = Bundle(for: type(of: self))
   let views = bundle.loadNibNamed("template", owner: self, options: nil)
    self.view.addSubview(views?[0] as! UIView)

아래의 연장을 선호합니다.

extension UIView {
    class var instanceFromNib: Self {
        return Bundle(for: Self.self)
            .loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
    }
}

이 확장명과 상위 응답 확장명의 차이점은 상수나 변수를 저장할 필요가 없다는 것입니다.

class TitleView: UIView { }

extension UIView {
    class var instanceFromNib: Self {
        return Bundle(for: Self.self)
            .loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
    }
}

self.navigationItem.titleView = TitleView.instanceFromNib

로버트 검메슨의 대답은 완벽합니다.하지만 SPM이나 프레임워크에서 사용하려고 하면 작동하지 않습니다.
작동하기 위해 아래와 같이 수정했습니다.

internal class func fromNib<T: UIView>() -> T {
    return Bundle.module.loadNibNamed(String(describing: T.self), owner: self, options: nil)![0] as! T
}

언급URL : https://stackoverflow.com/questions/24857986/load-a-uiview-from-nib-in-swift

반응형