在IB中使用自定义视图

时间:2014-10-30 12:59:37

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

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

所以...我已经用它的xib,h和m文件创建了UIView的子视图(并使用类在xib文件中设置了View),将我的UI添加到视图xib文件并将它们连接为IBOutlets:

enter image description here

enter image description here

enter image description here

然后......我向视图控制器添加了2个UIViews,将它们的类设置为MYCustomView并将该类型的IBOutlet连接到VC:

enter image description here

enter image description here

enter image description here

而且......它不起作用......

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

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

还有其他办法吗?或者我应该创建'占位符'UIViews并将我的MYCustomView实例添加为他们的子视图?

3 个答案:

答案 0 :(得分:2)

我总是使用一些UIView类别来自定义视图,你可以自己使用它,只需几个简单的步骤:

单击命令+ 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];
祝你好运。

答案 1 :(得分:1)

不,你问一下从其他笔尖引用笔尖。有一些方法可以通过一些awakeAfterUsingCoder:黑客或类似的东西实现这一目标。通常你检查视图是否没有子视图并从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 grailAn Update on Nested Nib Loading

答案 2 :(得分:1)

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

  1. 如您所述,请使用loadNibNamed:owner:options
  2. 的技巧
  3. 如果您使用的是Storyboards,则可以使用所谓的容器视图。只需在故事板中设置自定义视图的样式,使用segue连接它就可以了。
  4. 对于第二种方法,您需要使用视图控制器来放入自定义视图,但这应该是一个问题。在我看来,使用单独的视图控制器放入视图控制器逻辑会更好。