从nib文件加载UIView而不需要猜测

时间:2011-03-24 02:00:50

标签: ios memory-management uiview uiviewcontroller

好的,这是另一个问题。

我正在创建一个名为ProgressView的UIView,它是一个带有活动指示器和进度条的半透明视图。

我希望能够在需要时在我的应用中的不同视图控制器中使用此视图。

我知道有3种不同的做法(但我只对其中一种感兴趣):

1)以编程方式创建整个视图,根据需要进行实例化和配置。不用担心我会得到那个。

2)在界面构建器中创建UIView,添加所需的对象并使用如下方法加载它。问题在于我们基本上猜测视图是objectAtIndex:0,因为在文档中我没有找到对[[NSBundle mainBundle] loadNibName:函数返回的元素顺序的引用。

NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:@"yournib" 
                                                 owner:self 
                                               options:nil];
UIView *myView = [nibContents objectAtIndex:0];
myView.frame = CGRectMake(0,0,300,400); //or whatever coordinates you need
[scrollview addSubview:myView];

3)子类UIViewController,让它按照正常方式管理视图。在这种情况下,我永远不会实际将视图控制器推入堆栈,而只是将其推向主视图:

ProgressViewController *vc = [[ProgressViewController alloc] initWithNibName:@"ProgressView" bundle:nil];
[vc.view setCenter:CGPointMake(self.view.center.x, self.view.center.y)];
[self.view addSubview:vc.view];
[vc release];

据我所知,#3是这样做的正确方法(除了以编程方式),但我不完全确定释放ProgressView的视图控制器是否安全,而另一个控制器的视图保留其主视图(直觉觉得它会泄漏?)?

在这种情况下,我应该在内存管理方面做些什么,应该在何时何地发布ProgressView的视图控制器?

提前感谢您的想法。

干杯,

罗格

4 个答案:

答案 0 :(得分:10)

我认为您的解决方案#3通过引入UIViewController实例作为ProgressView的容器来增加不必要的复杂性,以便您可以设置nib绑定。虽然我认为能够使用IBOutlet绑定属性而不是遍历nib内容是很好的,但您可以这样做,而无需引入您既不需要也不想要的行为的UIViewController。这应避免您对如何以及何时释放视图控制器以及它可能对响应者链或加载视图的其他行为产生的副作用(如果有)产生混淆。

相反,请重新考虑使用NSBundle并利用owner参数的强大功能。

@interface ProgressViewContainer : NSObject {

}

@property (nonatomic, retain) IBOutlet ProgressView *progressView;

@end

@implementation ProgressViewContainer

@synthesize progressView = progressView;

- (void) dealloc {
    [progressView release];
    [super dealloc];
}

@end

@interface ProgressView : UIView {

}

+ (ProgressView *) newProgressView;

@end

@implementation ProgressView

+ (ProgressView *) newProgressView {
    ProgressViewContainer *container = [[ProgressViewContainer alloc] init];
    [[NSBundle mainBundle] loadNibNamed:@"ProgressView" owner:container options:nil];

    ProgressView *progressView = [container.progressView retain];
    [container release];
    return progressView;
}

@end

创建一个名为“ProgressView”的包含ProgressView的nib,并将其File的Owner类设置为ProgressViewContainer。现在,您可以创建从笔尖加载的ProgressView。

ProgressView *progressView = [ProgressView newProgressView];
[scrollView addSubview:progressView];
[progressView release];

如果您有多个进度视图配置,那么您可能希望在ProgressView而不是-initWithNibNamed:上实现+newProgressView方法,以便您可以指定用于创建每个ProgressView实例的nib。

答案 1 :(得分:3)

我投票选择#2。来自 - [NSBundle loadNibNamed]的返回值是顶级对象的数组。因此,只要您的笔尖中只有一个顶级对象,那么索引0就是正确的。其他视图是子视图,而不是顶级对象。

另一个选择当然是为所有视图控制器创建一个超类,包括一个名为'progressView'的插座,然后将你的视图连接到nib中文件所有者的那个插座。不过,这似乎有点过头了。

答案 2 :(得分:3)

我也更喜欢替代品#2。如果“0”打扰你,你可以:

  • 创建一个名为ProgressView的UIView的子类
  • 创建一个名为ProgressView.xib的nib文件,描述您的进度视图。
  • 选择笔尖中最顶层的视图,并在接口构建器
  • 中将其Class设置为ProgressView

然后做

NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:@"ProgressView" owner:self options:nil];
ProgressView *progressView = nil;

for (UIView *view in nibContents) {
    if ([view isKindOfClass:[ProgressView class]]) {
        progressView = (ProgressView *) view;
        break;
    }
}

if (progressView != nil) {
    //Use progressView here
}

答案 3 :(得分:3)

我最终为UIView添加了一个类别:

 #import "UIViewNibLoading.h"

 @implementation UIView (UIViewNibLoading)

 + (id) loadNibNamed:(NSString *) nibName {
    return [UIView loadNibNamed:nibName fromBundle:[NSBundle mainBundle] retainingObjectWithTag:1];
 }

 + (id) loadNibNamed:(NSString *) nibName fromBundle:(NSBundle *) bundle retainingObjectWithTag:(NSUInteger) tag {
    NSArray * nib = [bundle loadNibNamed:nibName owner:nil options:nil];
    if(!nib) return nil;
    UIView * target = nil;
    for(UIView * view in nib) {
        if(view.tag == tag) {
            target = [view retain];
            break;
        }
    }
    if(target && [target respondsToSelector:@selector(viewDidLoad)]) {
        [target performSelector:@selector(viewDidLoad)];
    }
    return [target autorelease];
 }

 @end

此处的解释:http://gngrwzrd.com/blog-view-controller-less-view-loading-ios-mac.html