Swift의 펜촉에서 UIView로드


148

다음은 내 사용자 정의를 위해 펜촉을로드하는 데 사용하는 Objective-C 코드입니다 UIView.

-(id)init{

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

}

Swift에서 동등한 코드는 무엇입니까?

답변:


172

독창적 인 솔루션

  1. XIB와 SomeView라는 클래스를 만들었습니다 (편의성과 가독성을 위해 같은 이름을 사용했습니다). UIView를 기반으로합니다.
  2. XIB에서는 "파일 소유자"클래스를 SomeView (ID 관리자에서)로 변경했습니다.
  3. SomeView.swift에 UIView 콘센트를 만들어 XIB 파일의 최상위 뷰에 연결했습니다 (편의를 위해 "view"로 명명). 그런 다음 필요에 따라 XIB 파일의 다른 컨트롤에 다른 콘센트를 추가했습니다.
  4. SomeView.swift에서는 "init with code"이니셜 라이저에 XIB를로드했습니다. "self"에 아무것도 할당 할 필요가 없습니다. 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 유형의 선택적 객체를 반환하는 일반적인 방법입니다. 뷰를로드하지 못하면 nil을 반환합니다.
  3. 현재 클래스 인스턴스와 동일한 이름의 XIB 파일을로드하려고합니다. 실패하면 nil이 리턴됩니다.
  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 서브 클래스입니다. "final"키워드는 선택 사항입니다.
  2. 스토리 보드에서보기를 사용할 때의 초기화 프로그램 (스토리 보드보기의 사용자 정의 클래스로 SomeClass를 사용해야 함).
  3. 뷰가 프로그래밍 방식으로 생성 될 때의 초기화 프로그램입니다 (예 : "let myView = SomeView ()").
  4. 이 뷰는 자동 레이아웃을 사용하여 배치되므로 0이 아닌 프레임을 사용합니다. 프로젝트에서 자동 레이아웃이 독점적으로 사용되므로 "init (frame : CGRect) {..}"메소드는 독립적으로 생성되지 않습니다.
  5. & 6. 확장자를 사용하여 xib 파일로드.

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

혼동을 피하기 위해 "view"를 "contentView"로 변경 하십시오 . 또한 배열 첨자를 ".first"로 변경했습니다.


7
File's Owner반점 을 맞추기 위해 수업 이름을 설정하는 중 ... 감사합니다!
Aviel Gross

13
UIView에는 속성보기가 없으므로 self.view를 호출하면 오류가 발생합니다
Nastya Gorban

4
@NastyaGorban self.view는 실제로이 경우 GK100이 .xib의 최상위 레벨 뷰에서 SomeView.swift로 링크 된 콘센트 속성 ( "view")을 참조합니다. 해당 콘센트를 추가하지 않으면 "view"가 없으므로 오류가 발생합니다 "당신이 말한 NSView 클래스의 속성
Ausiàs

3
nib (loadNibNamed)를로드 할 때 충돌이 발생합니다. 엑스 코드 6.3 및 스위프트 사용
karthikPrabhu Alagu

11
전화 fromNib()내에서 init(coder aDecoder: NSCoder)공진 영역 펜촉로드로서의 무한 루프 생성 fromNib(): 방법은 호출 할 수있다init(coder aDecoder: NSCoder)
마 콜리

334

내 공헌 :

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()

20
최고의 답변.
CodyMace

7
가장 좋은 답변은 여기입니다. 깨끗하고
단순함

3
@YuchenZhong-옵션을 반환하므로 .first보다 [0]을 선호합니다. 포장을 강제로 풀면 더 안전하지 않습니다. ... 그리고 이것은 위의 해결책 중 하나로서 옵션을 반환하지 않습니까? 답 : 가능합니다. 아무 문제가 없습니다. 그러나 ... nil을 반환하면 xib / class의 이름이 일치하지 않습니다. 이것은 개발자의 실수이며 즉시 포착되어 생산에 걸리지 않아야합니다. 여기서는 이상한 상태로 두는 대신 앱이 충돌하는 것을 선호합니다. 내 2 센트 / 선호
Robert Gummesson 2016 년

1
@allenlinli-이 메소드는 CustomView에 대한 UIView의 정적 확장입니다. 컴파일러가 명시 적 형식 주석을 사용하여 형식을 유추하기 때문에 작동합니다. CustomView는 UIView의 서브 클래스이고 유형이 이미 유추되었으므로 다시 유추 할 필요가 없으므로 UIView는 두 번째 예와 같이 생략 할 수 있습니다. 그렇게 말하면, 당신은 분명히 당신이 그것을 내려 놓는 방식으로 전화를 걸 수 있습니다.
Robert Gummesson 2016 년

3
이 솔루션은 .xib 내부에 사용자 정의보기가있는 경우에는 효과가 없었습니다. 이 부분을 다음과 같이 수정하는 것이 좋습니다 : return Bundle.main.loadNibNamed (String (decribing : self), owner : nil, options : nil)! [0] as! T
Nadzeya

79

이제 -> Self신속하게 돌아올 수 있으면 이 부분이 약간 단순화됩니다. Swift 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")

노트:

"YourType 유형을 찾을 수 없습니다."오류가 표시되면 .xib파일 에서보기 클래스를 설정하지 않은 것입니다.

에서보기를 선택 .xib파일을 누릅니다 cmd + opt + 4과에 class입력 클래스를 입력


1
XCode 7.1 베타 3에서 작동하도록 할 수는 없습니다. 베타 일인지 확실하지 않지만 기본적으로 Swift의 펜촉에서 직접 사용자 정의보기를 만드는 모든 방법을 시도했지만 항상 동일한 결과를 얻습니다. 콘센트와 KVC를 준수하지 않습니다. 그것이 내가 잘못하고있는 것인지 확실하지 않지만 수업은 매우 간단하고 파일 소유자가 정확합니다. Objective-C에서 항상이 작업을 수행했습니다.
Echelon

1
@Logan 실제로 코드와 관련이 없지만 imo 사용자 정의 뷰는 Storyboard / XIB에서로드를 지원해야합니다. 내 의견은 그러한 견해를 만들고 싶은 사람들을위한 알림 일뿐입니다.
Nikita Took

1
참고이 함수를 호출하는 두 번째 형식, 즉를 사용하는 데 여전히 문제가 있습니다 let myCustomView = UIView.fromNib() as? CustomView. 이 경우에, 오히려 T.self해결 하고 펜촉을 찾지 못합니다. 왜 이것이 함수인지에 대한 유추 된 유형인지 확실하지 않습니다 . UIViewCustomViewletUIView
Echelon

2
파일 소유자를 사용하여 콘센트를 연결하려고 시도하면 (이전의 좋은 시절에했던 것처럼) 이것이 중단 될 수 있다는 점을 지적하는 것이 중요합니다. IB에서 파일 소유자는 비어 있거나 비어 있어야하며 콘센트는 대신보기에 연결되어야합니다.
Echelon

1
@Echelon 당신은 내 하루를 저장 !!! File 's Owner를 사용하여 콘센트를 연결했지만 작동하지 않고 뷰가 대신 작동했습니다.
Jan Schlorf

19

다음 코드를 시도하십시오.

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
    }


}

Swift에서 init () 인 객체 초기화 메소드 내 에서이 메소드를 호출해야합니다.
Bagusflyer

12

스위프트 4 프로토콜 확장

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()

3
이것은 지금까지 가장 우아하고 간단한 솔루션이며 이것이 왜 받아 들일 수 없는지 모르겠습니다!
horseshoe7

@ horseshoe7은 질문 이후 4 년 동안 작성 되었기 때문입니다.
Freeubi

11

다음 코드로 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 보기 콘센트 를 연결하는 것을 잊지 마십시오 . 또한 첫 번째 응답자를 사용자 정의 클래스 이름으로 설정하여 추가 콘센트를 연결할 수 있습니다.

도움이 되었기를 바랍니다!


10

Xcode 7 베타 4, Swift 2.0 및 iOS9 SDK에서 테스트되었습니다. 다음 코드는 xib를 uiview에 할당합니다. 스토리 보드에서이 사용자 정의 xib보기를 사용하고 IBOutlet 오브젝트에도 액세스 할 수 있습니다.

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);



    }


}

프로그래밍 방식으로 customview에 액세스

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

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


9

프로젝트에 많은 사용자 정의보기가 있으면 다음과 같은 클래스를 만들 수 있습니다 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)
    }
}

스위프트 3

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() {
        contentView = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?[0] as! UIView
        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

그리고 모든 클래스에서 단지에서 상속받습니다. 파일 이름이 다른 경우 속성 UIViewFromNib을 재정의 할 수도 있습니다 .nibName.xib

class MyCustomClass: UIViewFromNib {

}

8

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

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

스위프트 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")

6

스위프트 4

".first as? CustomView"를 작성하는 것을 잊지 마십시오.

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

어디서나 사용하고 싶다면

최선의 해결책은 Robert Gummesson 의 답변입니다.

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()

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

그러나 Swift의 init ()에는 반환 값이 없습니다. UIView 초기화시 loadNibNamed를 호출해야한다는 것을 언급하지 않았습니다.
Bagusflyer

"반환 값 없음"은 무슨 뜻입니까? self모든 init메소드 에서 암시 적으로 리턴됩니다 ...
Grimxn

1
내 말은 init 메소드 내에서 loadNibNamed를 호출한다는 것입니다. 로드 된 UIView는 ObjC에서 자체에 할당됩니다. 그러나 신속하게 그렇지 않습니다.
Bagusflyer

5

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()

2
비어있는 펜촉 파일 또는 UIView 루트 노드가없는 펜촉 파일로이 작업을 수행하면 배열 크기 나 위치 0의 요소를 검사하는 것이 정상이 아니므로 충돌이 발생합니다.
Matthew Cawley

4

이 솔루션을 선호합니다 (@ GK100의 경우 답변을 기반으로 함).

  1. XIB와 SomeView라는 클래스를 만들었습니다 (편의성과 가독성을 위해 같은 이름을 사용했습니다). UIView를 기반으로합니다.
  2. XIB에서는 "파일 소유자"클래스를 SomeView (ID 관리자에서)로 변경했습니다.
  3. SomeView.swift에 UIView 콘센트를 만들어 XIB 파일의 최상위 뷰에 연결했습니다 (편의를 위해 "view"로 명명). 그런 다음 필요에 따라 XIB 파일의 다른 컨트롤에 다른 콘센트를 추가했습니다.
  4. SomeView.swift에서 XIB를 init또는 init:frame: CGRect초기화 프로그램에 로드했습니다 . "self"에 아무것도 할당 할 필요가 없습니다. 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
      }
    
    
      ...
    }

나는 이것을 inroot로 사용하는 것을 선호하므로 이것을 뿌리 뽑았다! 한 가지주의 할 점 ... 전달한 프레임과보기를 일치 시키려면 self.view.frame = frame 추가
Mike E

3

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
        }
    }
}

3

다음은 프로토콜 및 프로토콜 확장 (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 파일의 이름이 같아야합니다.

2

나는 단지 이런 식으로한다 :

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

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

나는 뷰를 init 메소드로 깨우거나 위의 솔루션 (일반적으로 똑똑한)과 같은 일반적인 메소드를 만들었지 만 더 이상 그렇게하지 않습니다.

이 방법으로 동일한 뷰 로직과 코드를 유지하면서 다른 레이아웃이나 특성을 사용할 수 있습니다.

팩토리 객체 (보통 view를 사용하는 viewController)가 필요에 따라 생성하게하는 것이 더 쉽다는 것을 알았습니다. 때로는 소유자가 필요합니다 (일반적으로 생성 된 뷰에 생성자와 연결된 콘센트가있을 때).

아마도 Apple이 initFromNibUIView 클래스에 메소드를 포함시키지 않은 이유 일 것입니다 ...

지면 수준의 예를 들어 보면 자신이 어떻게 태어 났는지 알 수 없습니다. 당신은 단지 태어났습니다. 뷰도 마찬가지입니다.)


2

UIView수업 에서 init 메소드를 호출하기 만하면 됩니다.

그렇게하세요 :

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
    }
}

이제이보기를보기 컨트롤러에서 하위보기로 추가하려면 view controller.swift 파일에서 그렇게하십시오.

self.view.addSubview(className())

큰 대답이지만 잘못된 것이 있습니다. 편집하겠습니다.
C0mrade

내가 구현 한 방식입니다. 그러나 당신은 그것을 즉흥적으로 할 수 있습니다. 미리 감사드립니다 @ C0mrade
Alap Anerao

1

위의 답변 중 일부와 유사하지만보다 일관된 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)
    }
}

자체 이름의 펜촉뿐만 아니라 다른 펜촉 / 번들에서 클래스를로드 할 수있는 편리함을 제공합니다.


1

스토리 보드를 통해이 작업을 수행 할 수 있으며보기에 적절한 제약 조건을 추가하십시오. 자신의 관점에서 뷰를 서브 클래 싱하여이를 쉽게 수행 할 수 있습니다 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이름은 구현 클래스 이름과 이름이 같다고 가정합니다 . 그렇지 않은 경우-변경 만xibName 매개 변수 .

뷰를 서브 클래 싱하는 경우 BaseView- 뷰를 쉽게 배치하고 IB에서 클래스를 지정할 수 있습니다.


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

0

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 }
}

0

가장 편리한 구현입니다. 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

0

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;
    }
}

0
  let bundle = Bundle(for: type(of: self))
   let views = bundle.loadNibNamed("template", owner: self, options: nil)
    self.view.addSubview(views?[0] as! UIView)

코드 만 답변하지 않는 것이 좋습니다. 이것이 어떻게 문제를 해결하는지 또는 이것이 기존 답변과 어떻게 다른지에 대한 설명을 추가하십시오. 검토에서

0

아래 확장을 선호합니다

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

어떤 Xcode 버전을 사용하고 있습니까? 최신 버전의 XCode를 사용하고 있는지 확인하십시오. XCode 11.5 (현재 최신 버전)에서 제대로 작동합니다.
iCoder

0
    let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
    let shareView = nibs![0] as! ShareView
    self.view.addSubview(shareView)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.