it-swarm.com.ru

Как загрузить UIView с помощью nib-файла, созданного с помощью Interface Builder

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

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

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

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

237
Gonso

Есть также более простой способ получить доступ к представлению вместо того, чтобы иметь дело с пером как массивом.

1) Создайте пользовательский подкласс View с любыми точками, к которым вы хотите иметь доступ позже. --Мой взгляд

2) в UIViewController, который вы хотите загрузить и обработать пером, создайте свойство IBOutlet, которое будет содержать представление загруженного кончика, например

в MyViewController (подкласс UIViewController)

  @property (nonatomic, retain) IBOutlet UIView *myViewFromNib;

(не забудьте синтезировать его и выпустить в вашем файле .m)

) откройте ваше перо (мы назовем его "myViewNib.xib") в IB, установите для владельца файла значение MyViewController

4) теперь подключите выход MyViewFromNib владельца файла к основному виду в кончике.

5) Теперь в MyViewController напишите следующую строку:

[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];

Теперь, как только вы сделаете это, вызов вашего свойства "self.myViewFromNib" даст вам доступ к представлению с вашего кончика!

223
averydev

Спасибо вам всем. Я нашел способ сделать то, что хотел.

  1. Создайте свое UIView с необходимыми IBOutlets.
  2. Создайте xib в IB, разработайте его по своему вкусу и свяжите с ним так: владелец файла принадлежит к классу UIViewController (нет пользовательского подкласса, кроме "реального"). Представление "Владелец файла" связано с основным представлением, и его класс объявляется как класс из шага 1).
  3. Соедините ваши элементы управления с IBOutlets.
  4. DynamicViewController может запустить свою логику, чтобы решить, какое представление/xib загрузить. Как только он принял решение, в методе loadView поместите что-то вроде этого:

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
                                                      owner:self
                                                    options:nil];
    
    QPickOneView* myView = [ nibViews objectAtIndex: 1];
    
    myView.question = question;
    

Это оно!

Метод loadNibNamed основного пакета позаботится об инициализации представления и создании соединений.

Теперь ViewController может отображать то или иное представление в зависимости от данных в памяти, и "родительскому" экрану не нужно беспокоиться об этой логике.

119
Gonso

Я не уверен, о чем идет речь в некоторых ответах, но мне нужно разместить этот ответ здесь, когда я буду искать в Google в следующий раз. Ключевые слова: "Как загрузить UIView из пера" или "Как загрузить UIView из NSBundle".

Вот код почти на 100% прямо из книги Apress Начиная с iPhone (стр. 247, "Использование новой ячейки табличного представления"):

- (void)viewDidLoad {
    [super viewDidLoad];
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah"
                                                 owner:self options:nil];
    Blah *blah;
    for (id object in bundle) {
        if ([object isKindOfClass:[Blah class]]) {
            blah = (Blah *)object;
            break;
        }
    }   
    assert(blah != nil && "blah can't be nil");
    [self.view addSubview: blah];
} 

Предполагается, что у вас есть подкласс UIView с именем Blah, перо с именем Blah, которое содержит UIView, класс которого установлен на Blah.


Категория: NSObject + LoadFromNib

#import "NSObject+LoadFromNib.h"

@implementation NSObject (LoadFromNib)

+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:classToLoad]) {
            return object;
        }
    }
    return nil;
}

@end

Быстрое расширение

extension UIView {
    class func loadFromNib<T>(withName nibName: String) -> T? {
        let nib  = UINib.init(nibName: nibName, bundle: nil)
        let nibObjects = nib.instantiate(withOwner: nil, options: nil)
        for object in nibObjects {
            if let result = object as? T {
                return result
            }
        }
        return nil
    }
}

И пример в использовании:

class SomeView: UIView {
    class func loadFromNib() -> SomeView? {
        return self.loadFromNib(withName: "SomeView")
    }
}
69
Dan Rosenstark

Для всех тех, кому необходимо управлять более чем одним экземпляром пользовательского представления, а именно Outlet Collection, я объединил и настроил ответы @Gonso, @AVeryDev и @Olie следующим образом:

  1. Создайте пользовательский MyView : UIView и установите его как "Custom Class" корневого UIView в нужном XIBcustom class

  2. Создайте все выходы, которые вам нужны, в MyView (сделайте это сейчас, потому что после пункта 3 IB предложит вам подключать выходы к UIViewController, а не к пользовательскому представлению, как мы хотим); custom class outlet

  3. Установите ваше UIViewController как "Владелец файла" пользовательского представления XIBenter image description here

  4. В UIViewController добавьте новое UIViews для каждого требуемого экземпляра MyView и подключите их к UIViewController, создав Outlet Collection: эти представления будут действовать как "оболочки" для экземпляров настраиваемого представления; enter image description here

  5. Наконец, в viewDidLoad вашего UIViewController добавьте следующие строки:

NSArray *bundleObjects;
MyView *currView;
NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count];
for (UIView *currWrapperView in myWrapperViews) {
    bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
    for (id object in bundleObjects) {
        if ([object isKindOfClass:[MyView class]]){
            currView = (MyView *)object;
            break;
        }
    }

    [currView.myLabel setText:@"myText"];
    [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal];
    //...

    [currWrapperView addSubview:currView];
    [myViews addObject:currView];
}
//self.myViews = myViews; if need to access them later..
22
Francesco Vadicamo

Я бы использовал UINib для создания экземпляра пользовательского UIView для повторного использования

UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];

В этом случае необходимы следующие файлы: MyCustomView.xib, MyCustomViewClass.h и MyCustomViewClass.m Обратите внимание, что [UINib instantiateWithOwner] возвращает массив, поэтому вам следует использовать элемент, который отражает UIView, который вы хотите использовать повторно. В данном случае это первый элемент.

18
thgc

Вы не должны устанавливать класс вашего контроллера представления как подкласс UIView в Интерфейсном Разработчике. Это определенно, по крайней мере, часть вашей проблемы. Оставьте это как UIViewController, какой-то его подкласс или какой-нибудь другой пользовательский класс, который у вас есть.

Что касается загрузки только представления из xib, я предполагал, что вы имели иметь какой-то контроллер представления (даже если он не расширяет UIViewController, который может быть слишком тяжелым для ваших нужд) ) установите в качестве владельца файла в Интерфейсном Разработчике, если вы хотите использовать его для определения вашего интерфейса. Я провёл небольшое исследование, чтобы подтвердить это. Это связано с тем, что в противном случае не было бы никакого доступа к любому из элементов интерфейса в UIView, и не было бы способа, чтобы ваши собственные методы в коде запускались событиями.

Если вы используете UIViewController в качестве владельца вашего файла для ваших представлений, вы можете просто использовать initWithNibName:bundle:, чтобы загрузить его и вернуть объект контроллера представления. В IB убедитесь, что вы установили выход view в вид с вашим интерфейсом в xib. Если вы используете какой-либо другой тип объекта в качестве Владельца вашего Файла, вам нужно будет использовать метод NSBundle's loadNibNamed:owner:options: для загрузки пера, передавая в него экземпляр Владельца Файла. Все его свойства будут установлены в соответствии с точками, которые вы определили в IB.

10
Marc W

Вы также можете использовать initWithNibName UIViewController вместо loadNibNamed. Это проще, я нахожу.

UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release];  // release the VC

Теперь вам просто нужно создать MySubView.xib и MySubView.h/m. В MySubView.xib установите для класса "Владелец файла" значение UIViewController, а для класса представления - "MySubView".

Вы можете расположить и определить размер subview, используя родительский xib-файл.

10
Ying

Это то, что должно быть проще. В итоге я расширил UIViewController и добавил селектор loadNib:inPlaceholder:. Теперь я могу сказать

self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];

Вот код для этой категории (она выполняет ту же процедуру, что и Гонсо):

@interface UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;

@end

@implementation UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName
{
  NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; 
  for (id view in xib) { // have to iterate; index varies
    if ([view isKindOfClass:[UIView class]]) return view;
  }
  return nil;
}

- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
  UIView *nibView = [self viewFromNib:nibName];
  [nibView setFrame:placeholder.frame];
  [self.view insertSubview:nibView aboveSubview:placeholder];
  [placeholder removeFromSuperview];
  return nibView;
}

@end
8
skot

Это отличный вопрос (+1), и ответы были почти полезными;) Извините, ребята, но у меня было чертовски много времени на это, хотя и Gonso, и AVeryDev дали хорошие подсказки. Надеюсь, этот ответ поможет другим.

MyVC - это контроллер представления, содержащий все эти вещи.

MySubview - это представление, которое мы хотим загрузить из xib

  • В MyVC.xib создайте представление типа MySubView, которое имеет правильный размер и форму и расположено там, где вам нужно.
  • В MyVC.h есть

    IBOutlet MySubview *mySubView
    // ...
    @property (nonatomic, retain) MySubview *mySubview;
    
  • В MyVC.m, @synthesize mySubView; и не забудьте выпустить его в dealloc.

  • В MySubview.h есть выход/свойство для UIView *view (может быть ненужным, но для меня это сработало.) Синтезировать и выпустить его в .m
  • В MySubview.xib
    • установите тип владельца файла на MySubview и свяжите свойство представления с вашим представлением.
    • Выложите все биты и подключитесь к IBOutlets по желанию
  • Вернуться в MyVC.m, есть

    NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL];
    MySubview *msView = [xibviews objectAtIndex: 0];
    msView.frame = mySubview.frame;
    UIView *oldView = mySubview;
    // Too simple: [self.view insertSubview: msView aboveSubview: mySubview];
    [[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting
    self.mySubview = msView;
    [oldCBView removeFromSuperview];
    

Хитрость для меня заключалась в том, что подсказки в других ответах загружали мое представление из xib, но НЕ заменяли представление в MyVC (да!) - мне пришлось поменять его самостоятельно.

Кроме того, чтобы получить доступ к методам mySubview, для свойства view в файле .xib должно быть установлено значение MySubview. В противном случае он возвращается как обычный старый UIView.

Если есть способ загрузить mySubview напрямую из его собственной xib, это было бы здорово, но это дало мне то, что мне нужно было.

8
Olie

Я тоже хотел сделать что-то подобное, вот что я нашел: (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 установлена ​​точка выхода IS)

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

6
Brynjar

In Swift

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

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

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

6
kalafun

Я сделал категорию, которая мне нравится:

UIView+NibInitializer.h

#import <UIKit/UIKit.h>

@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end

UIView+NibInitializer.m

#import "UIView+NibInitializer.h"

@implementation UIView (NibInitializer)

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    if (!nibNameOrNil) {
        nibNameOrNil = NSStringFromClass([self class]);
    }
    NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil
                                                        owner:self
                                                      options:nil];
    for (id view in viewsInNib) {
        if ([view isKindOfClass:[self class]]) {
            self = view;
            break;
        }
    }
    return self;
}

@end

Затем позвоните так:

MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];

Используйте имя пера, если ваше перо названо не так, как имя вашего класса.

Чтобы переопределить его в ваших подклассах для дополнительного поведения, это может выглядеть так:

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    self = [super initWithNibNamed:nibNameOrNil];
    if (self) {
        self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
    }
    return self;
}
6
Logan

Для 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 hereenter image description here

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

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

5
Andrea

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

4
Meltemi

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

Вот способ сделать это в 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()

3
Johannes

Предыдущий ответ не учитывает изменения в структуре 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;

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

Гулянка

3
Barney Mattox

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

2
whitneyland

@AVeryDev

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

[self.view addSubview:myViewFromNib];

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

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

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

1
Matt

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

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];
1
wzbozon

Потратив много часов, я придумал следующее решение. Выполните следующие действия, чтобы создать собственное UIView.

1) Создать класс myCustomView , унаследованный от UIView .
enter image description here

2) Создайте . Xib с именем myCustomView .
enter image description here

3) Измените класс UIView внутри вашего .xib файла, назначьте myCustomView Класс там.
enter image description here

4) Создать IBOutlets
enter image description here

5) Загрузите .xib в myCustomView * customView . Используйте следующий пример кода.

myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];

Примечание: для тех, кто все еще сталкивается с проблемой, можете прокомментировать, я предоставлю им ссылку примера проекта с myCustomView

1
Aamir

У меня есть соглашение о присвоении имен XIB с представлениями в них так же, как представление. То же самое, что можно было бы сделать для контроллера представления. Тогда мне не нужно записывать имена классов в коде. Я загружаю UIView из файла пера с тем же именем.

Пример для класса под названием MyView.

  • Создайте файл пера MyView.xib в Интерфейсном Разработчике
  • В Интерфейсном Разработчике добавьте UIView. Установите его класс в MyView. Настройте по своему вкусу, подключите переменные экземпляра MyView к подпредставлениям, к которым вы, возможно, захотите обратиться позже.
  • В своем коде создайте новый MyView, например:

    MyView * myView = [MyView nib_viewFromNibWithOwner: owner];

Вот категория для этого:

@implementation UIView (nib)

+ (id) nib_viewFromNib {
    return [self nib_viewFromNibWithOwner:nil];
}

+ (id) nib_viewFromNibWithOwner:(id)owner {
    NSString *className = NSStringFromClass([self class]);
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil];
    UIView *view = nil;
    for(UIView *v in nib) {
        if ([v isKindOfClass:[self class]]) {
            view = v;
            break;
        }
    }
    assert(view != nil && "View for class not found in nib file");
    [view nib_viewDidLoad];
    return view;
}

// override this to do custom setup
-(void)nib_viewDidLoad {

}

Затем я бы связывал кнопки с действиями используемого мной контроллера и устанавливал объекты на метках с помощью выходов в моем подклассе пользовательского представления.

0
n13

Чтобы программно загрузить представление из nib/xib в Swift 4:

// Load a view from a Nib given a placeholder view subclass
//      Placeholder is an instance of the view to load.  Placeholder is discarded.
//      If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {

    let nib = loadNib(givenNibName, placeholder: placeholderView)
    return instantiateView(fromNib: nib, placeholder: placeholderView)
}

// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
    //1. Load and unarchive nib file
    let nibName = givenNibName ?? String(describing: type(of: placeholderView))

    let nib = UINib(nibName: nibName, bundle: Bundle.main)
    return nib
}

// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
    //1. Get top level objects
    let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)

    //2. Have at least one top level object
    guard let firstObject = topLevelObjects.first else {
        fatalError("\(#function): no top level objects in nib")
    }

    //3. Return instantiated view, placeholderView is not used
    let instantiatedView = firstObject as! T
    return instantiatedView
}
0
Eng Yew