在使用故事板时,自定义UIView中的IBOutlet为nil。

23
我有一个自定义的UIView类。在里面,我声明了一个UIImageViewIBOutlet属性。
#import <UIKit/UIKit.h>

@interface SettingItem : UIView{

}

@property (strong, nonatomic) IBOutlet UIImageView *myImage;

@end

现在我正在使用Storyboard(故事板)。有一个视图控制器(viewcontroller)。我将UIView拖到了视图控制器中,并将一个UIImageView作为以上UIView的子视图。我从Storyboard中将“SettingItem”类设置为UIView,然后通过在 utilities 窗口中从 SettingItem 的outlet中进行正常拖动,将outlet与myImage连接起来。


SettingItem 实现

#import "SettingItem.h"

@implementation SettingItem

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        [self baseInit];
    }
    return self;
}

-(id) initWithCoder:(NSCoder *)aDecoder{
    self = [super initWithCoder:aDecoder];
    if (self) {
        // Initialization code
        [self baseInit];
    }
    return self;
}

- (void) baseInit{
    NSLog(@"myImage %@"self.myImage);
}

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

@end

现在我的问题是myImage始终为nil,上面的NSLog仅为该outlet打印(null)。我检查了storyboard中的视图并检查了其outlet引用,它指向myImage。我错过了什么。我在谷歌上搜索了帮助,但找不到任何解决方法。

请问您能指出我做错了什么吗?


为什么将IBOutlet属性声明为strong而不是weak?这是因为它是UIView本身的属性,所以它是该属性的唯一所有者吗?谢谢。 - Pablo A.
@PabloA。是的,它是唯一的所有者。而且当时的Xcode IB默认使用强引用绑定,所以你需要重新编辑为弱引用或根据手头的情况加以处理。 - padam thapa
7个回答

80

在你的视图中重写awakeFromNib方法——这是在IBOutlets被分配后第一个被调用的生命周期方法,也是你的代码最合适的位置。


6
简短版:这是唯一正确的答案,其他答案应该被包装起来并送到太空中。长版:哦,当我在 Stack Overflow 上提问题的时候,我多么希望有像 @Oly 这样能提供唯一合理和正确答案的人。其他答案太垃圾了,它们只是证明了缺乏必要的知识。请变得更像 Oly,而不是这个主题中的其余部分。 - MANIAK_dobrii
2
@MANIAK_dobrii 你刚刚让我开心了起来,去年发生了一场可怕的事故后,我刚回到工作岗位,需要一些鼓励;-) - Oly Dungey
6
在awakeFromNib中,它仍然获取到null值,我在stackoverflow上搜索了很多东西,得到的答案是内容是惰性加载,所以在awakeFromNib中,你可能会有iboutlet的值为空。 - Mehul Thakkar

4

在awakeFromNib中初始化它们:

- (void)awakeFromNib
{
    //init code
}

2
我刚遇到了完全相同的问题,解决方法非常简单:
你访问了myImage太早了 - 就是这样。
-(id)initWithCoder:(NSCoder *)aDecoder{- (id)initWithFrame:(CGRect)frame之间,UIView尚未绘制。因此,myImage尚未初始化。
如果您添加一个UIButton和一个IBAction并执行以下操作,则可以进行测试:
- (IBAction)buttonClicked:(id)sender {
    NSLog(@"%@",myImage);
}

你会看到myImage不再是nil了。


0

我曾经遇到过类似的问题,但最终找出了问题所在——XIB文件和Storyboards之间的习语差异。

在我看过的所有使用Xib文件的教程中,如果我有一个UITableViewController创建了一个DetailViewController来让我编辑表中项目的内容,那么tableViewController会在第一次显示它时创建DVC实例,但随后每当需要编辑列表中的另一个项目时,都会重复使用同一个DVC实例。

而对于storyboards,似乎每次表视图调用它时都会创建一个新的视图(并且不会调用UIViewController模板中的init版本)。正如之前的答案所述,即使您之前已经显示过此视图,也必须等到“ViewDidLoad”才能访问任何控件。

希望这可以帮到你。


0

你能不能不使用属性的setter,这样当它实际被填充时,你就可以做你需要做的事情了?

@IBOutlet weak var xxx:UIKitClass! {
  didSet { 
    outletSetup() 
  }
}

-4
-(void)viewWillAppear:(BOOL)animated内部调用[self baseInit]。此时myImage应该有一个值。
#import "SettingItem.h"

@implementation SettingItem

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code

    }
    return self;
}

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

    }
    return self;
}
-(void)viewWillAppear:(BOOL)animated
{
   [self baseInit];
}

- (void) baseInit{
    NSLog(@"myImage %@"self.myImage);
}

3
viewWillAppear不属于UIView,它属于UIViewController。 - padam thapa

-6

在你的 .h 文件中,你需要替换你的代码:

#import <UIKit/UIKit.h>

@interface SettingItem : UIView{

}

@property (strong, nonatomic) IBOutlet UIImageView *myImage;

@end

使用这段代码

#import <UIKit/UIKit.h>

@interface SettingItem : UIView{

  IBOutlet UIImageView*myImage;

}

@end

实际上你并没有告诉Xcode myImage 是什么,你只是让它成为了一个强引用的非原子属性


那我应该如何从Storyboard连接到它的Outlet,因为该属性不再可见了? - padam thapa
如果您单击视图的状态栏,然后转到连接检查器,然后将其链接。 - OnkaPlonka
@OnkaPlonka,你错了。在@property声明中的IBOutlet确实意味着它在UIStoryBoard中可用。你不应该再使用@interface Class : SuperClass { IBOutlet ... }这种方式了。 - Fabio Poloni
这两个声明在其ivar存储方面是等效的(模块ivar的名称:在第一个声明中,属性将被自动合成为“_myImage”,而第二个声明命名为“myImage”),但更重要的是,这与所提问的问题无关。 - cbowns
但是你没有回答他的问题。 - lagos
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接