【问题标题】:Loading a Nib within a Nib在笔尖中装入笔尖
【发布时间】:2009-08-18 22:25:35
【问题描述】:

我是界面构建器的新手,我想要一个屏幕,其中包含一个 3x3 网格的 UIView,每个网格包含一个 UIImageView 和 4 个 UILabel。

我的逻辑可能有缺陷,但我试图实现这一点的方法是:

  1. 创建一个 UIView Nib 文件 MyUIView.nib 并在 IB 中使用 imageView 和 4 个标签进行布局
  2. 创建一个名为 MyUIView.m 的 UIView 子类,其中包含 1 个 IBOutlet UIImageView 和 4 个 IBOutlet UILabel。将 MyUIView.nib 和 MyUIView.m 链接为文件所有者并连接插座。
  3. 然后创建另一个 nib MyGridViewController.nib,其中包含 9 个 MyUIView,以 3x3 网格布局。
  4. 创建一个具有 9 个 IBOutlet MyUIView 的 UIViewController 并通过 Interface Builder 连接它们。

是否可以从 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 个 UILabels 但这显然是当我想要更改某些内容时很痛苦,因为我需要更新每个 3x3“单元格”。我认为在一个文件中更改它会更容易,并且所有 9 个都会更新。

【问题讨论】:

    标签: iphone objective-c


    【解决方案1】:

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

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

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

    此外,由于您要加载 nib 9 次,因此您可能希望使用一次缓存 nib

    NSNib* theNib = [[NSNib alloc] initWithNibNamed:@"MyGridItem" bundle:nil];
    

    加载它:

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

    您可能希望在加载所有九个子视图后释放它。

    【讨论】:

      【解决方案2】:

      “是的,你(几乎)可以。”

      我在我的项目中使用 Interface Builder。

      唯一的缺陷是您在 Interface Builder 中看到一个表示“嵌套 nib”的白色区域。假设同时(我主要等待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 的视图控制器应该使用 strong 属性来保存 awakeFromNib 返回的实例。在我的示例代码中,customView 是这样引用的:


      @property (strong, nonatomic) IBOutlet CustomView* customView;


      我终于使用在我的 UIView+Util 类别中定义的 copyUIPropertiesTo:loadNibNamed 对属性处理和 nib 加载进行了一些其他改进。

      所以 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 以获得初始解决方案。我刚刚改进了它。

      【讨论】:

      • 在使用这种技术时需要注意的是,对于 customView(即同一个实例),awakeFromNib 将被调用两次。如果您的实现覆盖它,则需要考虑这一点。
      • 这种方法似乎不可靠。如果 Apple 在未来的 iOS 版本中添加更多 UIView 属性会怎样?或者改个名字?
      • @ettore 好点。第一点可以通过反思来解决。其次,我认为方法可能会被“弃用”,但不会被删除。这是更改方法名称/签名时的常见做法,因此旧代码仍然有效。
      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          这是一种更动态的方式来做这样的事情。

          我真的不想拉入笔尖并在我想在代码中使用它们的任何地方筛选它们的对象,这看起来很混乱。另外,我希望能够将它们添加到界面生成器中。

          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,并将class设置为myView。 myView 将自动搜索我们关联的 myViewNIB.xib 并将其视图添加为子视图。

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

          【讨论】:

          • 是否有官方文档说要获取笔尖名称,您可以使用格式“NIB”?
          • 没有@ettore。我不确定我是否会再推荐这样做。
          【解决方案6】:

          我所做的是:

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

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

          用途:

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

          啊!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-06-08
            • 1970-01-01
            • 1970-01-01
            • 2011-08-11
            相关资源
            最近更新 更多