Interface Builder로 작성된 nib 파일을 사용하여 UIView를로드하는 방법


241

조금 정교한 작업을 시도하고 있지만 가능한 일을하려고합니다. 그래서 여기에 모든 전문가에게 도전이 있습니다 (이 포럼은 많은 사람들이 모여 있습니다 :)).

설문지 "구성 요소"를 작성하고 있는데 NavigationContoller(my QuestionManagerViewController) 에로드하려고합니다 . "컴포넌트"는 "빈"UIViewController , 답변이 필요한 질문에 따라 다른보기를로드 할 수 있습니다.

내가하는 방식은 다음과 같습니다.

  1. Question1View 객체를 UIView서브 클래스 로 생성하여IBOutlets .
  2. (인터페이스 빌더를 사용하여) Question1View.xib (내 문제는 어디에 있습니까? )를 작성하십시오. UIViewControllerand UIView클래스를 Question1View 클래스로 설정했습니다 .
  3. 출구를 뷰의 구성 요소와 연결합니다 (IB 사용).
  4. 나는 우선 initWithNib내의를 QuestionManagerViewController같이하기 :

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
        if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) {
            // Custom initialization
        }
        return self;
    }

코드를 실행할 때 다음 오류가 발생합니다.

2009-05-14 15 : 05 : 37.152 iMobiDines [17148 : 20b] *** 발견되지 않은 예외 ' NSInternalInconsistencyException'(으) 로 인해 앱을 종료하는 이유 : ' -[UIViewController _loadViewFromNibNamed:bundle:]"Question1View"펜촉이로드되었지만 뷰 콘센트가 설정되지 않았습니다.'

viewController 클래스를 만들 필요없이 nib 파일을 사용하여 뷰를로드하는 방법이 있다고 확신합니다.

답변:


224

펜촉을 배열로 처리하는 대신보기에 액세스하는 더 쉬운 방법도 있습니다.

1 ) 나중에 액세스하려는 콘센트가있는 사용자 정의 View 서브 클래스를 작성하십시오. --MyView

2 ) nib를로드하고 처리하려는 UIViewController에서로드 된 nib의 뷰를 보유 할 IBOutlet 특성을 작성하십시오 (예 :

MyViewController (UIViewController 서브 클래스)

  @property (nonatomic, retain) IBOutlet UIView *myViewFromNib;

(합성하여 .m 파일로 릴리스하는 것을 잊지 마십시오)

3) IB에서 펜촉을 열고 ( 'myViewNib.xib'라고 함) 파일 소유자를 MyViewController로 설정하십시오.

4) 이제 파일의 소유자 콘센트 myViewFromNib를 펜촉의 기본보기에 연결하십시오.

5) 이제 MyViewController에서 다음 줄을 작성하십시오.

[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];

이제 속성을 "self.myViewFromNib"로 호출하면 펜촉에서 뷰에 액세스 할 수 있습니다!


4
메인 컨트롤러보기 (self.view)에서 작동합니까? 어떤 이유로 표준 컨트롤러 init 방법 후에 nib에서 메인 뷰를로드해야합니다. 이유-복잡한 소거 구조
루카스

@Lukasz 얼마나 성공할 수 있습니까? 심지어 당신과 같은 것을 찾고 있습니다.
Ajay Sharma

두껍게해서 미안하지만 처음에는 혼란 스러워요. 사용자 정의보기 서브 클래스에서 아울렛을 작성하는 방법 아울렛은 UIViewController 전용이라고 생각 했습니까?
jowie

1
이 뷰가 여러 상위 뷰에 나타나는 경우는 어떻습니까? 그렇다면 각각 수동으로 xib를 편집 할 것을 제안합니까? 너무 나쁜입니다 !!!
user2083364

2
단일 뷰 컨트롤러에서 커스텀 펜촉을 사용하려는 경우에만 작동합니다. 각각 다른 커스텀 펜촉의 인스턴스를 생성하는 다른 뷰 컨트롤러에서 사용하려는 경우에는 작동하지 않습니다.
thgc

120

다들 감사 해요. 나는 내가 원하는 것을 할 수있는 방법을 찾았다.

  1. 당신 UIView과 함께 당신 을 만드십시오 IBOutlet.
  2. IB에서 xib를 작성하여 원하는대로 디자인하고 다음과 같이 링크하십시오 UIViewController. 파일 소유자의보기가 기본보기에 연결되고 해당 클래스가 1) 단계에서 선언됩니다.
  3. 컨트롤을 IBOutlets 와 연결하십시오 .
  4. DynamicViewController부하 / XIB를 볼 것을 결정하는 그 논리를 실행할 수 있습니다. 그것이 결정을 내리면, loadView방법에 다음과 같이 넣으십시오 :

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
                                                      owner:self
                                                    options:nil];
    
    QPickOneView* myView = [ nibViews objectAtIndex: 1];
    
    myView.question = question;

그게 다야!

메인 번들의 loadNibNamed메소드는 뷰를 초기화하고 연결을 생성합니다.

이제 ViewController는 메모리의 데이터에 따라 뷰 또는 다른 뷰를 표시 할 수 있으며 "부모"화면은이 논리를 신경 쓸 필요가 없습니다.


2
매우 비슷한 문제가 있습니다. xib 파일에서 UIView를로드하고 싶습니다. 이 단계는 작동하지 않습니다.로드 된보기를 하위보기로 추가 한 후 곧 앱이 "잘못된 액세스"와 충돌합니다.
Justicle

2
iPhone 3.0의 경우 objectAtIndex : 0을 사용하여 첫 번째 요소를 가져 오십시오. Justicle에서 설명한대로 정확하게 충돌합니다. 왜 그런지 알아?
tba

1
+1 그것은 나를 위해 완벽하게 작동했습니다. 나는 (메신저 뷰의 섹션이 회전 플립보기 시나리오를 사용) 내가 배열 0에 인덱스했습니다 하나의 뷰 컨트롤러를 가지고 여러 뷰 (아이폰 3.0)을 추가하고 싶었
아란 멀홀랜드

42
이것은 정답이지만 코드는 nibView를 통해 반복되고 각 객체에 대해 클래스 검사를 수행하여 올바른 객체를 얻을 수 있도록 수정해야합니다. objectAtIndex : 1은 위험한 가정입니다.
M. Ryan

2
Jasconius가 옳습니다. 다음과 같이 nibViews 배열을 반복해야합니다. gist.github.com/769539
leviathan

71

답변 중 일부에 대해 잘 모르겠지만 다음에 Google에서 검색 할 때이 답변을 입력해야합니다. 키워드 : "nib에서 UIView를로드하는 방법"또는 "NSBundle에서 UIView를로드하는 방법"

다음은 Apress Beginning iPhone 3 책 (247 페이지의 "새 테이블 뷰 셀 사용") 에서 거의 100 % 완성 된 코드입니다 .

- (void)viewDidLoad {
    [super viewDidLoad];
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah"
                                                 owner:self options:nil];
    Blah *blah;
    for (id object in bundle) {
        if ([object isKindOfClass:[Blah class]]) {
            blah = (Blah *)object;
            break;
        }
    }   
    assert(blah != nil && "blah can't be nil");
    [self.view addSubview: blah];
} 

이것은 클래스가로 설정된 것을 포함하는 nib라는 UIView서브 클래스 가 있다고 가정합니다 .BlahBlahUIViewBlah


카테고리 : NSObject + LoadFromNib

#import "NSObject+LoadFromNib.h"

@implementation NSObject (LoadFromNib)

+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:classToLoad]) {
            return object;
        }
    }
    return nil;
}

@end

스위프트 확장

extension UIView {
    class func loadFromNib<T>(withName nibName: String) -> T? {
        let nib  = UINib.init(nibName: nibName, bundle: nil)
        let nibObjects = nib.instantiate(withOwner: nil, options: nil)
        for object in nibObjects {
            if let result = object as? T {
                return result
            }
        }
        return nil
    }
}

그리고 사용중인 예 :

class SomeView: UIView {
    class func loadFromNib() -> SomeView? {
        return self.loadFromNib(withName: "SomeView")
    }
}

2
를 사용하면 isKindOf번들 구조 변경으로부터 보호됩니다.
Dan Rosenstark

1
assert아마 더 나은 솔루션이 될 것입니다. 이렇게하면 문제 만 숨길 수 있습니다.
Sulthan

1
@Sulthan assert는 본질적으로 다릅니다. 나는 Nib의 어느 곳에 나 Blah 타입의 객체를 원한다 . 나는 그것이 승진 또는 강등을 받았는지 상관하지 않습니다. 그러나 귀하의 의견에 따라 어설 션을 추가했지만 루프 를 대신 하지는 않습니다for . 그것을 보완합니다.
Dan Rosenstark

매개 변수없이 loadFromNib를 수행 할 수 있습니다. + (id) loadFromNib {NSArray * bundle = [[NSBundle mainBundle] loadNibNamed : NSStringFromClass ([self class]) owner : self options : nil]; for (번들 내의 id 객체) {if ([object isKindOfClass : [self class]]) {return object; }} return nil; }
JVC

22

하나 이상의 Outlet Collection 인 사용자 정의보기 인스턴스를 관리 해야하는 모든 사람들을 위해 다음 과 같이 @Gonso, @AVeryDev 및 @Olie 답변을 병합하고 사용자 정의했습니다.

  1. 사용자 정의를 작성 하고 원하는 XIB 에서 루트의 MyView : UIView" 사용자 정의 클래스 " 로 설정하십시오 . UIView커스텀 클래스

  2. 필요한 모든 아울렛 을 작성하십시오 MyView(지금 지점 3 이후 IB가 UIViewController원하는대로 아울렛을 사용자 정의보기가 아닌 아울렛에 연결하도록 제안하기 때문에 지금 수행하십시오 ). 커스텀 클래스 아울렛

  3. 사용자 정의보기 XIB UIViewController " 파일 소유자 " 로 설정하십시오 . 여기에 이미지 설명을 입력하십시오

  4. 에서 UIViewController새로운 추가 UIViews의 각 인스턴스에 대해 MyView당신이 원하는, 그리고에 연결 UIViewController만드는 아울렛 컬렉션 : 이러한 뷰는 사용자 정의보기 인스턴스에 대해 "래퍼"뷰 역할을합니다; 여기에 이미지 설명을 입력하십시오

  5. 마지막에 viewDidLoad당신의 UIViewController추가 다음 줄 :

NSArray *bundleObjects;
MyView *currView;
NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count];
for (UIView *currWrapperView in myWrapperViews) {
    bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
    for (id object in bundleObjects) {
        if ([object isKindOfClass:[MyView class]]){
            currView = (MyView *)object;
            break;
        }
    }

    [currView.myLabel setText:@"myText"];
    [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal];
    //...

    [currWrapperView addSubview:currView];
    [myViews addObject:currView];
}
//self.myViews = myViews; if need to access them later..

안녕하세요 ... u 프로그래밍 방식 으로이 작업을 수행하는 방법을 알고 있습니다. xib 파일을 전혀 사용하지 않습니까?
X- 코더

19

재사용 할 사용자 정의 UIView를 인스턴스화하기 위해 UINib를 사용합니다.

UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];

이 경우 필요한 파일은 MyCustomView.xib , MyCustomViewClass.hMyCustomViewClass.m 입니다 [UINib instantiateWithOwner]. 배열 을 반환하는 참고 이므로 재사용하려는 UIView를 반영하는 요소를 사용해야합니다. 이 경우 첫 번째 요소입니다.


1
"rankingNib"대신 2 행에서 "customNib"를 의미하지 않습니까
Danny

11

UIViewInterface Builder에서 뷰 컨트롤러의 클래스를 하위 클래스로 설정해서는 안됩니다 . 그것은 가장 확실하게 문제의 일부입니다. UIViewController하위 클래스 또는 사용자 정의 클래스 로 남겨 두십시오 .

XIB에서 유일한보기를로드에 관해서는, 나는 당신이 있다는 가정하에 있었다 있었다 (이 확장되지 않는 경우에도 뷰 컨트롤러의 일종 가지고 UIViewController인터페이스에서 파일의 소유자로 설정을 사용자의 요구에 너무 무겁 수 있습니다) 인터페이스를 정의하기 위해 사용하려는 경우 빌더. 나는 이것을 확인하기 위해 약간의 연구를했다. 그렇지 않으면의 인터페이스 요소에 액세스 할 UIView수있는 방법이 없거나 이벤트에 의해 코드의 고유 한 메소드를 트리거 할 수있는 방법이 없기 때문입니다.

UIViewController뷰의 파일 소유자로 a를 사용하면 뷰 initWithNibName:bundle:를로드하고 뷰 컨트롤러 객체를 다시 가져올 수 있습니다. IB view에서 xib의 인터페이스가있는 콘센트를보기로 설정하십시오. 파일 소유자로 다른 유형의 객체를 사용하는 경우 NSBundleloadNibNamed:owner:options:메소드를 사용하여 펜촉을로드하고 파일 소유자의 인스턴스를 메소드에 전달해야합니다. IB에서 정의한 콘센트에 따라 모든 속성이 올바르게 설정됩니다.


Apress 책 "iPhone 개발 시작하기 : iPhone SDK 탐색"을 읽고 9 장 : 탐색 컨트롤러 및 테이블보기에서이 방법에 대해 설명했습니다. 너무 복잡해 보이지는 않았다.
Lyndsey Ferguson

그들이 어떻게 말했는지 궁금합니다. 현재 해당 책의 사본이 없습니다.
Marc W

10

loadNibNamed 대신 UIViewController의 initWithNibName을 사용할 수도 있습니다. 더 간단합니다.

UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release];  // release the VC

이제 MySubView.xib 및 MySubView.h / m을 작성하면됩니다. MySubView.xib에서 File 's Owner 클래스를 UIViewController로 설정하고 클래스를 MySubView로 설정하십시오.

subview부모 xib 파일을 사용하여 크기와 위치를 지정할 수 있습니다 .


8

이것은 훌륭한 질문 (+1)이며 대답은 거의 도움이되었습니다.) 죄송합니다. 그러나 Gonso와 AVeryDev는 좋은 힌트를 주었지만이 문제를 해결하는 데 시간이 많이 걸렸습니다. 이 답변이 다른 사람들에게 도움이되기를 바랍니다.

MyVC 이 모든 것을 담는 뷰 컨트롤러입니다.

MySubview xib에서로드하려는 뷰입니다.

  • MyVC.xib MySubView에서 올바른 크기 및 모양이며 원하는 위치에있는 유형의보기를 작성 하십시오.
  • MyVC.h에서

    IBOutlet MySubview *mySubView
    // ...
    @property (nonatomic, retain) MySubview *mySubview;
  • MyVC.m에서 @synthesize mySubView;및에서 릴리스하는 것을 잊지 마십시오 dealloc.

  • MySubview.h에서 콘센트 / 속성이 필요합니다 UIView *view(불필요 할 수도 있지만 나를 위해 일했습니다.).
  • MySubview.xib에서
    • 파일 소유자 유형을로 설정 MySubview하고보기 속성을보기에 연결하십시오.
    • 모든 비트를 배치하고 IBOutlet원하는대로에 연결
  • MyVC.m으로 돌아가서

    NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL];
    MySubview *msView = [xibviews objectAtIndex: 0];
    msView.frame = mySubview.frame;
    UIView *oldView = mySubview;
    // Too simple: [self.view insertSubview: msView aboveSubview: mySubview];
    [[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting
    self.mySubview = msView;
    [oldCBView removeFromSuperview];

나를위한 까다로운 점은 다른 답변의 힌트가 xib에서 내 견해를로드했지만 MyVC의 견해를 대체하지는 않았다는 것입니다 (duh!)-나는 그것을 스스로 바꿔야했습니다.

또한 mySubview메소드에 액세스 view하려면 .xib 파일 의 특성을로 설정해야합니다 MySubview. 그렇지 않으면 평범한 것으로 돌아옵니다 UIView.

mySubview자체 xib에서 직접 로드 할 수 있는 방법이 있다면 그것은 바위 일 것입니다. 그러나 이것이 내가 필요한 곳에 도착했습니다.


1
이 방법을 사용했습니다. 감사합니다. 중첩 된 뷰를 지원할 수 있도록 한 가지 변경 사항은 [ self.view insertSubview : msView aboveSubview : mySubview]; [ mySubview.superview insertSubview : msView aboveSubview : mySubview];
Jason

8

이것은 더 쉬워야 할 것입니다. 결국 선택기를 확장 UIViewController하고 추가했습니다 loadNib:inPlaceholder:. 이제 말할 수 있어요

self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];

범주에 대한 코드는 다음과 같습니다 (Gonso에서 설명한 것과 동일한 rigamarole 수행).

@interface UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;

@end

@implementation UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName
{
  NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; 
  for (id view in xib) { // have to iterate; index varies
    if ([view isKindOfClass:[UIView class]]) return view;
  }
  return nil;
}

- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
  UIView *nibView = [self viewFromNib:nibName];
  [nibView setFrame:placeholder.frame];
  [self.view insertSubview:nibView aboveSubview:placeholder];
  [placeholder removeFromSuperview];
  return nibView;
}

@end

7

신속하게

실제로이 문제에 대한 나의 해결책은 CustonViewController의 viewDidLoad에 뷰를로드하는 것입니다.

myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView

loadView()메소드에 뷰를로드하지 마십시오 ! loadView 메소드는 사용자 정의 ViewController에 대한보기를로드하는 데 사용됩니다.


6

나도 비슷한 것을하고 싶었다. 이것은 내가 찾은 것이다 : (SDK 3.1.3)

버튼을 누르면 VC B를로드하는 뷰 컨트롤러 A (Nav 컨트롤러 자체 소유)가 있습니다.

AViewController.m에서

BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];

이제 VC B는 Bnib의 인터페이스를 가지고 있지만 버튼을 누르면 다른 펜촉과 별도의 UI가있는 '편집 모드'로 가고 싶지만 편집 모드에 대한 새로운 VC는 원하지 않습니다. 새 펜촉이 기존 B VC와 연결되기를 원합니다.

따라서 BViewController.m에서 (버튼 누름 방법)

NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];

그런 다음 다른 버튼에서 (편집 모드를 종료하려면)를 누릅니다.

[editView removeFromSuperview];

다시 원래의 브릭으로 돌아 왔습니다.

이것은 잘 작동하지만 내 EditMode.nib에는 UIView obj라는 최상위 수준 obj가 하나만 있습니다. 이 펜촉의 파일 소유자가 BViewController 또는 기본 NSObject로 설정되어 있는지 여부는 중요하지 않지만 파일 소유자의보기 콘센트가 아무것도 설정되어 있지 않은지 확인하십시오. 그렇다면 exc_bad_access 충돌이 발생하고 xcode는 반복적으로 호출되는 내부 UIView 메소드를 보여주는 6677 스택 프레임을로드합니다 ... 그래서 무한 루프처럼 보입니다. (그러나 View Outlet은 원래의 노브에 설정되어 있습니다)

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


줄 사이를 읽으면 나에게 도움이되었습니다.
petert

링크 의 정보에 따르면 이러한 방식으로 뷰 컨트롤러를 조작해서는 안됩니다.
T. Markle

6

내가 좋아하는 카테고리를 만들었습니다.

UIView+NibInitializer.h

#import <UIKit/UIKit.h>

@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end

UIView+NibInitializer.m

#import "UIView+NibInitializer.h"

@implementation UIView (NibInitializer)

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    if (!nibNameOrNil) {
        nibNameOrNil = NSStringFromClass([self class]);
    }
    NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil
                                                        owner:self
                                                      options:nil];
    for (id view in viewsInNib) {
        if ([view isKindOfClass:[self class]]) {
            self = view;
            break;
        }
    }
    return self;
}

@end

그런 다음 다음과 같이 호출하십시오.

MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];

펜촉의 이름이 클래스 이름 이외의 이름 인 경우 펜촉 이름을 사용하십시오.

추가 동작을 위해 하위 클래스에서이를 재정의하려면 다음과 같이 보일 수 있습니다.

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    self = [super initWithNibNamed:nibNameOrNil];
    if (self) {
        self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
    }
    return self;
}

5

설계 옵션이있는 Swift 사용자의 경우 :

  1. 커스텀 UIView서브 클래스 와 xib 파일을 생성합니다.이 클래스의 이름은 MemeView입니다. Meme View 클래스 내에서 @IBDesignable클래스 선언 전에 속성 으로 디자인 가능한 것으로 정의 해야합니다.
  2. UIViewIndetity Inspector 패널 의 사용자 정의 서브 클래스를 사용하여 xib에서 파일 소유자를 설정하는 Rember

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

  3. xib 파일에서 인터페이스를 만들고, 구속 조건을 만들고, 아울렛, 액션 등을 만들 수 있습니다. 여기에 이미지 설명을 입력하십시오

  4. 일단 초기화되면 xib를 열려면 사용자 정의 클래스에 몇 가지 메소드를 구현해야합니다.

    class XibbedView: UIView {

    weak var nibView: UIView!
    
    override convenience init(frame: CGRect) {
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        self.init(nibName: nibName)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    init(nibName: String) {
        super.init(frame: CGRectZero)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    func setUpConstraints() {
        ["V","H"].forEach { (quote) -> () in
            let format = String(format:"\(quote):|[nibView]|")
            addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView]))
        }
    }
    
    func loadNib(name: String) -> UIView {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: name, bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
    
        return view
    }

    }

  5. 사용자 정의 클래스에서 인터페이스 작성기에서 특성을 완전히 제어 할 수 있도록 몇 가지 특성을 정의 할 수도 있습니다.

    @IBDesignable class MemeView: XibbedView {

    @IBInspectable var memeImage: UIImage = UIImage() {
        didSet {
            imageView.image = memeImage
        }
    }
    @IBInspectable var textColor: UIColor = UIColor.whiteColor() {
        didSet {
            label.textColor = textColor
        }
    }
    @IBInspectable var text: String = "" {
        didSet {
            label.text = text
        }
    }
    @IBInspectable var roundedCorners: Bool = false {
        didSet {
            if roundedCorners {
                layer.cornerRadius = 20.0
                clipsToBounds = true
            }
            else {
                layer.cornerRadius = 0.0
                clipsToBounds = false
            }
        }
    }
    
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var imageView: UIImageView!

}

몇 가지 예 :
여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

스토리 보드 또는 다른 xib에 표시되는 동안 더 많은 정보를 추가 해야하는 경우 구현 할 수 있도록하기 prepareForInterfaceBuilder()위해이 메소드는 인터페이스 빌더에서 파일을 여는 동안에 만 실행됩니다. 내가 작성한 모든 것을했지만 아무것도 작동하지 않으면 구현에 중단 점을 추가하여 sigle보기를 디버깅하는 방법입니다. 뷰 계층 구조는 다음과 같습니다. 여기에 이미지 설명을 입력하십시오
히 어치보기

여기 에서 전체 샘플을 다운로드 할 수 있기 를 바랍니다.


전체 기사는 내 블로그 cloudintouch.it/2016/04/21/designable-xib-in-xcode-hell-yeah 에서 볼 수 있지만 이미 관련 부분을 게시했습니다.
Andrea

let nib = loadNib(nibName)이것은``addSubview (nib)``를 호출하면 XibbedView 인스턴스입니다. 그러면 XibbedView 인스턴스를 다른 XibbedView 인스턴스에 추가합니까?
William Hu

나는이 점에 동의한다. "Debug View Hierarchy"에서 보려고하면 XibbedView에 XibbedView가 포함 된 것 같습니다. 당신은 그것을 볼 수 있습니다.
William Hu

@WilliamHu 제공 예제를 wwload하면 MemeView.xib가 하위 뷰가 많은 UIView 인스턴스이고 파일 홀더는 XibbedView의 하위 클래스 인 MemeView입니다. 따라서 XibbedView의 유일한 인스턴스는 MemeView입니다. 메인 뷰는 단지보기 인 xib 파일을로드합니다
Andrea

그럼요 테스트하겠습니다. 감사합니다!
William Hu

4

나는 발견 이 블로그 게시물을 매우 계몽하는 아론 힐레 가스 (저자, 강사, 코코아 닌자)에 의해. 지정된 이니셜 라이저를 통해 NIB 파일을로드하기 위해 수정 된 접근 방식을 채택하지 않더라도 적어도 진행중인 프로세스를 더 잘 이해하게 될 것입니다. 최근에이 방법을 사용하여 큰 성공을 거두었습니다!


연결이 끊어졌습니다. 나는 그것이 그것이 현재 bignerdranch.com/blog/
joelliusp

3

이전 답변에서는 iPhone SDK 2.0과 2.1 사이에서 발생한 NIB (XIB) 구조의 변경 사항을 고려하지 않았습니다. 사용자 컨텐츠는 이제 1 대신 인덱스 0에서 시작합니다.

모든 버전 2.1 이상에 유효한 2.1 매크로를 사용할 수 있습니다 (IPHONE 앞의 두 개의 밑줄).

 // Cited from previous example
 NSArray* nibViews =  [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil];
 int startIndex;
 #ifdef __IPHONE_2_1
 startIndex = 0;
 #else
 startIndex = 1;
 #endif
 QPickOneView* myView = [ nibViews objectAtIndex: startIndex];
 myView.question = question;

우리는 대부분의 응용 프로그램에 이와 유사한 기술을 사용합니다.

바니


2
바니 : "이전 답변"은 스택 오버플로에서 의미가 없습니다. "Fred 's answer"또는 "Barney 's answer"또는 "Olie 's answer"를 참조하는 것이 좋습니다.
Olie

3

프로그래밍 방식으로 XIB 파일에서 뷰를로드하는 것과 같은 일을해야 할 이유가 있었지만 서브 클래스의 서브 클래스의 컨텍스트에서 완전히 수행해야했습니다 UIView(즉, 뷰 컨트롤러를 사용하지 않고). 이를 위해이 유틸리티 메소드를 작성했습니다.

+ (id) initWithNibName:(NSString *)nibName withSelf:(id)myself {

    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName
                                                    owner:myself options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:[myself class]]) {
            return object;
        }
    }  

    return nil;
} 

그런 다음 하위 클래스의 initWithFrame메소드 에서 다음과 같이 호출합니다 .

- (id)initWithFrame:(CGRect)frame {

    self = [Utilities initWithNibName:@"XIB1" withSelf:self];
    if (self) {
        // Initialization code.
    }
    return self;
}

일반적인 관심을 위해 게시 됨; 이 방법으로 문제를 발견 한 사람이 있으면 알려주십시오.


코드를 구현했지만 초기화 범위를 벗어납니다. 유틸리티 클래스에서 정적 메소드를 작성했습니다. MyView (UIView에서 서브 클래 싱됨)라는 h / m 파일과 같은 이름의 xib를 만들었습니다. IB에서는 xib 파일 소유자와보기를 "MyView"로 설정했습니다. 디버거에서는 범위를 벗어납니다. IB에 뭔가 빠졌습니까?
TigerCoding 2019

내 새로운 SO 질문을 살펴보십시오 : stackoverflow.com/questions/9224857/…
TigerCoding

self = [유틸리티 initWithNibName : @ "XIB1"withSelf : self]; 아직 설정되지 않은 상태에서 자기 자신을 전달하고 있습니다. 해당 코드가 다음과 동일하지 않습니까? self = [Utilities initWithNibName : @ "XIB1"withSelf : nil]; ?
Ken Van Hoeylandt 2012

@Javy : 여기의 코드 샘플이 ARC와 함께 작동하지 않을 수 있습니다-ARC가 활성화 된 상태에서 테스트하지 않았습니다.
MusiGenesis

3

다음은 Swift에서 수행하는 방법입니다 (현재 XCode 7 베타 5에서 Swift 2.0 작성).

당신에서 UIView당신이 (내 서브 클래스가 RecordingFooterView라고합니다)이 같은 방법을 만들 인터페이스 빌더에서 "사용자 정의 클래스"로 설정하는 것이 서브 클래스 :

class func loadFromNib() -> RecordingFooterView? {
    let nib = UINib(nibName: "RecordingFooterView", bundle: nil)
    let nibObjects = nib.instantiateWithOwner(nil, options: nil)
    if nibObjects.count > 0 {
        let topObject = nibObjects[0]
        return topObject as? RecordingFooterView
    }
    return nil
}

그런 다음 다음과 같이 호출하면됩니다.

let recordingFooterView = RecordingFooterView.loadFromNib()


2

이 질문의 근본 인 독립형 XIB를 작성하는 방법에 대한 답변은 없습니다. Xcode 4"새 XIB 파일 작성"옵션 이 없습니다 .

이것을하기 위해

1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format

이것은 단순 해 보일 수 있지만 단어 "XIB"가 어디에도 나타나지 않기 때문에 몇 분만 둘러 볼 수 있습니다.


1

아리따움

6) 로드 된 뷰를 뷰 컨트롤러의 뷰에 연결하려면 :

[self.view addSubview:myViewFromNib];

아마도 메모리 누수를 피하기 위해 뷰에서 제거해야 할 것입니다.

명확히하기 위해 : 뷰 컨트롤러에는 여러 개의 IBOutlet이 있으며 그중 일부는 원래 펜촉 파일의 항목에 연결되고 일부는로드 된 펜촉의 항목에 연결됩니다. 두 펜촉에는 동일한 소유자 클래스가 있습니다. 로드 된 뷰가 원래 뷰를 오버레이합니다.

힌트 :로드 된 펜촉에있는 메인 뷰의 불투명도를 0으로 설정하면 원래 펜촉의 항목이 가려지지 않습니다.


조심해야하지만 뷰를 하위 뷰로 추가하면 뷰가 이전 상위에서 자동으로 제거되므로 누출되지 않아야합니다.
averydev

1

많은 시간을 보낸 후 다음 해결책을 찾아 냈습니다. 다음 단계에 따라 custom을 만드십시오 UIView.

1) UIView 에서 상속받은 myCustomView 클래스를 만듭니다 .
여기에 이미지 설명을 입력하십시오

2) 이름이 myCustomView 인 .xib 를 작성하십시오 .
여기에 이미지 설명을 입력하십시오

3) .xib 파일 내 에서 UIView 클래스를 변경하고 거기에 myCustomView 클래스를 지정하십시오 .
여기에 이미지 설명을 입력하십시오

4) IBOutlets 생성
여기에 이미지 설명을 입력하십시오

5) myCustomView * customView에 .xib를로드 하십시오 . 다음 샘플 코드를 사용하십시오.

myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];

참고 : 여전히 문제가있는 사람들은 의견을 말할 수 있으므로 샘플 프로젝트의 myCustomView 링크를 제공 할 것입니다


1

최단 버전 :

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];

0

뷰와 동일한 뷰로 xib의 이름을 지정하는 규칙이 있습니다. 뷰 컨트롤러와 동일합니다. 그런 다음 코드로 클래스 이름을 작성할 필요가 없습니다. 같은 이름의 펜촉 파일에서 UIView를로드합니다.

MyView라는 클래스의 예

  • Interface Builder에서 MyView.xib라는 펜촉 파일 만들기
  • Interface Builder에서 UIView를 추가하십시오. 클래스를 MyView로 설정하십시오. 마음에 맞게 사용자 정의하고 MyView의 인스턴스 변수를 나중에 액세스하려는 하위보기에 연결하십시오.
  • 코드에서 다음과 같이 새 MyView를 작성하십시오.

    MyView * myView = [MyView nib_viewFromNibWithOwner : 소유자];

이에 대한 범주는 다음과 같습니다.

@implementation UIView (nib)

+ (id) nib_viewFromNib {
    return [self nib_viewFromNibWithOwner:nil];
}

+ (id) nib_viewFromNibWithOwner:(id)owner {
    NSString *className = NSStringFromClass([self class]);
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil];
    UIView *view = nil;
    for(UIView *v in nib) {
        if ([v isKindOfClass:[self class]]) {
            view = v;
            break;
        }
    }
    assert(view != nil && "View for class not found in nib file");
    [view nib_viewDidLoad];
    return view;
}

// override this to do custom setup
-(void)nib_viewDidLoad {

}

그런 다음 사용중인 컨트롤러의 작업과 버튼을 연결하고 사용자 정의보기 하위 클래스의 콘센트를 사용하여 레이블에 항목을 설정했습니다.


0

Swift 4의 펜촉 / xib에서 뷰를 프로그래밍 방식으로로드하려면 :

// Load a view from a Nib given a placeholder view subclass
//      Placeholder is an instance of the view to load.  Placeholder is discarded.
//      If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {

    let nib = loadNib(givenNibName, placeholder: placeholderView)
    return instantiateView(fromNib: nib, placeholder: placeholderView)
}

// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
    //1. Load and unarchive nib file
    let nibName = givenNibName ?? String(describing: type(of: placeholderView))

    let nib = UINib(nibName: nibName, bundle: Bundle.main)
    return nib
}

// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
    //1. Get top level objects
    let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)

    //2. Have at least one top level object
    guard let firstObject = topLevelObjects.first else {
        fatalError("\(#function): no top level objects in nib")
    }

    //3. Return instantiated view, placeholderView is not used
    let instantiatedView = firstObject as! T
    return instantiatedView
}

-1

나는 이것을 위해 UIView에 카테고리를 추가했다.

 #import "UIViewNibLoading.h"

 @implementation UIView (UIViewNibLoading)

 + (id) loadNibNamed:(NSString *) nibName {
    return [UIView loadNibNamed:nibName fromBundle:[NSBundle mainBundle] retainingObjectWithTag:1];
 }

 + (id) loadNibNamed:(NSString *) nibName fromBundle:(NSBundle *) bundle retainingObjectWithTag:(NSUInteger) tag {
    NSArray * nib = [bundle loadNibNamed:nibName owner:nil options:nil];
    if(!nib) return nil;
    UIView * target = nil;
    for(UIView * view in nib) {
        if(view.tag == tag) {
            target = [view retain];
            break;
        }
    }
    if(target && [target respondsToSelector:@selector(viewDidLoad)]) {
        [target performSelector:@selector(viewDidLoad)];
    }
    return [target autorelease];
 }

 @end

여기에 설명 : ios & mac에서 viewcontroller가 덜로드됩니다.


흠 태그와 함께 무엇입니까?
n13

유지할 최상위 개체를 알기 위해 당시에는 필요했지만 지금은 ARC를 준수하기 위해 보유를 꺼낼 수 있습니다.
gngrwzrd
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.