【问题标题】:Custom UIView subclass requires awakeFromNIB to be called twice自定义 UIView 子类需要 awakeFromNIB 被调用两次
【发布时间】:2015-01-19 22:39:44
【问题描述】:

我绝望地卡住了,我会很感激一个指针。

我们正在尝试构建一个包含多个视图的视图控制器,这些视图是 UIView 的子类。除了我们需要手动初始化视图或再次手动调用 awakeFromNib 之外,一切都“运行良好”,这是不允许的。

问题来了……

子类 UIView 头文件:

#import <UIKit/UIKit.h>
@interface introView : UIView
@property (strong, nonatomic) UIView *view;
@property (strong, nonatomic) NSString *title;
@property (strong, nonatomic) IBOutlet UILabel *titleLabel;
@end

子类 UiView 主文件:

#import "introView.h"

@interface introView ()
@end

@implementation introView

-(id)init
{
    NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"introView" owner:self options:nil];
    id mainView = [subviewArray objectAtIndex:0];
    return mainView;
}
- (void) awakeFromNib
{
    [super awakeFromNib];
    [self initialize];
    [self addSubview:self.view];
}

-(void)initialize
{
    NSLog(@"title: %@", self.title);
    self.titleLabel.text = self.title;
}

然后我们从视图控制器初始化视图:

introView *view = [[introView alloc]init];
view.title = @"Test";
[self addSubview:view];

这就是问题所在——只要调用这个视图,视图就会正确显示,但标题为 NULL;

[43605:1957090] title: (null)

如果我们再次调用 awakeFromNib,那么视图会正确初始化

在视图控制器中:

introView *view = [[introView alloc]init];
view.title = @"Test;
[self addSubview:view];
[view awakeFromNib];

然后就可以了:

2014-11-21 09:33:03.500 Test[43706:1972060] title: (null)
2014-11-21 09:33:03.500 Test[43706:1972060] title: Test

或者,如果我公开初始化方法并在初始化后调用它,它也可以工作。但这违背了我眼中的目的......

在视图控制器中:

introView *view = [[introView alloc]init];
view.title = @"Test;
[self addSubview:view];
[view initialize]; //calling the method directly...

在我看来,我们以某种方式遇到了一种情况,即视图尚未准备好,但在第二次尝试时它可以工作(即使 awakeFromNib 调用是非法的)

再次正确设置出口,设置文件所有者...只需要两次 awakeFromNib 调用。

感谢任何帮助。

----------------更新-----------------

谢谢大家,我很感激你的指点。我按照概述实现了初始化程序,但我遇到了同样的问题。此外,我使用 GitHub 示例作为一个干净的示例,并尝试为该视图分配一个变量,甚至显示相同的行为。因此,我开始认为我在其他地方做错了什么。见这里:

//
//  SubClassView.h
//  CustomView
//
//  Created by Paul Solt on 4/28/14.
//  Copyright (c) 2014 Paul Solt. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "PSCustomViewFromXib.h"

@interface SubClassView : PSCustomViewFromXib
@property (strong, nonatomic) NSString *title;
@end

子类视图.m

//
//  SubClassView.m
//  CustomView
//
//  Created by Paul Solt on 4/28/14.
//  Copyright (c) 2014 Paul Solt. All rights reserved.
//

#import "SubClassView.h"

@interface SubClassView()
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UISwitch *onSwitch;


@end

@implementation SubClassView

// Note: You can customize the behavior after calling the super method

// Called when loading programatically
- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if(self) {
        // Call a common method to setup gesture and state of UIView
        [self setup];
    }
    return self;
}

// Called when loading from embedded .xib UIView
- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if(self) {
        // Call a common method to setup gesture and state of UIView
        [self setup];
    }
    return self;
}

- (void)setup {
    // Add a gesture to show that touch input works on full bounds of UIView
    NSLog(@"%@", self.title);
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    [self addGestureRecognizer:panGesture];
}

- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture {
    NSLog(@"Pan: %@", NSStringFromCGPoint([gesture locationInView:gesture.view]));
}
- (IBAction)switchChanged:(UISwitch *)sender {
    NSLog(@"Switch: %d", sender.on);
}

@end

视图控制器类

//
//  ViewController.m
//  CustomView
//
//  Created by Paul Solt on 4/28/14.
//  Copyright (c) 2014 Paul Solt. All rights reserved.
//

#import "ViewController.h"
#import "SubClassView.h"
#import "LabelMadness.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];


    // Add a custom view's programmatically (position using 1/2 width and height)

    SubClassView *oneView = [[SubClassView alloc] init];
    oneView.center = CGPointMake(80 + 80, 282 + 40);
    oneView.backgroundColor = [UIColor blueColor];
    oneView.title = @"Test2";
    [self.view addSubview:oneView];

}

@end

-------输出-----

2014-11-21 11:49:38.893 CustomView[45653:2123668] LabelMadness.initWithFrame:
2014-11-21 11:49:38.893 CustomView[45653:2123668] (null)

我在这里做错了什么?

【问题讨论】:

  • 你的代码有点混乱。在 introView 类中,self.view 是什么(在 [self addSubview:self.view] 中)? mainView 是什么样子的(它有哪些子视图)?

标签: ios objective-c uiview


【解决方案1】:

您根本不需要使用 awakeFromNib。如果你想让 introView 加载自己的 nib,你可以这样做(在 xib 文件中将视图的类更改为 introView),

-(instancetype)init{
    if (self = [super init]) {
        NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"introView" owner:self options:nil];
        self = [subviewArray objectAtIndex:0];
    }
    return self;
}

要设置标题,你可以覆盖你的title属性的setter,

-(void)setTitle:(NSString *)title {
    _title = title;
    self.titleLabel.text = title;
}

控制器中的代码与您最初尝试的代码相同。

【讨论】:

  • 我试过了,但后来出现异常... *** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[UIView setTitle:]:无法识别的选择器发送到实例 0x7fbc4d8c4040”
  • @user3757285,听起来您没有将 xib 中的视图类更改为自定义类(这就是错误显示 UIView 不是 introView 的原因)。
  • 你是对的,但它是不正确的,但它并没有改变任何东西。我得到一个例外。但是,我开始认为我的整个方法有些缺陷。不能将全局变量分配为 init 函数的一部分吗?我是否必须调用 [view initialize] 才能获取变量?这对我来说没有意义,但它的工作原理......
  • @user3757285,我不知道为什么它不适合你。我测试了这段代码,它对我有用。如果您仍然收到相同的错误消息,那么出于某种原因,您没有创建 introView,您只是创建了一个 UIView。
  • 嗨@rdelmar,它现在可以工作了。非常感谢您的帮助!
【解决方案2】:

那么,您有 .xib 或情节提要中的视图吗?

它看起来来自 .xib,所以你应该看看 this answer

您不应该重写 init 方法,也不应该在代码中分配 init 它。如果这样做,您在界面构建器中创建的视图将不会被加载。

所以不要使用alloc init,而是按照我指出的答案做类似的事情。

无论如何,我看不到你在哪里初始化你的

@property (strong, nonatomic) UIView *view;

不是一个 IBOutlet,你永远不会在任何地方初始化它,所以显然它总是nil

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-28
    • 2014-09-17
    • 1970-01-01
    • 1970-01-01
    • 2021-07-12
    相关资源
    最近更新 更多