【问题标题】:Using Custom View in IB在 IB 中使用自定义视图
【发布时间】:2014-10-30 12:59:37
【问题描述】:

我创建了一个自定义视图,它应该出现在应用程序的多个视图控制器中。

所以...我为它创建了一个带有 xib、h 和 m 文件的 UIView 子视图(并在带有类的 xib 文件中设置视图),将我的 UI 添加到视图 xib 文件并将它们连接为IBOutlets:

然后...我将 2 个 UIViews 添加到 View Controller,将它们的 Class 设置为 MYCustomView 并将该类型的 IBOutlets 连接到 VC:

而且……它不起作用……

现在...我熟悉使用 NSBundle loadNibNamed:owner:options: 方法加载 xib 的技术。

但是...我真的希望能够直接在 IB 中设置 MYCustomView 的实例(约束、大小调整等...)。

还有其他方法可以做到这一点吗?还是应该只创建“占位符”UIView 并将我的 MYCustomView 实例添加为它们的子视图?

【问题讨论】:

    标签: ios objective-c loading iboutlet custom-view


    【解决方案1】:

    有一些 UIView 类别我总是用于自定义视图,您可以通过几个简单的步骤自己使用它:

    单击 command + N 添加一个新的 Objective-C 类别。 在头文件中添加这些行:

    // This category let the UIView that using it to load a XIB with the same name as the UIView.h & .m files easily.
    // See: MyContentWaitingSong custom view for reference.
    @interface UIView (NibLoading)
    
    + (id)loadInstanceFromNib;
    
    @end
    

    比在你的 .m 文件中添加这些行:

    // This category let the UIView that using it to load a XIB with the same name as the UIView.h & .m files easily.
    // See: MyContentWaitingSong custom view for reference.
    @implementation UIView (NibLoading)
    
    + (id)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;
    }
    
    @end
    

    你可以在自定义视图类中使用它,只需导入类别并实现一个像这样的 init 方法:

    - (id)init
    {
        self = [YourCustomClassName loadInstanceFromNib];
        if (self)
        {
            //    Your initial implementation
        }
        return self;
    }
    

    你的实现应该是这样的:

    YourCustomClassName *someName = [[YourCustomClassName alloc] init];
    someName.frame = frame;
    [self.view addSubView:someName];
    

    祝你好运。

    【讨论】:

    • 不错的答案,我使用这样的东西(但有点复杂)。唯一的问题是,这是对另一个问题的答案。
    【解决方案2】:

    我认为有两种可能的方法:

    1. 正如你提到的使用loadNibNamed:owner:options 的技术
    2. 如果您使用 Storyboard,则可以使用所谓的 容器视图。只需在情节提要中设置自定义视图的样式,使用 segue 将其连接起来,您就差不多完成了。

    对于第二种方法,您需要使用视图控制器来放入您的自定义视图,但这应该不是问题。在我看来,最好使用单独的视图控制器来放入您的视图控制器逻辑。

    【讨论】:

      【解决方案3】:

      不,您是在询问从其他笔尖引用笔尖的问题。有一些方法可以通过一些awakeAfterUsingCoder: hacks 或类似的东西来实现这一点。通常,您检查视图是否没有子视图并从 nib 加载视图,或者创建一些 IB-dummy 类,在加载后将其替换为目标类。所有这些技术都有问题,所以我建议不要使用它们。

      所以我建议从代码中加载它,不错的方法是使用视图控制器包含。

      有一个很酷的东西,不久前出现了:IBDesignable and IBInspectable,它必须在某个时候出现。

      一个例子是我在所需框架尚未准备好时使用的一些临时代码(它是一个 UIButton 虚拟 IB 子类):

      //
      // That's just an example, do not use it in production code
      //
      - (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
      {
          UIButton *buttonMain = // create button, which will replace button of dummy class from nib
          buttonMain.translatesAutoresizingMaskIntoConstraints = NO;
      
          // copy internal constraints
          [self copyInternalConstraintsToView:buttonMain];
      
          // set titles
          NSString *title = [self titleForState:UIControlStateNormal];
          [buttonMain setTitle:title forState:UIControlStateNormal];
      
          title = [self titleForState:UIControlStateHighlighted];
          [buttonMain setTitle:title forState:UIControlStateHighlighted];
      
          title = [self titleForState:UIControlStateDisabled];
          [buttonMain setTitle:title forState:UIControlStateDisabled];
      
          title = [self titleForState:UIControlStateSelected];
          [buttonMain setTitle:title forState:UIControlStateSelected];
      
          return buttonMain;
      }
      
      /**
       *  Used only in awakeAfterUsingCoder, as self has only internal constraints at that moment.
       *  If you'll try to use it somewhere else, behavior is undefined.
       *
       *  This method is straightforward and could not suit everybody's needs.
       *
       *  %Comments edited%
       *
       *  @param replacementView view to add constraints to
       */
      - (void)copyInternalConstraintsToView:(UIView *)replacementView
      {
          for (NSLayoutConstraint* constraint in self.constraints)
          {
              if([constraint isMemberOfClass:[NSLayoutConstraint class]])
              {
                  id first = constraint.firstItem;
                  id second = constraint.secondItem;
                  id newFirst = first;
                  id newSecond = second;
      
                  BOOL match = NO;
                  if (first == self)
                  {
                      newFirst = replacementView;
                      match = YES;
                  }
                  if (second == self)
                  {
                      newSecond = replacementView;
                      match = YES;
                  }
      
      
                  // constraints to subviews from recource are not supported
                  if(newFirst != nil && newFirst != replacementView)
                  {
                      match = NO;
                  }
      
                  if(newSecond != nil && newSecond != replacementView)
                  {
                      match = NO;
                  }
      
                  if (match)
                  {
                      @try
                      {
                          NSLayoutConstraint* newConstraint = nil;
                          newConstraint = [NSLayoutConstraint constraintWithItem:newFirst
                                                                       attribute:constraint.firstAttribute
                                                                       relatedBy:constraint.relation
                                                                          toItem:newSecond
                                                                       attribute:constraint.secondAttribute
                                                                      multiplier:constraint.multiplier
                                                                        constant:constraint.constant];
                          newConstraint.shouldBeArchived = constraint.shouldBeArchived;
                          newConstraint.priority = constraint.priority;
      
                          [replacementView addConstraint:newConstraint];
                      }
                      @catch (NSException *exception)
                      {
                          NSLog(@"Constraint exception: %@\nFor constraint: %@", exception, constraint);
                      }
                  }
              }
          }
      }
      

      一些链接: Embedding custom-view Nibs in another Nib: Towards the holy grail, An Update on Nested Nib Loading

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-10
        • 2012-10-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多