Подтвердить что ты не робот

Подкласс UIView со своим XIB

Я создал пользовательский подкласс UIView и предпочел бы не размещать UI в коде в подклассе UIView. Я хотел бы использовать xib для этого. Итак, я сделал следующее.

Я создал класс "ShareView", который подклассифицирует UIView. Я создал файл XIB с его владельцем файла, установленным в "ShareView". Затем я связываю некоторые выходы, объявленные в моем "ShareView.h".

Далее у меня есть ViewController, MainViewController, который добавляет ShareView в качестве подзаголовка. с этим кодом:

NSArray *arr = [[NSBundle mainBundle] loadNibNamed:@"ShareView" owner:nil options:nil];
UIView *fv = [[arr objectAtIndex:0] retain];
fv.frame = CGRectMake(0, 0, 320, 407);
[self.view addSubview:fv];

Но теперь я получаю ошибки NSUnknownKeyException в точках, объявленных в моем ShareView.

Причина, по которой я все это делал, - это то, что я хочу UIView с собственной логикой в ​​отдельном XIB файле. Я читал в нескольких местах, что ViewControllers используются только для управления полноэкранным режимом, т.е. Не части экрана... Так что я делаю неправильно? Я хочу, чтобы моя логика для ShareView была в отдельном классе, поэтому мой класс MainController не раздувается с помощью логики из ShareView (который, я думаю, является описанием для решения этой проблемы?)

4b9b3361

Ответ 1

ThomasM,

У нас были похожие идеи об инкапсулировании поведения внутри пользовательского представления (скажем, ползунка с метками сопутствующих сообщений для значений min/max/current, причем события с измененными значениями также обрабатывались внутренним элементом управления).

В нашей современной передовой практике мы разработали ShareView в Interface Builder (ShareView.xib), как описано Эймантасом в его ответе. Затем мы вставляем ShareView в иерархию представлений в MainViewController.xib.

Я написал, как мы вставляем пользовательские виджеты в другие Nibs в нашем блоге разработчиков iOS. Кружок переопределяет -awakeAfterUsingCoder: в вашем пользовательском представлении, заменяя объект, загруженный из MainViewController.xib, с загруженного из "внедренного" Nib (ShareView.xib).

Что-то в этом роде:

// ShareView.m
- (id) awakeAfterUsingCoder:(NSCoder*)aDecoder {
    BOOL theThingThatGotLoadedWasJustAPlaceholder = ([[self subviews] count] == 0);
    if (theThingThatGotLoadedWasJustAPlaceholder) {
        // load the embedded view from its Nib
        ShareView* theRealThing = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([ShareView class]) owner:nil options:nil] objectAtIndex:0];

        // pass properties through
        theRealThing.frame = self.frame;
        theRealThing.autoresizingMask = self.autoresizingMask;

        [self release];
        self = [theRealThing retain];
    }
    return self;
}

Ответ 2

Вы определили владельца загруженного xib как nil. Поскольку владелец файла в xib сам подключен к розеткам и определен как экземпляр ShareView, вы получаете исключение из неизвестных ключей (nil не имеет свойств, которые вы определили для ShareView).

Вы должны определить загрузчика xib как владельца (т.е. диспетчера представлений, ответственного за загрузку xib). Затем добавьте отдельный объект UIView в xib и определите его как экземпляр ShareView. Затем при загрузке xib.

ShareView *shareView = [[[[NSBundle mainBundle] loadNibNamed:@"ShareView" owner:self options:nil] objectAtIndex:0] retain];

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

Ответ 3

Я хотел бы добавить к ответу. Я надеюсь, что люди улучшат этот ответ.

В первую очередь это работает.

XIB:

enter image description here

Результат:

enter image description here

Я хотел бы подклассифицировать UIView в течение длительного времени, особенно для tableViewCell.

Вот как я это сделал.

Это удачно, но какая-то часть по-прежнему "неудобна", на мой взгляд.

Сначала я создал обычный файл .h,.m и xib. Обратите внимание, что у Apple нет флажка для автоматического создания xib, если созданный вами подкласс не является подклассом UIViewController. Ну, все равно создавайте их.

#import <UIKit/UIKit.h>
#import "Business.h"

@interface BGUIBusinessCellForDisplay : UITableViewCell

+ (NSString *) reuseIdentifier;


- (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz;
@end

Действительно простой UITableViewCell, который я хочу инициализировать последним с помощью biz.

Я использую reuseidentifier, который вы должны сделать для UITableViewCell

//#import "Business.h"
@interface BGUIBusinessCellForDisplay ()
@property (weak, nonatomic) IBOutlet UILabel *Title;
@property (weak, nonatomic) IBOutlet UIImageView *Image;
@property (weak, nonatomic) IBOutlet UILabel *Address;
@property (weak, nonatomic) IBOutlet UILabel *DistanceLabel;
@property (weak, nonatomic) IBOutlet UILabel *PinNumber;
@property (strong, nonatomic) IBOutlet BGUIBusinessCellForDisplay *view;

@property (weak, nonatomic) IBOutlet UIImageView *ArrowDirection;
@property (weak, nonatomic) Business * biz;
@end

@implementation BGUIBusinessCellForDisplay

- (NSString *) reuseIdentifier {
    return [[self class] reuseIdentifier];
};
+ (NSString *) reuseIdentifier {
    return NSStringFromClass([self class]);
};

Затем я исключил большинство кодов инициализации и поставил вместо этого:

- (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz
{
    if (self.biz == nil) //First time set up
    {
        self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right
        NSString * className = NSStringFromClass([self class]);
        //PO (className);
        [[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];
        [self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it subview Things don't work without it though
    }
    if (biz==nil)
    {
        return self;
    }

    self.biz = biz;
    self.Title.text = biz.Title; //Let set this one thing first
    self.Address.text=biz.ShortenedAddress;

    //if([self.distance isNotEmpty]){
    self.DistanceLabel.text=[NSString stringWithFormat:@"%dm",[biz.Distance intValue]];
    self.PinNumber.text =biz.StringPinLineAndNumber;

Обратите внимание, что это действительно неудобно.

Прежде всего, init можно использовать двумя способами.

  • Его можно использовать сразу после aloc
  • Его можно использовать, если у нас есть другой существующий класс, а затем мы просто хотим инициализировать эту существующую ячейку в другой бизнес.

Итак, я сделал:

if (self.biz == nil) //First time set up
{
    self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right
    NSString * className = NSStringFromClass([self class]);
    //PO (className);
    [[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];
    [self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it subview Things don't work without it though
}

Другие неприятные вещи, которые я сделал, это когда я делаю [self addSubview: self.view];

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

Ответ 4

Вы можете создать свой пользовательский UIView, созданный в xib, и даже заставить Interface Builder отображать его внутри других xib файлов или раскадровки в новом Xcode 6 с использованием IB_DESIGNABLE. В xib установите владельца файла в свой собственный класс, но не устанавливайте класс UIView, чтобы избежать проблем с загрузкой ревальвации. Просто оставьте класс UIView по умолчанию, и вы добавите этот UIView в качестве поднабора вашего пользовательского представления класса. Подключите все свои торговые точки к владельцу файла и в своем пользовательском классе загрузите xib, как в приведенном ниже коде. Вы можете проверить мой видеоурок здесь: https://www.youtube.com/watch?v=L97MdpaF3Xg

IB_DESIGNABLE
@interface CustomControl : UIView
@end

@implementation CustomControl

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

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

- (void)load
{
    UIView *view = [[[NSBundle bundleForClass:[self class]] loadNibNamed:@"CustomControl" owner:self options:nil] firstObject];
    [self addSubview:view];
    view.frame = self.bounds;
}

@end

Если вы используете автозапуск, вы можете изменить: view.frame = self.bounds; на:

[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];

Ответ 5

Чтобы использовать шаблон Yang с Auto-Layout, вам нужно добавить следующее в методе -awakeWithCoder:.

    theRealThing.translatesAutoresizingMaskIntoConstraints = NO;

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

ИЗМЕНИТЬ: Автомакет все еще может быть болью. Некоторые ограничения не соблюдаются, но другие (например, закрепление на дне не работает, но закрепление в верхней части). Мы не совсем уверены, почему, но вы можете обойти это, вручную передав ограничения от placeholder к RealThing.

Также стоит отметить, что этот шаблон работает точно так же, как и с Storyboards, как и с обычными xxs (т.е. вы можете создать элемент пользовательского интерфейса в xix и перевести его в контроллер StoryBoard View, выполнив следующие шаги).

Ответ 6

Вместо подкласса UIView почему бы вам не подклассифицировать UIViewController. Проверьте следующую ссылку. В этом были созданы "RedView" и "BlueView" UIViewControllers с их xib и добавлены в представление MultipleViewsController, создав и экземпляр первых двух классов и добавив [self.view addSubview:red.view] и [self.view addSubview:blue.view] в метод MultipleViewsController viewDidLoad

Несколько контроллеров в одном представлении

Просто добавьте (id) отправителя к функции нажатия кнопки в RedView и BlueView в коде вышеуказанной ссылки.