使用界面构建器创建可循环视图

时间:2011-05-10 18:13:31

标签: iphone uiview interface-builder

从Nib文件中获取项目的新方法。

以下答案中的示例。

这个新答案是此项目的最新变化和问题

我想创建一个UIView子类,我想为UIView创建一个Interface构建器文件。我如何将文件所有者连接到自身。我希望能够设计视图并让它创建连接,但我不需要视图控制器。

除了将nib加载到数组中之外,我可以使用nib文件作为视图本身的连接器吗?

我希望我正确地问这个问题。我会详细说明。

创建UIViewController我可以选择为它创建一个xib文件。那时我可以将UIViews添加到xib中并进行连接。但是文件所有者似乎没有与其关联的UI编辑器,所以我想创建一个UIView,它将成为另一个视图中的子视图。我真的不想用代码创建它,因为手动布局项目很痛苦。所以我想使用相同的构造来创建一个小的uiview以放入另一个

修改

当我找到另一种更好的加载nib类的方法时,我再次编辑了这个。我想一些用户希望看到的堆栈溢出。

我想找到一种从笔尖加载UIView的优雅方法。并且来自堆栈溢出用户PeyloW的一些信息我已经设法做到这一点。

我将发布我的结果,因为信息将导致一个解决方案,但这个解决方案对我来说非常优雅。

  • 首先,我创建了一个名为“TableViewCells.xib”的Xib文件 (名称匹配类名称因此[[self class] description] ==“TableViewCells”)
  • 接下来,我创建了我用于Nib视图的类。 类并不重要,只需确保为界面构建器中的类对象选择“自定义类”
  • 最后,我创建了类“Ta​​bleViewCells”以匹配xib文件名

我的新头文件。
(导入的每个头文件都与单个xib文件中的项相关联。)

#import <Foundation/Foundation.h>
#import "TitleCell.h"
#import "ToggleCell.h"
#import "ButtonCell.h"
#import "TextCell.h"

@interface TableViewCells : NSObject {
    UINib *uiNibHolder;
}

@property (nonatomic, readonly) NSArray *nibArray;

// These are the classes that will be loadable.
- (TitleCell*) titleCell;
- (ToggleCell*) toggleCell;
- (ButtonCell*) buttonCell;
- (TextCell*) textCell;
- (id) loadViewByClass: (id) viewClass;
@end

和类文件。

loadViewByClass:是在nib文件中查找项目的方法 便捷存取器具有正确的类型,可以将类对象实例化 注意:我不建议将一堆视图加载到此中,加载的视图越多,加载nib文件时必须创建的视图越多。

#import "TableViewCells.h"

@implementation TableViewCells

@synthesize nibArray;

- (void) unloadProperties{
    [uiNibHolder release];
}
- (NSArray *)nibArray{
    return [uiNibHolder instantiateWithOwner:nil options:nil];
}
- (id) init{
    if ((self = [super init]))
    {
        // Im using the class name for the Xib file. 
        // This means it will look in TableViewCells.xib
        // You can change the class name, or pass a Nib name to this class 
        uiNibHolder = [[UINib nibWithNibName:[[self class] description] bundle: [NSBundle mainBundle]] retain];
    }
    return self;
}
- (TitleCell *)titleCell{
    return [self loadViewByClass:[TitleCell class]];
}
- (ToggleCell *)toggleCell{
    return [self loadViewByClass:[ToggleCell class]];
}
- (ButtonCell *)buttonCell{
    return [self loadViewByClass:[ButtonCell class]];
}
- (TextCell *)textCell{
    return [self loadViewByClass:[TextCell class]];
}
- (id)loadViewByClass:(Class)viewClass{
    for (id item in self.nibArray) 
    {
        if ([item isKindOfClass:viewClass])
        {
            return item;
        }
    }
    return nil;
}

- (void)dealloc{
    [self performSelector:@selector(unloadProperties)];
    [super dealloc];
}

@end

能够按班级加载是非常好的。如果该类不在xib文件中,则返回nil。

这可以适用于使用多个nib文件,具体取决于您加载的视图类型。使用延迟加载属性技术,您可以仅在需要时加载这些笔尖,使内存占用空间小,并且仍然允许您通过一个方便的类加载器加载类。

作为演示,我使用TableView加载了一小撮视图

@synthesize tableViewCellLoader = _tableViewCellLoader;
- (TableViewCells *)tableViewCellLoader{
    if (_tableViewCellLoader == nil){
        _tableViewCellLoader = [[TableViewCells alloc] init];
    }
    return _tableViewCellLoader;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    switch (indexPath.row) {
        case 0:
            return self.tableViewCellLoader.toggleCell;
            break;
        case 1: 
            return self.tableViewCellLoader.textCell;
            break;
        case 2:
            return self.tableViewCellLoader.titleCell;
            break;
        case 3:
            return self.tableViewCellLoader.buttonCell;
            break;
    }
    return nil;
}

正如你所看到的,我懒得加载我的TableViewCellLoader,如果我想,我只能在调用它们的类时使TableViewCells类延迟加载UINib对象。

我喜欢代码方便。

仍然, 再次感谢PeyloW。

3 个答案:

答案 0 :(得分:1)

文件所有者只是一个代理对象,不需要在运行时指定,但在Interface Builder中的设计时始终可见。

如果您不需要,可以忽略连接文件所有者的任何内容。 at运行时使用以下内容加载NIB文件:

NSArray* rootObject = [[NSBundle mainBundle] loadNibNamed:@"MyNib" 
                                                    owner:nil 
                                                  options:nil];

owner参数传递的内容是加载nib时作为文件所有者代理传递的内容,nil工作正常。

rootObject数组包含所有根级别对象,但不包含代理。请注意,数组是自动释放的,因此如果您需要加载的对象留在周围,则必须保留数组,或者只保留您感兴趣的特定元素。

使用loadNibNamed:owner:options:并不是IO绑定。如果需要性能,那么您应该使用UINib的实例来缓存内存中的NIB文件并从中实例化对象。

UINib* myNib = [[UINib nibWithNibName:@"MyNib" 
                               bundle:[NSBundle mainBundle]] retain];
// And then instantiate using:
NSArray* rootObject = [myNib instantiateWithOwner:nil
                                          options:nil];

答案 1 :(得分:0)

您可以这样做,但没有理由不能在UIView中创建IBOutlets并直接连接到它们。因此,在xib文件中,您将拥有一个根UIView,请确保将该类更改为您的自定义类。然后,您可以直接在视图上设置插座,不需要使用文件所有者。

答案 2 :(得分:0)

我的最新答案

经过很长时间并且玩这种可能性后,我尝试了许多场景并得出结论,最好的方案是为每个请求创建UINib并获取相关对象。一次通过可以获得最佳效果。如果你有一个你计划经常使用的对象,最好将它放入自己的nib中,而不是在具有多个不同类的nib中,因为每次初始化UINib时都会创建每个类。

这是我用来完成这项任务的课程。

LSObjectLoader.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface LSObjectLoader : NSObject {
    UINib *nibHolder;
    NSString *currentNibName;
    NSArray *nibArray;
}

- (id) loadViewFromNib: (NSString*) nibName ofClassType:(Class) classType;

@end

LSObjectLoader.m

//
//  LSObjectLoader.m
//  APMobile
//
//  Created by jason.mckinley on 2/8/12.
//  Copyright (c) 2012 LoKi-Systems. All rights reserved.
//

#import "LSObjectLoader.h"

@implementation LSObjectLoader

- (id) loadViewFromNib: (NSString*) nibName ofClassType:(Class) classType
{
    if (![currentNibName isEqualToString:nibName])
    {
        currentNibName = [nibName copy];
        nibHolder = [UINib nibWithNibName:currentNibName bundle:[NSBundle mainBundle]];
    }
    nibArray = [nibHolder instantiateWithOwner:nil options:nil];

    for (id item in nibArray)
    {
        if ([item isKindOfClass:classType])
        {
            return item;
        }
    }

    return nil;
}

@end

最后有几种方法可以实现这一点。

方法1直接加载。

LSObjectLoader *oLoader = [[LSObjectLoader alloc] init];
MyClass *thisInstance = [oLoader loadViewFromNib:@"TableViewCells"
                                     ofClassType:[MyClass class]];

方法2类加载

Nib文件“TableViewCells.xib”(包含“MyChatCell”类对象)

类文件“MyChatCell.h / MyChatCell.m”

标题

#import <UIKit/UIKit.h>

@interface MyChatCell : UITableViewCell

@property (weak, nonatomic) IBOutlet UILabel *name;
@property (weak, nonatomic) IBOutlet UILabel *message;
@property (weak, nonatomic) IBOutlet UIImageView *image;

+ (MyChatCell*) newChatCell;

@end

实施

#import "MyChatCell.h"
#import "LSObjectLoader.h"

@implementation MyChatCell

+ (LSObjectLoader*) nibLoader{
    static LSObjectLoader *_objectLoader;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _objectLoader = [[LSObjectLoader alloc] init];
    });
    return _objectLoader;
}


+ (MyChatCell *)newChatCell{
    return [[self nibLoader] loadViewFromNib:@"TableViewCells" ofClassType:[self class]];
}

- (id)initWithCoder:(NSCoder *)aDecoder{
    self = [super initWithCoder:aDecoder];
    if (self){
        // This is the init code for nib loading
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

@end

方法3自定义加载程序类

Nib文件“MyChatCells.xib”(包含“MeChat”和“ThemChat”类对象)

班级档案“MyChatCells.h / MyChatCells.m”

标头文件

#import <UIKit/UIKit.h>
#import "MeChat.h"
#import "ThemChat.h"

@interface MyChatCells : NSObject

+ (MeChat*) newMeChat;
+ (ThemChat*) newThemChat;

@end

实施档案

#import "MyChatCells.h"

@implementation MyChatCells

+ (LSObjectLoader*) nibLoader{
    static LSObjectLoader *_objectLoader;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _objectLoader = [[LSObjectLoader alloc] init];
    });
    return _objectLoader;
}

+ (MeChat*)newMeChat{
    return [[self nibLoader] loadViewFromNib:NSStringFromClass([self class])
                                 ofClassType:[MeChat class]];
}

+ (ThemChat*)newThemChat{
    return [[self nibLoader] loadViewFromNib:NSStringFromClass([self class])
                                 ofClassType:[ThemChat class]];
}

@end