web-dev-qa-db-ja.com

UIViewサブクラスのペン先をロードする正しい方法

私はこの質問が以前に尋ねられたことを知っていますが、答えは矛盾しており、私は混乱しています。

アプリ全体で再利用可能なUIViewサブクラスが必要です。 nibファイルを使用してインターフェイスを説明します。

ここで、アクティビティインジケータを含むローディングインジケータビューであるとします。何らかのイベントでこのビューをインスタンス化し、View Controllerのビューにアニメーション化したいと思います。ビューのインターフェースをプログラムで問題なく記述し、要素をプログラムで作成し、フレームを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;
    }

しかし、私は自己を上書きしており、それをインスタンス化するとき:

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

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

フレームをIBから取得するのではなく、手動で設定する必要があります。

編集:ビューコントローラーでこのカスタムビューを作成し、ビューの要素を制御するアクセス権があります。新しいView Controllerは必要ありません。

ありがとう

編集:これがベストプラクティスであるかどうかはわかりませんが、そうではないはずですが、これが私がやった方法です:

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) {
                        }];

正しい練習がまだ必要

これは、View Controllerとinitをnib名で使用して行う必要がありますか?コード内のいくつかのUIView初期化メソッドでペン先を設定する必要がありますか?それとも私がやったことは大丈夫ですか?

98
Adam Waite
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では、ビュー自体をクリックし、必要に応じてアウトレットにドラッグする必要がある場合があることを忘れないでください。 (非常に価値のあるヒントは、テーブルセル行に対してこれを行う場合、絶対に行う必要があるということです。ビュー自体をコード内の関連するプロパティに接続する必要があります。)

129
Premsuraj

CustomViewとそのxibFile's Ownerから独立させたい場合は、次の手順に従ってください

  • File's Ownerフィールドは空のままにします。
  • xibCustomViewファイルで実際のビューをクリックし、そのCustom ClassCustomView(カスタムビュークラスの名前)として設定します
  • カスタムビューの.hファイルにIBOutletを追加します。
  • カスタムビューの.xibファイルで、ビューをクリックしてConnection Inspectorに進みます。ここでは、.hファイルで定義するすべてのIBOutletsを作成します
  • それらをそれぞれのビューに接続します。

CustomViewクラスの.mファイルで、次のように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];を使用します

39
Ans

次の手順に従ってください

  1. タイプUIViewのMyView .h/.mという名前のクラスを作成します。
  2. 同じ名前MyView.xibのxibを作成します。
  3. 次に、ファイル所有者クラスをxibのUIViewControllerからNSObjectに変更します。下の画像をご覧ください enter image description here
  4. ファイル所有者ビューをビューに接続します。下の画像をご覧ください enter image description here

  5. ビューのクラスをMyViewに変更します。 3と同じ。

  6. プレースコントロールはIBOutletsを作成します。

ビューをロードするコードは次のとおりです。

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

それが役に立てば幸い。

明確化

UIViewControllerはxibをロードするために使用され、UIViewControllerが持つビューは、実際にはMyViewで、MyView xibで割り当てています。

Demoデモを取得しました here

24
iphonic

ここで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()
9
Adam Waite

Swiftの場合:

たとえば、カスタムクラスの名前はInfoViewです

最初に、次のようなファイルInfoView.xibおよびInfoView.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 OwnerUIViewControllerに次のように設定します。

enter image description here

Viewの名前をInfoViewに変更します。

enter image description here

File's Ownerを右クリックして、viewフィールドをInfoViewに接続します。

enter image description here

クラス名がInfoViewであることを確認してください:

enter image description here

この後、カスタムクラスのボタンに問題なくアクションを追加できます。

enter image description here

MainViewControllerでのこのカスタムクラスの使用法:

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

さて、View Controllerを使用して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がカスタムビューを参照するためです

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