Как загрузить UIView с помощью nib-файла, созданного с помощью Interface Builder - PullRequest
237 голосов
/ 14 мая 2009

Я пытаюсь сделать что-то немного сложное, но то, что должно быть возможным. Так что это вызов для всех вас, экспертов (на этом форуме собрались многие из вас, ребята :)).

Я создаю «компонент» вопросника, который я хочу загрузить на NavigationContoller (мой QuestionManagerViewController). «Компонент» - это «пустой» UIViewController, который может загружать различные представления в зависимости от вопроса, на который необходимо ответить.

Я делаю это так:

  1. Создание объекта Question1View в качестве подкласса UIView, определяющего некоторый IBOutlets.
  2. Создайте (используя Interface Builder) Question1View.xib (ЗДЕСЬ, ГДЕ МОЯ ПРОБЛЕМА, ВОЗМОЖНО, ). Я установил UIViewController и 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 », но выход из представления не был установлен. '

Я уверен, что есть способ загрузить представление, используя nib-файл, без необходимости создавать класс viewController.

Ответы [ 24 ]

6 голосов
/ 05 февраля 2016

В быстром

На самом деле мое решение этой проблемы состояло в том, чтобы загрузить представление в viewDidLoad в моем CustonViewController, где я хотел использовать такое представление:

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

Не загружайте представление методом loadView()! Метод loadView служит для загрузки представления для вашего пользовательского ViewController.

6 голосов
/ 21 апреля 2010

Я тоже хотел сделать что-то подобное, вот что я нашел: (SDK 3.1.3)

У меня есть контроллер представления A (принадлежащий контроллеру Nav), который загружает VC B нажатием кнопки:

В AViewController.m

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

Теперь VC B имеет свой интерфейс от Bnib, но когда нажата кнопка, я хочу перейти в «режим редактирования», в котором есть отдельный интерфейс пользователя от другого пера, но я не хочу новый 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 содержит только 1 объект верхнего уровня, объект UIView. Неважно, установлен ли Владелец файла в этом перо как BViewController или NSObject по умолчанию, НО убедитесь, что для параметра Просмотр выхода в Владельце файла НЕ установлено ничего. Если это так, то я получаю сбой exc_bad_access и xcode переходит к загрузке 6677 кадров стека показывающий внутренний метод UIView, который неоднократно вызывается ... так выглядит бесконечный цикл. (Однако в моем исходном Bnib установлен View Outlet)

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

5 голосов
/ 18 июня 2016

Для пользователя Swift с настраиваемой опцией:

  1. Создайте пользовательский UIView подкласс и файлы xib , которые мы назовем по имени нашего собственного класса: в нашем случае MemeView. Внутри класса Meme View не забудьте определить его как проектируемый с атрибутом @IBDesignable перед объявлением класса
  2. Помните, чтобы установить владельца файла в xib с нашим пользовательским UIView подклассом на панели Indetity Inspector

    enter image description here

  3. В файле xib теперь мы можем построить наш интерфейс, создать ограничения, создать точки сбыта, действия и т. Д. enter image description here

  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!
    

}

Несколько примеров:
enter image description here enter image description here

Если нам нужно добавить больше информации, когда представление отображается внутри раскадровки или другой xib, для этого мы можем реализовать prepareForInterfaceBuilder(), этот метод будет выполняться только при открытии файла в конструкторе интерфейса. Если вы сделали все, что я написал, но ничего не работает, это способ отладить представление SIGLE, добавив точки останова в его реализации. enter image description here
Вот иерархия представлений. View hiearchy

Надеюсь, это поможет загрузить полный образец здесь

4 голосов
/ 25 марта 2010

Я нашел эту запись в блоге Аароном Хиллегасом (автор, инструктор, Какао-ниндзя) очень поучительной. Даже если вы не примете его модифицированный подход к загрузке файлов NIB через назначенный инициализатор, вы, вероятно, по крайней мере получите лучшее представление о происходящем процессе. Я использую этот метод в последнее время с большим успехом!

3 голосов
/ 01 июля 2009

Предыдущий ответ не учитывает изменения в структуре NIB (XIB), которые произошли между 2.0 и 2.1 из iPhone SDK. Пользовательское содержимое теперь начинается с индекса 0 вместо 1.

Вы можете использовать макрос 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;

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

Barney

3 голосов
/ 25 октября 2011

У меня была причина сделать то же самое (программная загрузка представления из файла 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;
}

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

3 голосов
/ 22 августа 2015

Вот способ сделать это в Swift (в настоящее время пишу Swift 2.0 в XCode 7 beta 5).

Из вашего 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 голосов
/ 27 апреля 2011

Ни один из ответов не объясняет, как создать автономный 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 голос
/ 29 ноября 2017

Кратчайшая версия:

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];
1 голос
/ 25 ноября 2010

@ AVeryDev

6) Чтобы прикрепить загруженный вид к представлению контроллера вида:

[self.view addSubview:myViewFromNib];

Предположительно, необходимо удалить его из поля зрения, чтобы избежать утечек памяти.

Чтобы уточнить: контроллер представления имеет несколько IBOutlets, некоторые из которых связаны с элементами в исходном файле пера (как обычно), а некоторые связаны с элементами в загруженном пиру. Оба пера имеют один и тот же класс владельца. Загруженный вид перекрывает исходный.

Подсказка: установите непрозрачность основного вида в загруженном наконечнике на ноль, тогда он не будет скрывать элементы от исходного наконечника.

...