10

我知道这很简单,但是在拉扯太多头发之后,我离解决方案还差得很远。

我已经看到了解释如何使用 XIB 和所有创建视图的教程。但他们都没有解决我在这里遇到的情况。

我有一个 XIB 文件,一个自定义 UIView 子类,标签和按钮很少。UIView 子类是可重用的,这就是我在任何单个 View 控制器中都不能有插座的原因。结果,我将此视图的各个控件(子视图)存储在我的自定义 UIView 本身中。这是合乎逻辑的,因为任何视图控制器都不应拥有此自定义视图的子视图,该自定义视图将包含在每个视图控制器中。

问题是,我不知道如何完全初始化整个 UI。

这是我的 UIView 子类代码:

@interface MCPTGenericView : UIView

+(id)createInstance : (bool) bPortrait;

@property (weak, nonatomic) IBOutlet UIView *topView;
@property (weak, nonatomic) IBOutlet UIView *titleView;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UIButton *logoButton;
@property (weak, nonatomic) IBOutlet UITextField *searchTextField;
@property (weak, nonatomic) IBOutlet UIButton *menuButton;
@end

稍后,我还计划将此相同的 XIB 文件也用于此 UIView 的横向,并且我计划在同一 XIB 中使用与横向控件相同的上述插座。

这是实现:

@implementation MCPTGenericView
//@synthesize topView, titleLabel, titleView;

+(id)createInstance : (bool) bPortrait
{
    UIView * topLevelView = nil;
    MCPTGenericView * instance = [MCPTGenericView new];
    NSArray * views = [[NSBundle mainBundle] loadNibNamed:@"MoceptGenericView" owner:instance options:nil];

    int baseTag = (bPortrait)?PORTRAIT_VIEW_TAG_OFFSET:LANDSCAPE_VIEW_TAG_OFFSET;

    // make sure customView is not nil or the wrong class!

   for (UIView * view in views)
   {

       if (view.tag == baseTag)
       {
           topLevelView = view;
           break;
       }
   }

    instance.topView = (MCPTGenericView *)[topLevelView viewWithTag:baseTag + 1];
    instance.searchTextField = (UITextField *)[topLevelView viewWithTag:baseTag + 2];
    instance.menuButton = (UIButton *)[topLevelView viewWithTag:baseTag + 3];
    instance.logoButton = (UIButton *)[topLevelView viewWithTag:baseTag + 4];
    instance.titleView = [topLevelView viewWithTag:baseTag + 5];
    instance.titleLabel = (UILabel *)[topLevelView viewWithTag:baseTag + 6];

    return instance;
}

-(id)initWithCoder:(NSCoder *)aDecoder
{
    if ((self = [super initWithCoder:aDecoder]))
    {
        [self addSubview:[[[NSBundle mainBundle] loadNibNamed:@"MCPTGenericView" owner:self options:nil] objectAtIndex:0]];
    }
    return self;
}

-(void)awakeFromNib
{
    [super awakeFromNib];
    [self addSubview: self.titleView];
    [self addSubview:self.topView];
}

- (id) init
{
    self = [super init];
    if (self)
    {
        [[NSBundle mainBundle] loadNibNamed:@"MCPTGenericView" owner:self options:nil];
        [self addSubview:self.topView];
        [self addSubview:self.titleView];

    }
    return self;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        // Initialization code
         [[NSBundle mainBundle] loadNibNamed:@"MCPTGenericView" owner:self options:nil];
        [self addSubview:self.topView];
        [self addSubview:self.titleView];
    }
    return self;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
*/

@end

有用的东西:

我成功地从我的视图控制器调用initWithFrame:frame。这样,我可以看到所有控件都已正确初始化。但是,如果我已经绘制了 XIB,为什么还要提供框架呢?loadNibNamed 不应该处理框架设置和布局的东西,因为这是 XIB 的预期用途?

我也对 loadNibNamed 需要所有者对象的方式感到困惑。为什么我们已经需要一个对象来从 XIB 获取相同的对象?那也是半生不熟的吗?

请帮忙...

4

2 回答 2

19

令我困惑的是loadnibnamed丢失 xib 布局和出口信息的方式。我终于找到了实现它的方法。

以下是有效方法的回顾:

1)假设 MyCustomView 是您的自定义视图类 - 您将其及其子视图设计为 XIB 的一部分。您可以通过界面生成器执行此操作,因此不言自明。

2) 通过 Xcode -> File -> New -> Objective C Class 添加 MyCustomView.h 和 MyCustomView.m (boilerplate)。

3) 接下来,在 MyCustomView.xib 中,设置 File's Owner = MyCustomView(刚刚添加的类名)。不要触摸最顶层 View 的自定义类 - 将其保留为 UIView。否则它将以递归结束!!!

4) 在 MyCustomView.h 中,创建几个与 MyCustomView.xib 中的子视图对应的 outlet。

如:

@property (weak)  IBOutlet UILabel * label1;
@property (weak)  IBOutlet UIButton * button1;

5) 转到 MyCustomView.xib。选择每个子视图(标签,按钮),右键单击,从“新引用插座”拖动并向上拖动到文件所有者。

这将弹出一个与您拖动的子视图类型匹配的插座列表。如果从标签中拖动,会弹出label1,以此类推。这表明您在此步骤之前所做的一切都是正确的。

另一方面,如果您在任何步骤中搞砸了,则不会出现弹出窗口。检查步骤,尤其是 3 和 4。

如果您没有正确执行此步骤,Xcode 将欢迎您将出现以下异常:

setValue:forUndefinedKey: this class is not key value coding-compliant for the key

6) 在您的 MyCustomView.m 中,粘贴/覆盖以下代码:

-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];

    if (self)
    {
        NSString * nibName = @"MyCustomView";
        [[[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil] firstObject];

        [self addSubview:self.labelContinentName];
    }
    return self;
}

这一步至关重要 - 它将您的出口值(label1,button1)从 nil 设置为有形的子视图,最重要的是,根据您在 MyCustomView.xib 中设置的内容设置它们的框架

7) 在您的故事板文件中,添加 MyCustomView 类型的视图 - 就像任何其他视图一样:

  • 在 View Controller 主视图矩形中拖动 UIView
  • 选择新添加的视图
  • 在 Utilities -> Identity Inspector 中,设置自定义类值 = MyCustomView。

它应该启动并运行没问题!

于 2014-04-05T11:47:55.657 回答
0

loadNibNamed不处理框架设置,它只加载内容并使对象可用于您的代码。initWithFrame:必须调用才能将新对象插入到窗口的视图层次结构中。

于 2013-12-02T08:44:11.097 回答