在笔尖内加载笔尖

时间:2009-08-18 22:25:36

标签: iphone objective-c

我是接口构建器的新手,我希望有一个包含UIView的3x3网格的屏幕,每个网格都包含一个UIImageView和4个UILabels。

我的逻辑可能存在缺陷,但我试图实现这一目标的方式是:

  1. 使用imageView和4个标签在IB中创建一个UIView Nib文件MyUIView.nib和布局
  2. 创建一个名为MyUIView.m的UIView子类,其中包含1个IBOutlet UIImageView和4个IBOutlet UILabel。将MyUIView.nib和MyUIView.m链接为文件所有者并连接出口。
  3. 然后创建另一个nib MyGridViewController.nib,其中包含9个MyUIView,位于3x3网格中。
  4. 创建一个UIViewController,它有9个IBOutlet MyUIView并通过Interface Builder连接它们。
  5. 是否可以从InterfaceBuilder中以图形方式将nib加载到另一个nib中,如果是这样,我该怎么办?我是否将“标准”UIView拖到画布上然后将类更改为MyUIView?

    或者我是否需要在MyGridViewController.m中以编程方式执行此操作,如下所示:

    for (int i=0; i<9; i++)
    {
        NSArray* nibViews =  [[NSBundle mainBundle] loadNibNamed:@"MyUIView" owner:self options:nil];
       [myViewArray addObject:[ nibViews objectAtIndex: 1]];
    }
    

    我让它工作的唯一另一种方法是使用一个Nib并放置9个UIImageViews和36个UILabel但是当我想要更改某些内容时这显然很痛苦,因为我需要更新每个3x3“单元格”。我认为在一个文件中更改它会更容易,所有9个都会更新。

6 个答案:

答案 0 :(得分:3)

您无法在Interface Builder中执行此操作。

我可能会做的是创建一个自定义视图,比如MyGridItem,它在唤醒时将MyUIView.nib加载为子视图,然后在MyGridView.nib中使用其中的9个。

小心使用awakeFromNib,因为如果视图涉及加载两个不同的笔尖(例如,如果MyGridItem是加载MyGridView.nib时的所有者,那么它可以被调用两次),那么当它是MyGridItem时,将调用一次awakeFromNib加载作为加载MyUIView.nib的一部分,并在加载MyGridView.nib时加载。

此外,由于您正在加载笔尖9次,您可能需要使用

缓存笔尖一次
NSNib* theNib = [[NSNib alloc] initWithNibNamed:@"MyGridItem" bundle:nil];

加载:

if ( [theNib instantiateNibWithOwner:self topLevelObjects:NULL] ) {

然后,您可能希望在加载所有九个子视图后解除分配。

答案 1 :(得分:2)

“是的你(差不多)可以。”

我是在使用Interface Builder的项目中完成的。

唯一的缺陷是你在Interface Builder中看到一个白色区域来表示'嵌套的笔尖'。让我们说,在同一时间(我主要等待Apple在XCode中添加此功能),我在这里提出的解决方案是可以接受的。

首先阅读:https://blog.compeople.eu/apps/?p=142

然后,如果您执行ARC,请按照这些说明操作并获取此处包含的UIVIew + Util类别。

对于ARC,您必须允许此“自我”分配。 (https://blog.compeople.eu/apps/?p=142声明它不需要,但确实如此。如果你不这样做,你会得到一些'消息发送到解除分配的实例')

要在ARC项目中实现此目的,请在文件中添加“-fno-objc-arc”标志编译器设置。 然后在这个文件中进行NO-ARC编码(比如dealloc设置nils,调用super dealloc等等。)

此外,客户端nib的viewcontroller应该使用strong属性来保存awakeFromNib返回的实例。对于我的示例代码,customView的引用如下:


@property(强大,非原子)IBOutlet CustomView * customView;


我最后使用 UIView + Util 类别中定义的 copyUIPropertiesTo: loadNibNamed 在属性处理和nib加载方面添加了一些其他改进。< / p>

所以 awakeAfterUsingCoder:代码现在

#import "UIView+Util.h"
...
- (id) awakeAfterUsingCoder:(NSCoder*)aDecoder
{
    // are we loading an empty “placeholder” or the real thing?
    BOOL theThingThatGotLoadedWasJustAPlaceholder = ([[self subviews] count] == 0);

    if (theThingThatGotLoadedWasJustAPlaceholder)
    {
        CustomView* customView = (id) [CustomView loadInstanceFromNib];
        // copy all UI properties from self to new view!
        // if not, property that were set using Interface buider are lost!
        [self copyUIPropertiesTo:customView];

        [self release];
        // need retain to avoid deallocation
        self = [customView retain];
    }
    return self;
}

UIView + Util类别代码是

@interface UIView (Util)
   +(UIView*) loadInstanceFromNib;
   -(void) copyUIPropertiesTo:(UIView *)view;
@end

及其实施

#import "UIView+Util.h"
#import "Log.h"

@implementation UIView (Util)

+(UIView*) loadInstanceFromNib
{ 
    UIView *result = nil; 
    NSArray* elements = [[NSBundle mainBundle] loadNibNamed: NSStringFromClass([self class]) owner: nil options: nil];
    for (id anObject in elements)
    { 
        if ([anObject isKindOfClass:[self class]])
        { 
            result = anObject;
            break; 
        } 
    }
    return result; 
}

-(void) copyUIPropertiesTo:(UIView *)view
{
    // reflection did not work to get those lists, so I hardcoded them
    // any suggestions are welcome here

    NSArray *properties =
    [NSArray arrayWithObjects: @"frame",@"bounds", @"center", @"transform", @"contentScaleFactor", @"multipleTouchEnabled", @"exclusiveTouch", @"autoresizesSubviews", @"autoresizingMask", @"clipsToBounds", @"backgroundColor", @"alpha", @"opaque", @"clearsContextBeforeDrawing", @"hidden", @"contentMode", @"contentStretch", nil];

    // some getters have 'is' prefix
    NSArray *getters =
    [NSArray arrayWithObjects: @"frame", @"bounds", @"center", @"transform", @"contentScaleFactor", @"isMultipleTouchEnabled", @"isExclusiveTouch", @"autoresizesSubviews", @"autoresizingMask", @"clipsToBounds", @"backgroundColor", @"alpha", @"isOpaque", @"clearsContextBeforeDrawing", @"isHidden", @"contentMode", @"contentStretch", nil];

    for (int i=0; i<[properties count]; i++)
    {
        NSString * propertyName = [properties objectAtIndex:i];
        NSString * getter = [getters objectAtIndex:i];

        SEL getPropertySelector = NSSelectorFromString(getter);

        NSString *setterSelectorName =
            [propertyName stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[propertyName substringToIndex:1] capitalizedString]];

        setterSelectorName = [NSString stringWithFormat:@"set%@:", setterSelectorName];

        SEL setPropertySelector = NSSelectorFromString(setterSelectorName);

        if ([self respondsToSelector:getPropertySelector] && [view respondsToSelector:setPropertySelector])
        {
            NSObject * propertyValue = [self valueForKey:propertyName];

            [view setValue:propertyValue forKey:propertyName];
        }
    }    
}

Tadaaaa: - )

积分转到https://stackoverflow.com/users/45018/yang以获得初始解决方案。我刚改进了它。

答案 2 :(得分:1)

如果使用1个图像视图创建视图,并在笔尖中创建4个标签(我将此视图称为“网格单元格”),则可以为网格创建另一个笔尖并拖动网格单元格的9个实例。 9个实例仅在IB中显示为占位符(空白视图),但它们在您运行应用程序时将起作用。

This article告诉你该怎么做。查看“可重用子视图”一节。而且由于您正在制作一个包含9个相同单元格的网格,因此使用AQGridView可能是有意义的 - 请参阅“可重用的AQGridViewCell”部分。

答案 3 :(得分:0)

AFAIK,无法在NIB中加载NIB。在您的情况下,我要做的是在MyUIView中以编程方式添加UILabels。

答案 4 :(得分:0)

这是一种更有活力的方式来做这样的事情。

我真的不想拉入笔尖并在我想要在代码中使用它们的任何地方筛选它们,看起来很乱。此外,我希望能够在界面构建器中添加它们。

1)创建UIView的自定义子类(让我们称之为myView) 2)创建你的笔尖并将其命名为myViewNIB

添加这两个方法(将它们放在超类UIView和子类中会更聪明)

- (UINib *)nib {
    NSBundle *classBundle = [NSBundle bundleForClass:[self class]];
    return [UINib nibWithNibName:[self nibName] bundle:classBundle];
}

- (NSString *)nibName {
    NSString *className = NSStringFromClass([self class]);
    return [NSString stringWithFormat:@"%@NIB", className];
}

所谓的魔法就是那里的最后一行,它通过将NIB附加到当前类来返回一个nibName。

然后为你的init方法,即initWithCoder,因为你想在界面构建器中使用它(你可以使它更健壮,所以它也可以通过设置initWithFrame以编程方式使用):

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        // Initialization code
        NSArray *nibObjects = [[self nib] instantiateWithOwner:nil options:nil];

        UIView *view = [nibObjects objectAtIndex:0];
        [self addSubview:view];

    }
    return self;
}

因此,这要求myViewNIB的第一个(也可能是唯一的)成员是一个视图。这不会给你带来的是一种在笔尖内以编程方式设置标签的方法,至少不容易。如果没有循环浏览视图并查找标签,我不确定你会怎么做。

反正。此时,您可以在IB中拖出一个新的UIView,并将该类设置为myView。 myView将自动搜索我们关联的myViewNIB.xib并将其视图添加为子视图。

似乎应该有更好的方法来做到这一点。

答案 5 :(得分:0)

我所做的是:

1)对于拥有视图,我将子视图作为空视图放入,并将自定义视图的类名作为占位符。这将创建自定义视图,但不会创建其子视图或连接,因为这些是在自定义视图的nib中定义的。

2)然后,我使用其笔尖手动加载自定义视图的新副本,并替换父级中的占位符自定义视图。

使用:

    NSView *newView = (Code to load the view using a nib.)
    newView.frame = origView.frame;
    NSView *superView = origView.superview;
    [superView replaceSubview:origView with:newView];

唉!