UIView 하위 클래스에 대한 Nib을로드하는 올바른 방법


98

이 질문이 이전에 요청 된 것을 알고 있지만 답변이 모순되고 혼란 스럽기 때문에 나를 화나게하지 마십시오.

UIView내 앱 전체 에서 재사용 가능한 하위 클래스 를 갖고 싶습니다 . nib 파일을 사용하여 인터페이스를 설명하고 싶습니다.

이제 활동 표시기가있는 로딩 표시기보기라고 가정 해 보겠습니다. 이 뷰를 인스턴스화하고 뷰 컨트롤러의 뷰에 애니메이션을 적용하는 이벤트를 원합니다. 뷰의 인터페이스를 프로그래밍 방식으로 설명 할 수 있고, 요소를 프로그래밍 방식으로 생성하고 프레임을 init 메서드 등으로 설정할 수 있습니다.

그래도 펜촉을 사용하여 어떻게 할 수 있습니까? 프레임을 설정하지 않고도 인터페이스 빌더에 제공된 크기를 유지합니다.

나는 이렇게 할 수 있었지만 그것이 잘못되었다고 확신합니다 (선택 기가있는보기 일뿐입니다).

 - (id)initWithDataSource:(NSDictionary *)dataSource {
        self = [super init];
        if (self){
            self = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@", [self class]] owner:self options:nil] objectAtIndex:0];
            self.pickerViewData = dataSource;
            [self configurePickerView];
        }
        return self;
    }

그러나 나는 self를 덮어 쓰고 있으며 그것을 인스턴스화 할 때 :

FSASelectView *selectView = [[FSASelectView alloc] initWithDataSource:selectViewDictionary];
    selectView.delegate = self;

    selectView.frame = CGRectMake(0, self.view.bottom + 50, [FSASelectView width], [FSASelectView height]);

IB에서 프레임을 선택하지 않고 수동으로 프레임을 설정해야합니다.

편집 :보기 컨트롤러에서이 사용자 지정보기를 만들고보기의 요소를 제어 할 수있는 액세스 권한이 있습니다. 새로운 뷰 컨트롤러를 원하지 않습니다.

감사

편집 : 이것이 모범 사례인지 모르겠지만 그렇지 않다고 확신하지만 이것이 내가 한 방법입니다.

FSASelectView *selectView = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@",[FSASelectView class]] owner:self options:nil] objectAtIndex:0];
    selectView.delegate = self;
    [selectView configurePickerViewWithData:ds];
    selectView.frame = CGRectMake(0, self.view.bottom + 50, selectView.width, selectView.height);
    selectView.alpha = 0.9;
    [self.view addSubview:selectView];
    [UIView animateWithDuration: 0.25 delay: 0 options:UIViewAnimationOptionAllowUserInteraction |UIViewAnimationOptionCurveEaseInOut animations:^{
                            selectView.frame = CGRectMake(0, self.view.bottom - selectView.height, selectView.width, selectView.height);
                            selectView.alpha = 1;
                        } completion:^(BOOL finished) {
                        }];

올바른 연습이 여전히 필요함

뷰 컨트롤러를 사용하고 nib 이름으로 초기화해야합니까? 코드의 일부 UIView 초기화 방법에서 펜촉을 설정해야합니까? 아니면 내가 한 일이 괜찮습니까?


그러나 첫 번째 코드 줄은 원래 질문에서 사용한 것과 거의 동일하지 initWithDataSource않습니까? 어쨌든 소유자를 '무효'로 주더라도 작동합니다.
Rakesh 2013 년

요즘 99.99999 %의 사례에서 컨테이너 뷰를 사용하고 있다는 점은 주목할 가치가 있습니다. 컨테이너 뷰 는 현재 모든 곳에서 항상 iOS에서 사용됩니다. 방법 문서
Fattie 2011

보조 노트 당신은 대체 할 수있는 [있는 NSString stringWithFormat을 : @ "%를 @", [FSASelectView 클래스] NSStringFromClass ([FSASelectView 클래스])로
naomimichiko

답변:


132
MyViewClass *myViewObject = [[[NSBundle mainBundle] loadNibNamed:@"MyViewClassNib" owner:self options:nil] objectAtIndex:0]

나는 이것을 사용하여 내가 가지고있는 재사용 가능한 사용자 정의보기를 초기화하고 있습니다.


끝에 "firstObject" 를 사용할 수 있습니다 . 약간 더 깔끔합니다. "firstObject"는 NSArray 및 NSMutableArray를위한 편리한 메소드입니다.

다음은 테이블 헤더로 사용하기 위해 xib를로드하는 일반적인 예입니다. YourClass.m 파일에서

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    return [[NSBundle mainBundle] loadNibNamed:@"TopArea" owner:self options:nil].firstObject;
}

일반적으로 TopArea.xib에서 파일 소유자를 클릭 하고 파일 소유자를 YourClass로 설정합니다 . 그런 다음 실제로 YourClass.h에 IBOutlet 속성이 있습니다. 에서는 TopArea.xib컨트롤을 해당 콘센트로 끌 수 있습니다.

점에서 잊지 마세요 TopArea.xib, 당신은 할 수 있습니다 보기 자체를 클릭 하면 필요한 경우, 그것의 제어 할 수 있도록, 일부 콘센트에 드래그. (매우 가치있는 팁은 테이블 셀 행에 대해이 작업을 수행 할 때 반드시 수행해야한다는 것입니다. 뷰 자체를 코드의 관련 속성에 연결해야합니다.)


더 initWithNibName가 없습니다 : UIView의에 대한 방법은, 그냥있는 UIViewController위한거야
meronix

그것은 뷰 컨트롤러의 경우 UIView를 사용하고 그것을 소유하기 위해 생성하는 컨트롤러를 갖고 싶습니다.
Adam Waite 2013 년

죄송합니다. 서둘러 잘못된 코드를 복사했습니다. 답변을 업데이트했습니다. 확인해주세요.
Premsuraj 2013 년

나는 이것을 올바른 것으로 표시 할 것입니다. 모범 사례인지 모르겠지만 작동합니다.
Adam Waite 2013 년

@Joe Blow 범프에 대해 죄송합니다. 왜 이렇게해야할까요? "TopArea.xib에서 View 자체를 클릭하고 일부 콘센트로 드래그해야 할 수 있으므로 제어 할 수 있습니다. , 필요하다면." 대신 myViewObject를 사용하여 UIView 자체이므로 기본 뷰에 액세스 할 수 있습니까? 부동산이 필요한 이유는 무엇입니까?
user1686342 2011

39

귀하 CustomView및 귀하 와의 xib독립성 을 유지 File's Owner하려면 다음 단계를 따르십시오.

  • File's Owner필드를 비워 둡니다 .
  • 에서 실제보기를 클릭 xib당신의 파일 CustomView과 설정 Custom Class으로 CustomView(사용자 정의 뷰 클래스의 이름)
  • 추가 IBOutlet.h 사용자 정의보기 파일.
  • 에서 .xib사용자 정의보기의 파일보기를 클릭하고 들어가 Connection Inspector. 여기에 .h파일에 정의한 모든 IBOutlets가 있습니다.
  • 그들 각각의 관점으로 그들을 연결하십시오.

.m당신의 파일 CustomView클래스의 오버라이드 (override) init다음과 같은 방법을

-(CustomView *) init{
    CustomView *result = nil;
    NSArray* elements = [[NSBundle mainBundle] loadNibNamed: NSStringFromClass([self class]) owner:self options: nil];
    for (id anObject in elements)
    {
        if ([anObject isKindOfClass:[self class]])
        {
            result = anObject;
            break;
        }
    }
    return result;
}

이제을로드 CustomView하려면 다음 코드 줄을 사용하십시오. [[CustomView alloc] init];


1
iOS 9에서 이것은 실제로 나를 위해 작동하는이 페이지에 나열된 유일한 솔루션입니다 (다른 솔루션으로 인해 충돌이 발생합니다).
lifjoy

이 접근 방식은를 호출하지 않으므로 기존 XIB에서 사용자 정의보기를로드하는 것과 호환되지 않습니다 -init. 대신 initWithFrame:-awakeFromNib. -init메소드 에서와 같이 뷰를로드하기 위해 동일한 코드를 작성하면 무한 루프에 들어갑니다.
Dustt

25

다음 단계를 따르십시오.

  1. MyView .h / .m 유형의 클래스를 만듭니다 UIView.
  2. 같은 이름의 xib를 만듭니다 MyView.xib.
  3. 이제 xib UIViewController에서 파일 소유자 클래스를 from NSObject으로 변경하십시오 . 아래 이미지를 참조하십시오 여기에 이미지 설명 입력
  4. 파일 소유자보기를보기에 연결합니다. 아래 이미지를 참조하십시오 여기에 이미지 설명 입력

  5. 보기 클래스를 다음으로 변경하십시오. MyView . 3과 같습니다.

  6. 장소 컨트롤은 IBOutlets를 만듭니다.

보기를로드하는 코드는 다음과 같습니다.

UIViewController *controller=[[UIViewController alloc] initWithNibName:@"MyView" bundle:nil];
MyView* view=(MyView*)controller.view;
[self.view addSubview:myview];

도움이되기를 바랍니다.

설명 :

UIViewControllerxib를로드하는 데 사용되며 UIViewController실제로 MyView있는 뷰 는 MyView xib에서 할당 한 것입니다.

데모 여기서 데모를 만들었습니다.


왜 사람들이 반대표를 던지는 거죠? 나는 아직 시도하지 않았지만 이것은 내가 원하는 것을하지 않을 것 같습니다.
Adam Waite 2013 년

나는 반대 투표를했지만 그로 인해 귀하의 답변을 잘못 읽었습니다 (UIView 하위 클래스를 파일 소유자로 할당했다고 생각했습니다). 미안합니다.
Rakesh 2013 년

2
xib 파일에서 파일 소유자를 무시하십시오. 그런 다음 MyView를 수행하여 UIViewController를 만들지 않아도됩니다. * view = [[UINib instantiateWithOwner : nil options : nil] lastObject];
LightningStryk

1
@LightningStryk 물론 그렇습니다.하지만 그것을하는 또 다른 방법입니다.
iphonic

1
재사용 가능한 UIView 하위 클래스를 만드는 것이지 그 목적으로 UIViewController를 사용하는 것이 아닙니다.
arielyz

11

여기에서 2 년 정도 후에 내 질문에 대답하지만 ...

프로토콜 확장을 사용하므로 모든 클래스에 대한 추가 코드없이 수행 할 수 있습니다.

/*

Prerequisites
-------------
- In IB set the view's class to the type hook up any IBOutlets
- In IB ensure the file's owner is blank

*/

public protocol CreatedFromNib {
    static func createFromNib() -> Self?
    static func nibName() -> String?
}

extension UIView: CreatedFromNib { }

public extension CreatedFromNib where Self: UIView {

    public static func createFromNib() -> Self? {
        guard let nibName = nibName() else { return nil }
        guard let view = NSBundle.mainBundle().loadNibNamed(nibName, owner: nil, options: nil).last as? Self else { return nil }
        return view
    }

    public static func nibName() -> String? {
        guard let n = NSStringFromClass(Self.self).componentsSeparatedByString(".").last else { return nil }
        return n
    }
}

// Usage:
let myView = MyView().createFromNib()

8

Swift에서 :

예를 들어, 사용자 정의 클래스의 이름은 InfoView

처음에는 다음 InfoView.xibInfoView.swift같이 파일을 생성합니다 .

import Foundation
import UIKit

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

그런 다음 설정 File's Owner하기 UIViewController이 좋아 :

여기에 이미지 설명 입력

당신의 이름 바꾸기 View에를 InfoView:

여기에 이미지 설명 입력

필드 를 마우스 오른쪽 버튼으로 클릭하고 다음 File's Owner과 연결 view합니다 InfoView.

여기에 이미지 설명 입력

클래스 이름이 InfoView다음과 같은지 확인하십시오 .

여기에 이미지 설명 입력

그런 다음 문제없이 사용자 정의 클래스의 버튼에 작업을 추가 할 수 있습니다.

여기에 이미지 설명 입력

그리고이 사용자 정의 클래스의 사용 MainViewController:

func someMethod() {
    var v = InfoView.instanceFromNib()
    v.frame = self.view.bounds
    self.view.addSubview(v)
}

2

보기 컨트롤러를 사용하여 xib를 초기화하고 viewController.view를 사용할 수 있습니다. 또는 당신이했던 방식대로하십시오. UIView컨트롤러로 서브 클래스 만 만드는 것은 UIView나쁜 생각입니다.

사용자 정의보기에 출력이없는 경우 UIViewController클래스를 직접 사용 하여 초기화 할 수 있습니다.

업데이트 : 귀하의 경우 :

UIViewController *genericViewCon = [[UIViewController alloc] initWithNibName:@"CustomView"];
//Assuming you have a reference for the activity indicator in your custom view class
CustomView *myView = (CustomView *)genericViewCon.view;
[parentView addSubview:myView];
//And when necessary
[myView.activityIndicator startAnimating]; //or stop

그렇지 않으면 사용자 정의 UIViewController를 만들어야합니다 (콘센트가 제대로 연결되도록 파일의 소유자로 만들기 위해).

YourCustomController *yCustCon = [[YourCustomController alloc] initWithNibName:@"YourXibName"].

뷰를 추가하고 싶은 곳이면 어디든 사용할 수 있습니다.

[parentView addSubview:yCustCon.view];

그러나 xib를로드하는 동안 다른 뷰 컨트롤러 (이미 다른 뷰에 사용 중임)를 소유자로 전달하는 것은 컨트롤러의 뷰 속성이 변경되고 원래 뷰에 액세스하려고 할 때 좋지 않기 때문에 좋은 생각이 아닙니다. 그것에 대한 참조가 있습니다.

편집 : 파일의 소유자를 동일한 주 UIViewController클래스 로 사용하여 새 xib를 설정 하고보기 속성을 새 xib보기에 연결 한 경우이 문제가 발생합니다 .

즉;

  • YourMainViewController-관리-mainView
  • CustomView-필요할 때 xib에서로드해야합니다.

아래 코드는 내부 뷰를 작성하면 나중에 혼란을 일으킬 것입니다 YourMainViewController. 그 때문입니다 self.view당신의있는 CustomView를 참조 할이 시점에서

-(void)viewDidLoad:(){
  UIView *childView= [[[NSBundle mainBundle] loadNibNamed:@"YourXibName" owner:self options:nil] objectAtIndex:0];
}

좋아, 기본적으로 모범 사례는 모든 UIView에 연결된 컨트롤러가 있어야한다는 것입니까?
Adam Waite 2013 년

반드시 그런 것은 아닙니다. 그러나 별도의 xib에서 뷰를로드 할 때 문제가없는 방법입니다. IOS5 이전 애플 문서에서는 하나의 뷰 컨트롤러를 사용하여 둘 이상의 장면 (xib)을 제어해서는 안되며 그 반대의 경우도 마찬가지라고 말합니다.
Rakesh 2013 년

XIB 실제로 경고 뷰의 크기입니다
아담 웨이트

개인적으로 새 컨트롤러를 만들어야하는지 여부는 코드 / xib의 복잡성에 따라 달라집니다. 성공적으로 발생한 문제를 처리 할 수 ​​있고 향후 수정 사항을 고려하지 않는다면 문제가되지 않습니다. 그러나 별도의 뷰 컨트롤러를 생성하면 많은 문제를 해결할 수 있습니다. 그리고 귀하의 경우에는 활동 표시기 일뿐이므로 UIViewController (답변에서 언급했듯이) 개체를 쉽게 사용할 수 있습니다. 그에 따라 내 답변을 업데이트하겠습니다.
Rakesh 2013 년

1
따라서 프로그래밍 방식이 아닌 인터페이스 빌더에서 인터페이스를 그릴 수 있습니다. 프로그래밍 방식으로 수행하는 것은 문제가되지 않습니다. UIView를 하위 클래스로 지정하는 방법을 알고 싶을뿐입니다. 이것은 어렵지 않습니다.
Adam Waite 2013 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.