스토리 보드의 외부 xib 파일에서보기로드


131

스토리 보드의 여러보기 컨트롤러에서보기를 사용하고 싶습니다. 따라서 외부 xib에서 뷰를 디자인하여 변경 사항이 모든 뷰 컨트롤러에 반영되도록 생각했습니다. 그러나 스토리 보드의 외부 xib에서 뷰를 어떻게로드 할 수 있습니까? 그렇지 않은 경우 상황에 맞도록 다른 대안이 있습니까?



답변:


132

내 전체 예는 여기 있지만 아래에 요약을 제공합니다.

나열한 것

동일한 이름을 가진 .swift 및 .xib 파일을 프로젝트에 추가하십시오. .xib 파일에는 사용자 정의보기 레이아웃이 포함됩니다 (자동 레이아웃 제약 조건을 사용하는 것이 좋습니다).

빠른 파일을 xib 파일의 소유자로 만듭니다.

여기에 이미지 설명을 입력하십시오 암호

다음 코드를 .swift 파일에 추가하고 .xib 파일에서 콘센트와 작업을 연결하십시오.

import UIKit
class ResuableCustomView: UIView {

    let nibName = "ReusableCustomView"
    var contentView: UIView?

    @IBOutlet weak var label: UILabel!
    @IBAction func buttonTap(_ sender: UIButton) {
        label.text = "Hi"
    }

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

        guard let view = loadViewFromNib() else { return }
        view.frame = self.bounds
        self.addSubview(view)
        contentView = view
    }

    func loadViewFromNib() -> UIView? {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: nibName, bundle: bundle)
        return nib.instantiate(withOwner: self, options: nil).first as? UIView
    }
}

그걸 써

스토리 보드의 어느 곳에서나 사용자 정의보기를 사용하십시오. a를 추가 UIView하고 클래스 이름을 사용자 정의 클래스 이름으로 설정하십시오.

여기에 이미지 설명을 입력하십시오


3
loadNibNamed가 init (coder :)를 호출하지 않습니까? 귀하의 접근 방식을 조정하려고하는 충돌이 있습니다.
Fishman

@Fishman, 스토리 보드가 아닌 프로그래밍 방식으로 뷰를로드하려고하면 현재가 없습니다 init(frame:). 자세한 내용은 이 튜토리얼 을 참조하십시오.
Suragch

7
충돌의 또 다른 일반적인 원인은 사용자 정의보기를 파일 소유자 로 설정하지 않기 때문입니다 . 내 대답에 빨간색 원이 표시됩니다.
Suragch

5
예, 파일 소유자 대신 루트보기 클래스를 설정했으며 무한 루프가 발생했습니다.
devios1

2
self.addSubview (view) 앞에 view.autoresizingMask = [.flexibleWidth, .flexibleHeight] 줄을 추가하면 완벽하게 작동합니다.
Pramod Tapaniya

69

잠시 동안 Christopher Swasey의 접근 방식 은 내가 찾은 최고의 접근 방식 이었습니다. 나는 우리 팀의 두 명의 수석 개발자에게 그것에 대해 물었고 그들 중 하나 는 완벽한 솔루션을 가지고있었습니다 ! 그것은 크리스토퍼 스와 지 (Christopher Swasey)가 웅변 적으로 해결 한 문제를 모두 만족 시키며 보일러 플레이트 하위 클래스 코드 (필요한 접근 방식)가 필요하지 않습니다. 이 하나 잡았다 , 그러나 상당히 직관적이고 쉽게 구현할 수 있다는 이외는.

  1. .swift 파일에서 사용자 정의 UIView 클래스를 작성하여 xib를 제어하십시오. 즉MyCustomClass.swift
  2. .xib 파일을 만들고 원하는대로 스타일을 지정하십시오. 즉MyCustomClass.xib
  3. 설정 File's Owner.xib 파일은 (사용자 정의 클래스가 될 수 있습니다 MyCustomClass)
  4. 잡았다 : 휴가 class(세 이하 값 identity Inspector.xib 파일 빈에서 사용자 정의보기를 들어). 따라서 사용자 정의보기에는 지정된 클래스가 없지만 지정된 파일 소유자가 있습니다.
  5. 평소와 같이 콘센트를 연결하십시오 Assistant Editor.
    • 참고 :를 보면 Connections Inspector참조 아웃렛이 사용자 정의 클래스 (예 MyCustomClass:)를 참조하는 것이 아니라 참조라는 것을 알 수 File's Owner있습니다. 이후 File's Owner사용자 정의 클래스로 지정되고, 출구는 후크와 일 때에 프로퍼티됩니다.
  6. 클래스 선언 전에 사용자 정의 클래스에 @IBDesignable이 있는지 확인하십시오.
  7. 사용자 정의 클래스가 NibLoadable아래 참조 된 프로토콜을 준수하도록하십시오 .
    • 참고 : 사용자 정의 클래스 .swift파일 이름이 파일 이름과 다른 .xib경우 nibName속성을 파일 이름으로 설정 .xib하십시오.
  8. 아래 예제와 같이 구현 required init?(coder aDecoder: NSCoder)하고 override init(frame: CGRect)호출 setupFromNib()하십시오.
  9. 원하는 스토리 보드에 UIView를 추가하고 클래스를 사용자 정의 클래스 이름 (예 :)으로 설정하십시오 MyCustomClass.
  10. 스토리 보드에서 .xib를 놀라움과 놀라움으로 그려주는 IBDesignable을보십시오.

참조하려는 프로토콜은 다음과 같습니다.

public protocol NibLoadable {
    static var nibName: String { get }
}

public extension NibLoadable where Self: UIView {

    public static var nibName: String {
        return String(describing: Self.self) // defaults to the name of the class implementing this protocol.
    }

    public static var nib: UINib {
        let bundle = Bundle(for: Self.self)
        return UINib(nibName: Self.nibName, bundle: bundle)
    }

    func setupFromNib() {
        guard let view = Self.nib.instantiate(withOwner: self, options: nil).first as? UIView else { fatalError("Error loading \(self) from nib") }
        addSubview(view)
        view.translatesAutoresizingMaskIntoConstraints = false
        view.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
        view.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
        view.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
        view.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
    }
}

다음은 MyCustomClass프로토콜을 구현 하는 예제입니다 (.xib 파일 이름 MyCustomClass.xib).

@IBDesignable
class MyCustomClass: UIView, NibLoadable {

    @IBOutlet weak var myLabel: UILabel!

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

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

}

참고 : Gotcha를 놓치고 class.xib 파일 내부 의 값을 사용자 정의 클래스로 설정하면 스토리 보드에 그려지지 않으며 EXC_BAD_ACCESS앱이 무한 루프에 빠지기 때문에 앱을 실행할 때 오류가 발생합니다 init?(coder aDecoder: NSCoder)메소드를 사용하여 펜촉에서 클래스를 초기화하려고 시도한 다음 다시 Self.nib.instantiate호출 init합니다.


2
그것에 대한 또 다른 훌륭한 접근 방법이 있지만, 위의 방법이 여전히 낫다고 생각합니다. medium.com/zenchef-tech-and-product/…
Ben Patch

4
위에서 언급 한 접근 방식은 완벽하게 작동하며 스토리 보드에서 바로 라이브 미리보기를 적용합니다. 절대적으로 편리하고 훌륭합니다.
Igor Leonovich

1
참고 :의 제약 조건 정의를 사용하는이 솔루션 setupFromNib()은 XIB 작성 뷰를 포함하는 자동 크기 조정 테이블 뷰 셀의 특정 이상한 자동 레이아웃 문제를 해결하는 것으로 보입니다.
Gary

1
지금까지 최고의 솔루션! 나는 @IBDesignable호환성을 좋아 합니다. Xcode 또는 UIKit이 UIView 파일을 추가 할 때 기본값으로 이와 같은 것을 제공하지 않는 이유를 알 수 없습니다.
fl034

1
이 작업을 수행 할 수없는 것 같습니다. .xib 파일 내에 파일 소유자를 설정할 때마다 사용자 정의 클래스도 설정됩니다.
drewster

32

사용하려는 xib를 작성했다고 가정하십시오.

1) UIView의 사용자 정의 서브 클래스를 작성하십시오 (파일-> 새로 작성-> 파일 ...-> Cocoa Touch Class로 이동하십시오. "서브 클래스 :"가 "UIView"인지 확인하십시오).

2) xib를 기반으로하는 뷰를 초기화시이 뷰에 하위 뷰로 추가하십시오.

Obj-C에서

-(id)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        UIView *xibView = [[[NSBundle mainBundle] loadNibNamed:@"YourXIBFilename"
                                                              owner:self
                                                            options:nil] objectAtIndex:0];
        xibView.frame = self.bounds;
        xibView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        [self addSubview: xibView];
    }
    return self;
}

스위프트 2에서

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    let xibView = NSBundle.mainBundle().loadNibNamed("YourXIBFilename", owner: self, options: nil)[0] as! UIView
    xibView.frame = self.bounds
    xibView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
    self.addSubview(xibView)
}

스위프트 3에서

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    let xibView = Bundle.main.loadNibNamed("YourXIBFilename", owner: self, options: nil)!.first as! UIView
    xibView.frame = self.bounds
    xibView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    self.addSubview(xibView)
}

3) 스토리 보드에서 사용하려는 곳마다 평소와 같이 UIView를 추가하고 새로 추가 된보기를 선택하고 Identity Inspector (오른쪽 상단의 세 번째 아이콘으로 선이있는 사각형 모양)로 이동하십시오. "Custom Class"아래에 "Class"로 서브 클래스의 이름을 입력하십시오.


xibView.frame = self.frame;되어야 xibView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);달리 xibView 뷰가 스토리 보드에 추가 될 때 오프셋을 가질 것이다.
BabyPanda

파티에 늦었지만 xibView.frame = self.bounds로 변경 한 것 같습니다. 오프셋이없는 프레임입니다
Heavy_Bullets

20
무한 재귀로 인해 충돌이 발생합니다. 펜촉을로드하면 서브 클래스의 다른 인스턴스가 작성됩니다.
David

2
xib 뷰의 클래스는이 새로운 서브 클래스와 동일해서는 안됩니다. xib가 MyClass 인 경우이 새 클래스를 MyClassContainer로 만들 수 있습니다.
user1021430

위의 솔루션은 무한 재귀로 충돌합니다.
JBarros35

27

나는 항상 (1) 자동 레이아웃, (2) @IBInspectable및 (3) 콘센트로 나사를 조이는 것을 보면서 "서브 뷰로 추가"솔루션이 만족스럽지 않다는 것을 알게되었습니다 . 대신, 내가의 마법을 소개하자 awakeAfter:, NSObject방법.

awakeAfterNIB / Storyboard에서 실제로 깨어 난 객체를 완전히 다른 객체로 교체 할 수 있습니다. 그런 다음 물체는 수화 과정을 거쳐 awakeFromNib호출되고 뷰로 추가됩니다.

이것을 뷰의 "카드 보드 컷 아웃"서브 클래스에서 사용할 수 있습니다. 유일한 목적은 NIB에서 뷰를로드하고 스토리 보드에서 사용하기 위해 반환하는 것입니다. 그런 다음 포함 가능한 하위 클래스가 원본 클래스가 아닌 스토리 보드보기의 ID 관리자에서 지정됩니다. 이것이 작동하기 위해 실제로 서브 클래스 일 필요는 없지만, 서브 클래스로 만드는 것은 IB가 IBInspectable / IBOutlet 특성을 볼 수있게하는 것입니다.

이 여분의 상용구는 최적이 아닌 것처럼 보일 수 있습니다. 이상적으로 UIStoryboard는 완벽하게 처리 할 수 있기 때문 입니다. 그러나 원래 NIB 및 UIView하위 클래스를 완전히 수정하지 않은 상태 로 둘 수 있다는 이점이 있습니다. 그것이 수행하는 역할은 기본적으로 어댑터 또는 브리지 클래스의 역할이며, 유감스럽게도 추가 클래스로서 디자인 측면에서 완벽하게 유효합니다. 반면에, 클래스와의 조화를 원한다면 @BenPatch의 솔루션은 다른 사소한 변경 사항이있는 프로토콜을 구현하여 작동합니다. 어떤 솔루션이 더 나은지에 대한 질문은 프로그래머 구성의 문제로 요약됩니다. 하나는 객체 구성을 선호하는지 아니면 다중 상속을 선호하는지 여부입니다.

참고 : NIB 파일의보기에서 설정된 클래스는 동일하게 유지됩니다. 임베드 가능한 서브 클래스는 스토리 보드 에서만 사용됩니다. 코드에서 뷰를 인스턴스화하는 데 서브 클래스를 사용할 수 없으므로 추가 로직 자체가 없어야합니다. 후크 포함 해야 합니다awakeAfter .

class MyCustomEmbeddableView: MyCustomView {
  override func awakeAfter(using aDecoder: NSCoder) -> Any? {
    return (UIView.instantiateViewFromNib("MyCustomView") as MyCustomView?)! as Any
  }
}

⚠️ 여기서 중요한 단점은 스토리 보드에서 다른 뷰와 관련이없는 너비, 높이 또는 종횡비 제약 조건을 정의하면 수동으로 복사해야한다는 것입니다. 두 개의 뷰와 관련된 제약 조건은 가장 가까운 공통 조상에 설치되며 뷰는 내부에서 스토리 보드에서 깨어 났으므로 이러한 제약 조건이 슈퍼 뷰에서 수화 될 때까지 스왑이 이미 발생했습니다. 해당 뷰에만 관련된 제약 조건은 해당 뷰에 직접 설치되므로 스왑이 발생하지 않으면 복사되지 않습니다.

여기서 일어나는 것은 스토리 보드 의 뷰 설치된 제약 조건 이 새로 인스턴스화 된 뷰로 복사되며, 이미 nib 파일에 정의 된 자체 제약 조건이있을 수 있습니다. 그것들은 영향을받지 않습니다.

class MyCustomEmbeddableView: MyCustomView {
  override func awakeAfter(using aDecoder: NSCoder) -> Any? {
    let newView = (UIView.instantiateViewFromNib("MyCustomView") as MyCustomView?)!

    for constraint in constraints {
      if constraint.secondItem != nil {
        newView.addConstraint(NSLayoutConstraint(item: newView, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: newView, attribute: constraint.secondAttribute, multiplier: constraint.multiplier, constant: constraint.constant))
      } else {
        newView.addConstraint(NSLayoutConstraint(item: newView, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: constraint.constant))
      }
    }

    return newView as Any
  }
}  

instantiateViewFromNib형식 안전 확장 UIView입니다. 유형과 일치하는 것을 찾을 때까지 NIB의 객체를 반복합니다. 제네릭 형식은 반환 값이므로 호출 사이트에서 형식을 지정해야합니다.

extension UIView {
  public class func instantiateViewFromNib<T>(_ nibName: String, inBundle bundle: Bundle = Bundle.main) -> T? {
    if let objects = bundle.loadNibNamed(nibName, owner: nil) {
      for object in objects {
        if let object = object as? T {
          return object
        }
      }
    }

    return nil
  }
}

그것은 마음이 흔들리는 것입니다. 그래도 실수하지 않으면 "스토리 보드에서만"작동합니다. 런타임에 코드에서 이러한 클래스를 만들려고하면 작동하지 않는다고 생각합니다. 나는 믿는다.
Fattie

서브 클래스는 모든 의도와 목적을 위해 원래 클래스뿐만 아니라 코드에서도 작동해야합니다. 코드의 펜촉에서 뷰를로드하려면 동일한 기술을 사용하여 뷰를 직접 인스턴스화하면됩니다. 모든 서브 클래스가 수행하는 작업은 코드를 가져와 펜촉에서 뷰를 인스턴스화하여 스토리 보드가 사용할 수 있도록하는 것입니다.
Christopher Swasey 2012 년

사실 내가 잘못 - 그것이었다 펜촉에서보기 때문에 그 유형으로 슈퍼 클래스를 가지고 있기 때문에, 당신은 할 수 없습니다 당신이 그것을 인스턴스화 할 수 있다면 마찬가지로 잘 작동하지만 instantiateViewFromNib아무것도 반환하지 않습니다. 하위 클래스는 IMO가 아니더라도 스토리 보드에 연결하는 데 도움이됩니다. 모든 코드는 원래 클래스에 있어야합니다.
Christopher Swasey 2012 년

1
큰! xib에 대한 경험이 거의 없기 때문에 한 가지 문제가 발생했습니다 (스토리 보드 및 프로그래밍 방식으로 만 작업했습니다). 클래스 유형은 MyCustomView입니다. 내 xib에서 왼쪽 내부 사이드 바는 기본적으로 누락되었습니다. 이 기능을 켜려면 하단 / 왼쪽 근처의 "보기 : iPhone 7"특성 컨트롤 옆에 단추가 있습니다.
xaphod

5
다른 객체로 교체 할 때 구속 조건을 제동합니다. :(
invoodoo

6

가장 좋은 해결책은 현재 xib에 정의 된보기를 가진 사용자 정의보기 컨트롤러를 사용하고보기 컨트롤러를 추가 할 때 스토리 보드 내에서 Xcode가 생성하는 "view"속성을 삭제하는 것입니다 (이름을 설정하는 것을 잊지 마십시오) 그래도 사용자 정의 클래스).

그러면 런타임에서 자동으로 xib를 찾아서로드합니다. 모든 종류의 컨테이너보기 또는 컨텐츠보기에이 트릭을 사용할 수 있습니다.


5

나는 별도의 스토리 보드에서alternative 사용 XIB views하는 것에 대해 생각 합니다.View Controller

그런 다음 사용자 정의보기 사용 대신에 주요 스토리 보드 container view와 함께 Embed Segue하고있는 StoryboardReference이에 사용자 지정보기 컨트롤러 의 주요 스토리 보드에서 다른보기 내부에 배치해야 볼 수 있습니다.

그런 다음 segue 준비를 통해이 내장 ViewController와 기본 뷰 컨트롤러간에 위임 및 통신을 설정할 수 있습니다 . 이 접근 방식은 UIView를 표시하는 것과는 다르지만 프로그래밍 관점에서 볼 때 훨씬 간단하고 효율적으로 동일한 목표를 달성 할 수 있습니다. 즉, 메인 스토리 보드에서 볼 수있는 재사용 가능한 사용자 정의보기가 있습니다.

추가 이점은 CustomViewController 클래스에서 로직을 구현할 수 있으며 별도의 (프로젝트에서 찾기가 더 어려운) 컨트롤러 클래스를 만들지 않고 Component를 사용하여 기본 UIViewController에 상용구 코드를 배치하지 않고 모든 위임 및 뷰 준비를 설정할 수 있다는 것입니다. 나는 이것이 재사용 가능한 구성 요소에 좋다고 생각합니다. 다른보기에 포함 할 수있는 뮤직 플레이어 구성 요소 (위젯처럼).


실제로 커스텀 뷰 xib를 사용하는 것보다 슬프게도 훨씬 더 간단한 해결책 :(
Wilson

1
사용자 정의 UIView에서 애플의 xib 라이프 사이클 생성 체인을 사용하여 서클에 들어간 후에 이것이 끝났습니다.
LightningStryk

사용자 지정 뷰를 동적으로 추가하려는 경우 별도의 뷰 컨트롤러 (가장 바람직하게는 XIB)를 사용해야합니다
Satyam

5

가장 인기있는 답변은 잘 작동하지만 개념적으로 잘못되었습니다. 그들은 모두 File's owner클래스의 콘센트와 UI 구성 요소 간의 연결로 사용 됩니다. s가 File's owner아닌 최상위 개체에만 사용해야합니다 UIView. Apple 개발자 문서를 확인하십시오 . UIView를 사용 File's owner하면 이러한 바람직하지 않은 결과가 발생합니다.

  1. 당신은 당신이 사용해야하는 contentView곳에서 사용해야합니다self 합니다. 중간보기는 데이터 구조가 UI 구조를 전달하지 못하기 때문에 추악 할뿐만 아니라 구조적으로 잘못되었습니다. 선언적 UI와 반대입니다.
  2. Xib 당 하나의 UIView 만 가질 수 있습니다. Xib에는 여러 UIView가 있어야합니다.

를 사용하지 않고 우아한 방법이 있습니다 File's owner. 이 블로그 게시물을 확인하십시오 . 올바른 방법으로 설명합니다.


왜 나쁜지 설명하지 않은 것은 중요하지 않습니다. 왜 그런지 알 수 없습니다. 부작용이 없습니다.
JBarros35

1

여기 당신이 원했던 답이 있습니다. CustomView클래스를 만들고 모든 서브 뷰와 아웃렛이있는 xib에 마스터 인스턴스를 만들 수 있습니다 . 그런 다음 스토리 보드 또는 다른 xib의 모든 인스턴스에 해당 클래스를 적용 할 수 있습니다.

File 's Owner와 바이올린을 연결하거나 콘센트를 프록시에 연결하거나 xib를 고유 한 방식으로 수정하거나 사용자 정의보기의 인스턴스를 자체의 하위보기로 추가 할 필요가 없습니다.

그냥 이렇게 :

  1. BFWControls 프레임 워크 가져 오기
  2. 에서 슈퍼 클래스를 변경 UIView하는 NibView(또는에서 UITableViewCell까지 NibTableViewCell)

그게 다야!

스토리 보드에서 디자인 타임에 사용자 정의보기 (xib의 하위보기 포함)를 참조하기 위해 IBDesignable 과도 작동합니다.

https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155에 대한 자세한 내용은 여기를 참조 하십시오.

https://github.com/BareFeetWare/BFWControls 에서 오픈 소스 BFWControls 프레임 워크를 얻을 수 있습니다.

NibReplaceable궁금한 경우를 대비 하여 코드를 구동 하는 간단한 코드 추출은 다음과 같습니다.
 https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4

톰 👣


멋진 솔루션, 감사합니다!
avee

기본적으로 제공되어야 할 때 프레임 워크를 설치하고 싶지 않습니다.
JBarros35

0

이 솔루션은 클래스 이름이 XIB와 동일하지 않은 경우에도 사용할 수 있습니다. 예를 들어, XIB 이름이 controllerA.xib 인 기본보기 컨트롤러 클래스 controllerA가 있고이를 controllerB로 서브 클래 싱하고 스토리 보드에서 controllerB의 인스턴스를 작성하려는 경우 다음을 수행 할 수 있습니다.

  • 스토리 보드에서 뷰 컨트롤러 생성
  • 컨트롤러의 클래스를 controllerB로 설정
  • 스토리 보드에서 controllerB의보기를 삭제하십시오.
  • controllerA의로드 뷰를 재정 의하여 다음을 수행합니다.

*

- (void) loadView    
{
        //according to the documentation, if a nibName was passed in initWithNibName or
        //this controller was created from a storyboard (and the controller has a view), then nibname will be set
        //else it will be nil
        if (self.nibName)
        {
            //a nib was specified, respect that
            [super loadView];
        }
        else
        {
            //if no nib name, first try a nib which would have the same name as the class
            //if that fails, force to load from the base class nib
            //this is convenient for including a subclass of this controller
            //in a storyboard
            NSString *className = NSStringFromClass([self class]);
            NSString *pathToNIB = [[NSBundle bundleForClass:[self class]] pathForResource: className ofType:@"nib"];
            UINib *nib ;
            if (pathToNIB)
            {
                nib = [UINib nibWithNibName: className bundle: [NSBundle bundleForClass:[self class]]];
            }
            else
            {
                //force to load from nib so that all subclass will have the correct xib
                //this is convenient for including a subclass
                //in a storyboard
                nib = [UINib nibWithNibName: @"baseControllerXIB" bundle:[NSBundle bundleForClass:[self class]]];
            }

            self.view = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
       }
}

0

Ben Patch의 응답에 설명 된 단계에 따른 Objective-C 솔루션 .

UIView 확장을 사용하십시오.

@implementation UIView (NibLoadable)

- (UIView*)loadFromNib
{
    UIView *xibView = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] firstObject];
    xibView.translatesAutoresizingMaskIntoConstraints = NO;
    [self addSubview:xibView];
    [xibView.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES;
    [xibView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES;
    [xibView.leftAnchor constraintEqualToAnchor:self.leftAnchor].active = YES;
    [xibView.rightAnchor constraintEqualToAnchor:self.rightAnchor].active = YES;
    return xibView;
}

@end

파일을 작성 MyView.h, MyView.m하고 MyView.xib.

먼저 당신의 준비 MyView.xib벤 패치의 반응이 너무 세트 클래스 말한다 MyView대신 XIB 내부 기본보기의 파일의 소유자가.

MyView.h:

#import <UIKit/UIKit.h>

IB_DESIGNABLE @interface MyView : UIView

@property (nonatomic, weak) IBOutlet UIView* someSubview;

@end

MyView.m:

#import "MyView.h"
#import "UIView+NibLoadable.h"

@implementation MyView

#pragma mark - Initializers

- (id)init
{
    self = [super init];
    if (self) {
        [self loadFromNib];
        [self internalInit];
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self loadFromNib];
        [self internalInit];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self loadFromNib];
    }
    return self;
}

- (void)awakeFromNib
{
    [super awakeFromNib];
    [self internalInit];
}

- (void)internalInit
{
    // Custom initialization.
}

@end

나중에 프로그래밍 방식으로 뷰를 만듭니다.

MyView* view = [[MyView alloc] init];

경고! Xcode> = 9.2의이 버그로 인해 WatchKit Extension을 사용하는 경우이보기의 미리보기가 스토리 보드에 표시되지 않습니다 : https://forums.developer.apple.com/thread/95616


Swift 버전을 제공해야합니다. 이 시점에서 여전히 ObjC를 사용하는 사람은 거의 없습니다
Satyam
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.