Правильный способ загрузки Nib для подкласса UIView

Я знаю, что этот вопрос задавали раньше, но ответы противоречат друг другу, и я смущен, поэтому, пожалуйста, не пламени меня.

Я хочу иметь многократно используемый подкласс 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;
    }

Но я переписываю себя, и когда я его создаю:

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

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

Мне нужно вручную установить кадр, а не выбрать его из IB.

EDIT: Я хочу создать это настраиваемое представление в контроллере представления и иметь доступ к элементам управления. Я не хочу новый контроллер представления.

Спасибо

РЕДАКТИРОВАТЬ: Я не знаю, является ли это лучшей практикой, я уверен, что нет, но так я это сделал:

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

Требуется правильная практика

Если это было сделано с помощью контроллера представления и init с именем nib? Должен ли я установить nib в некоторый метод инициализации UIView в коде? Или это то, что я сделал нормально?

Ответы

Ответ 1

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 вы нажимаете на File Owner и устанавливаете владельца файла на YourClass. Тогда на самом деле в YourClass.h у вас будут свойства IBOutlet. В TopArea.xib вы можете перетащить элементы управления в эти выходы.

Не забывайте, что в TopArea.xib вам может потребоваться щелкнуть по самому виду просмотра и перетащить его в какую-то розетку, чтобы вы могли контролировать ее, если это необходимо. (Очень полезен совет, что, когда вы делаете это для строк таблицы ячеек, вам абсолютно необходимо это сделать - вам нужно подключить представление к соответствующему свойству в вашем коде.)

Ответ 2

Если вы хотите сохранить свой CustomView и его xib независимо от File Owner, выполните следующие шаги

  • Оставьте поле File Owner пустым.
  • Нажмите фактический просмотр в xib файле вашего CustomView и установите его Custom Class как CustomView (имя вашего настраиваемого класса просмотра)
  • Добавить IBOutlet в .h файл вашего пользовательского представления.
  • В .xib файле вашего пользовательского представления нажмите на просмотр и перейдите в Connection Inspector. Здесь вы найдете все свои IBOutlets, которые вы определяете в .h файле
  • Подключите их к соответствующему виду.

в .m файла вашего класса CustomView, переопределите метод 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];

Ответ 3

Выполните следующие шаги

  • Создайте класс с именем MyView.h/.m типа UIView.
  • Создайте xib с тем же именем MyView.xib.
  • Теперь измените класс File Owner на UIViewController из NSObject в xib. См. Изображение ниже enter image description here
  • Подключите вид файла владельца к вашему представлению. См. Изображение ниже enter image description here

  • Измените класс вашего представления на MyView. То же, что 3.

  • Элементы управления созданием IBOutlets.

Вот код для загрузки View:

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

Надеюсь, что это поможет.

Разъяснение

UIViewController используется для загрузки xib и представления, которое UIViewController имеет на самом деле MyView, которое вы назначили в MyView xib..

Demo Я сделал demo grab здесь

Ответ 4

Отвечая на мой вопрос о 2 или что-то лет спустя, но...

Он использует расширение протокола, поэтому вы можете сделать это без какого-либо дополнительного кода для всех классов.

/*

Prerequisites
-------------
- In IB set the view class to the type hook up any IBOutlets
- In IB ensure the file 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()

Ответ 5

В 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 Owner в UIViewController следующим образом:

введите описание изображения здесь

Переименуйте View в InfoView:

введите описание изображения здесь

Щелкните правой кнопкой мыши по File Owner и соедините поле View со своим InfoView:

введите описание изображения здесь

Убедитесь, что имя класса InfoView:

введите описание изображения здесь

И после этого вы можете добавить действие к кнопке в своем пользовательском классе без проблем:

введите описание изображения здесь

И использование этого пользовательского класса в MainViewController:

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

Ответ 6

Ну, вы можете либо инициализировать 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 не является хорошей идеей, поскольку свойство представления контроллера будет изменено, и когда вы захотите получить доступ к исходному виду, вы не будет иметь к нему ссылки.

РЕДАКТИРОВАТЬ:. Вы столкнетесь с этой проблемой, если вы установили новый xib с владельцем файла как один и тот же основной класс UIViewController и связали свойство вида с новым представлением xib.

т.е.;

  • YourMainViewController - управляет - mainView
  • CustomView - требуется загрузить с xib по мере необходимости.

Ниже приведенный ниже код вызовет путаницу, если вы напишете его внутри вида, загрузите YourMainViewController. Это связано с тем, что self.view с этой точки будет ссылаться на ваш пользовательский вид

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