[ios] 사용자 지정 iOS 뷰 클래스를 만들고 여러 복사본을 인스턴스화하려면 어떻게합니까 (IB에서)?

현재 기본적으로 모두 동일한 타이머가 여러 개있는 앱을 만들고 있습니다.

타이머와 레이아웃 / 애니메이션에 대한 모든 코드를 사용하는 사용자 지정 클래스를 만들고 싶습니다. 그래서 서로 독립적으로 작동하는 5 개의 동일한 타이머를 가질 수 있습니다.

IB (xcode 4.2)를 사용하여 레이아웃을 만들었으며 타이머에 대한 모든 코드는 현재 viewcontroller 클래스에만 있습니다.

모든 것을 사용자 정의 클래스로 캡슐화 한 다음 뷰 컨트롤러에 추가하는 방법에 대해 내 두뇌를 감싸는 데 어려움을 겪고 있습니다. 어떤 도움을 주시면 감사하겠습니다.



답변

그럼 개념적으로 대답, 당신의 타이머는 가능성의 서브 클래스해야한다 UIView대신 NSObject.

IB에서 타이머의 인스턴스를 인스턴스화하려면 UIView뷰 컨트롤러의 뷰에 끌어다 놓고 타이머의 클래스 이름으로 클래스를 설정하면됩니다.

여기에 이미지 설명 입력

#import뷰 컨트롤러의 타이머 클래스를 기억하십시오 .

편집 : IB 설계 용 (코드 인스턴스화는 개정 내역 참조)

저는 스토리 보드에 전혀 익숙하지 않지만 .xib스토리 보드 버전을 사용하는 것과 거의 동일한 파일을 사용하여 IB에서 인터페이스를 구성 할 수 있다는 것을 알고 있습니다 . 뷰 전체를 기존 인터페이스에서 .xib파일 로 복사하여 붙여 넣을 수도 있습니다 .

이를 테스트하기 위해 .xib“MyCustomTimerView.xib”라는 새 빈을 만들었습니다 . 그런 다음 뷰를 추가하고 여기에 레이블과 두 개의 버튼을 추가했습니다. 그렇게 :

여기에 이미지 설명 입력

UIView“MyCustomTimer”라는 새로운 목표 C 클래스 하위 클래스를 만들었습니다 . 내에서 .xib나는 내 설정 파일의 소유자 로 클래스를 MyCustomTimer . 이제 다른 뷰 / 컨트롤러처럼 액션과 아웃렛을 자유롭게 연결할 수 있습니다. 결과 .h파일은 다음과 같습니다.

@interface MyCustomTimer : UIView
@property (strong, nonatomic) IBOutlet UILabel *displayLabel;
@property (strong, nonatomic) IBOutlet UIButton *startButton;
@property (strong, nonatomic) IBOutlet UIButton *stopButton;
- (IBAction)startButtonPush:(id)sender;
- (IBAction)stopButtonPush:(id)sender;
@end

점프해야 할 유일한 장애물은 .xibUIView하위 클래스 에서 이것을 얻는 것 입니다 . 를 사용하면 .xib필요한 설정 이 크게 줄어 듭니다. 그리고 스토리 보드를 사용하여 타이머를로드하기 -(id)initWithCoder:때문에 호출되는 유일한 초기화 프로그램은 우리가 알고 있습니다. 따라서 구현 파일은 다음과 같습니다.

#import "MyCustomTimer.h"
@implementation MyCustomTimer
@synthesize displayLabel;
@synthesize startButton;
@synthesize stopButton;
-(id)initWithCoder:(NSCoder *)aDecoder{
    if ((self = [super initWithCoder:aDecoder])){
        [self addSubview:
         [[[NSBundle mainBundle] loadNibNamed:@"MyCustomTimerView"
                                        owner:self
                                      options:nil] objectAtIndex:0]];
    }
    return self;
}
- (IBAction)startButtonPush:(id)sender {
    self.displayLabel.backgroundColor = [UIColor greenColor];
}
- (IBAction)stopButtonPush:(id)sender {
    self.displayLabel.backgroundColor = [UIColor redColor];
}
@end

명명 된 메서드 loadNibNamed:owner:options:는 소리처럼 들리는대로 정확히 수행합니다. Nib을로드하고 “File ‘s Owner”속성을 self로 설정합니다. 배열에서 첫 번째 객체를 추출하고 이것이 Nib의 루트 뷰입니다. 뷰를 하위 뷰로 추가하고 Voila는 화면에 표시합니다.

분명히 이것은 버튼을 눌렀을 때 레이블의 배경색을 변경하지만이 예제는 당신을 잘 이해할 것입니다.

주석에 따른 참고 사항 :

무한 재귀 문제가 발생한다면이 솔루션의 미묘한 트릭을 놓쳤을 것입니다. 당신이 생각하는 일을하는 것이 아닙니다. 스토리 보드에있는보기는 표시되지 않지만 대신 다른보기를 하위보기로로드합니다. 로드되는 뷰는 펜촉에 정의 된 뷰입니다. 펜촉의 “파일 소유자”는 보이지 않는보기입니다. 멋진 부분은이 보이지 않는 뷰가 펜촉에서 가져 오는 뷰에 대한 일종의 뷰 컨트롤러로 사용할 수있는 Objective-C 클래스라는 것입니다. 예를 들어 클래스 의 IBAction메서드 MyCustomTimer는 뷰보다 뷰 컨트롤러에서 더 많이 기대할 수있는 것입니다.

부수적으로, 일부는 이것이 깨 졌다고 주장 할 수 MVC있으며 나는 다소 동의합니다. 내 관점에서 볼 때 UITableViewCell때로는 일부 컨트롤러 여야 하는 custom과 더 밀접하게 관련 되어 있습니다.

이 답변은 매우 구체적인 솔루션을 제공하기위한 것이 었습니다. 스토리 보드에 배치 된 것과 동일한 뷰 에서 여러 번 인스턴스화 할 수있는 하나의 펜촉을 만듭니다 . 예를 들어,이 타이머 중 6 개를 iPad 화면에서 한 번에 모두 쉽게 상상할 수 있습니다. 응용 프로그램에서 여러 번 사용할 뷰 컨트롤러에 대한 뷰만 지정해야하는 경우 jyavenard에서이 질문에 대해 제공 한 솔루션 이 거의 확실히 더 나은 솔루션입니다.


답변

신속한 예

Xcode 10 및 Swift 4 용으로 업데이트 됨

다음은 기본적인 절차입니다. 저는 원래이 유튜브 비디오 시리즈 를 보면서 많은 것을 배웠습니다 . 나중에이 기사를 기반으로 답변을 업데이트했습니다 .

사용자 정의보기 파일 추가

다음 두 파일이 사용자 정의보기를 구성합니다.

  • 레이아웃을 포함 할 .xib 파일
  • UIView하위 클래스 로 .swift 파일

추가에 대한 세부 정보는 다음과 같습니다.

Xib 파일

프로젝트에 .xib 파일을 추가합니다 (파일> 새로 만들기> 파일 …> 사용자 인터페이스>보기) . 나는 나의 전화입니다 ReusableCustomView.xib.

사용자 지정보기에 포함 할 레이아웃을 만듭니다. 예를 들어 a UILabelUIButton. 나중에 설정 한 크기에 관계없이 자동으로 크기가 조정되도록 자동 레이아웃을 사용하는 것이 좋습니다. ( 시뮬레이션 된 메트릭을 조정할 수 있도록 Attributes inspector에서 xib 크기에 Freeform 을 사용 했지만 필수는 아닙니다.)

여기에 이미지 설명 입력

Swift 파일

프로젝트에 .swift 파일을 추가합니다 (File> New> File …> Source> Swift File) . 그것은의 하위 클래스 UIView이고 나는 내라고 부르고있다 ReusableCustomView.swift.

import UIKit
class ResuableCustomView: UIView {

}

Swift 파일을 소유자로 설정

.xib 파일로 돌아가서 문서 개요에서 “파일 소유자”를 클릭합니다. Identity Inspector에서 .swift 파일 의 이름을 커스텀 클래스 이름으로 씁니다 .

여기에 이미지 설명 입력

사용자 정의보기 코드 추가

ReusableCustomView.swift파일의 내용을 다음 코드로 바꿉니다 .

import UIKit

@IBDesignable
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)
        commonInit()
    }

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

    func commonInit() {
        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
    }
}

.xib 파일 이름의 철자가 올바른지 확인하십시오.

콘센트 및 작업 연결

xib 레이아웃의 레이블과 버튼에서 신속한 사용자 정의보기 코드로 드래그하여 콘센트와 작업을 연결합니다.

사용자 지정보기 사용

이제 사용자 정의보기가 완료되었습니다. UIView메인 스토리 보드에서 원하는 곳에 추가하기 만하면됩니다. ReusableCustomViewIdentity Inspector에서 보기의 클래스 이름을로 설정합니다 .

여기에 이미지 설명 입력


답변

뷰가 아닌 뷰 컨트롤러에 대한 답변 :

스토리 보드에서 xib를로드하는 더 쉬운 방법이 있습니다. 컨트롤러가에서 상속하는 MyClassController 유형이라고 가정하십시오 UIViewController.

UIViewController스토리 보드에 using IB를 추가합니다 . 클래스 유형을 MyClassController로 변경하십시오. 스토리 보드에 자동으로 추가 된보기를 삭제합니다.

호출하려는 XIB가 MyClassController.xib인지 확인하십시오.

스토리 보드가로드되는 동안 클래스가 인스턴스화되면 xib가 자동으로로드됩니다. 그 이유 UIViewController는 클래스 이름으로 명명 된 XIB를 호출하는 기본 구현 때문 입니다.


답변

이것은 실제로 답은 아니지만이 접근 방식을 공유하는 것이 도움이된다고 생각합니다.

목표 -C

  1. CustomViewWithXib.hCustomViewWithXib.m 을 프로젝트로 가져 오기
  2. 동일한 이름 (.h / .m / .xib)으로 사용자 정의보기 파일 생성
  3. CustomViewWithXib에서 사용자 정의 클래스 상속

빠른

  1. CustomViewWithXib.swift 를 프로젝트로 가져 오기
  2. 동일한 이름 (.swift 및 .xib)으로 사용자 정의보기 파일 생성
  3. CustomViewWithXib에서 사용자 정의 클래스 상속

선택 사항 :

  1. xib 파일로 이동하여 일부 요소를 연결해야하는 경우 사용자 지정 클래스 이름으로 소유자를 설정합니다 (자세한 내용은 Swift 파일을 @Suragch 응답의 소유자로 만들기 부분 참조).

이제 스토리 보드에 사용자 정의보기를 추가 할 수 있으며 표시됩니다. 🙂

CustomViewWithXib.h :

 #import <UIKit/UIKit.h>

/**
 *  All classes inherit from CustomViewWithXib should have the same xib file name and class name (.h and .m)
 MyCustomView.h
 MyCustomView.m
 MyCustomView.xib
 */

// This allows seeing how your custom views will appear without building and running your app after each change.
IB_DESIGNABLE
@interface CustomViewWithXib : UIView

@end

CustomViewWithXib.m :

#import "CustomViewWithXib.h"

@implementation CustomViewWithXib

#pragma mark - init methods

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // load view frame XIB
        [self commonSetup];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        // load view frame XIB
        [self commonSetup];
    }
    return self;
}

#pragma mark - setup view

- (UIView *)loadViewFromNib {
    NSBundle *bundle = [NSBundle bundleForClass:[self class]];

    //  An exception will be thrown if the xib file with this class name not found,
    UIView *view = [[bundle loadNibNamed:NSStringFromClass([self class])  owner:self options:nil] firstObject];
    return view;
}

- (void)commonSetup {
    UIView *nibView = [self loadViewFromNib];
    nibView.frame = self.bounds;
    // the autoresizingMask will be converted to constraints, the frame will match the parent view frame
    nibView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    // Adding nibView on the top of our view
    [self addSubview:nibView];
}

@end

CustomViewWithXib.swift :

import UIKit

@IBDesignable
class CustomViewWithXib: UIView {

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

        commonSetup()
    }

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

        commonSetup()
    }

    // MARK: setup view 

    private func loadViewFromNib() -> UIView {
        let viewBundle = NSBundle(forClass: self.dynamicType)
        //  An exception will be thrown if the xib file with this class name not found,
        let view = viewBundle.loadNibNamed(String(self.dynamicType), owner: self, options: nil)[0]
        return view as! UIView
    }

    private func commonSetup() {
        let nibView = loadViewFromNib()
        nibView.frame = bounds
        // the autoresizingMask will be converted to constraints, the frame will match the parent view frame
        nibView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        // Adding nibView on the top of our view
        addSubview(nibView)
    }
}

여기에서 몇 가지 예를 찾을 수 있습니다 .

도움이되기를 바랍니다.


답변