Ios 无需猜测即可从nib文件加载UIView

Ios 无需猜测即可从nib文件加载UIView,ios,memory-management,uiview,uiviewcontroller,Ios,Memory Management,Uiview,Uiviewcontroller,好的,还有一个问题 我正在创建一个名为ProgressView的UIView,它是一个半透明的视图,带有活动指示器和进度条 如果需要,我希望能够在我的应用程序中的不同视图控制器中使用此视图 我知道3种不同的方法(但我只对其中一种感兴趣): 1) 以编程方式创建整个视图,根据需要实例化和配置。不用担心,我有这个 2) 在interface builder中创建UIView,添加所需的对象,并使用如下方法加载它。问题是我们基本上猜测视图是objectAtIndex:0,因为在文档中我没有找到对从[[

好的,还有一个问题

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

如果需要,我希望能够在我的应用程序中的不同视图控制器中使用此视图

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

1) 以编程方式创建整个视图,根据需要实例化和配置。不用担心,我有这个

2) 在interface builder中创建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的视图控制器是否安全(gut feel说它会泄漏?)

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

提前感谢您的想法

干杯

Rog

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


当然,另一种选择是为所有视图控制器创建一个超类,其中包括一个名为“progressView”的出口,然后将视图连接到nib中文件所有者的该出口。不过,这似乎有些过分。

我认为您的解决方案#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”的nib,其中包含ProgressView,并将其文件的所有者类设置为ProgressViewContainer。现在您可以创建从nib加载的ProgressView

ProgressView *progressView = [ProgressView newProgressView];
[scrollView addSubview:progressView];
[progressView release];
如果您有多个进度视图配置,那么您可能希望在ProgressView上实现
-initWithNibNamed:
方法,而不是
+newProgressView
,这样您可以指定使用哪个nib来创建每个ProgressView实例。

我也更喜欢备选方案2。如果“0”困扰您,您可以:

  • 创建名为ProgressView的UIView子类
  • 创建一个名为ProgressView.xib的nib文件,描述您的进度视图
  • 选择nib中最顶层的视图,并在interface builder中将其类设置为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
}

我最终在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

此处的解释:

我可以评论一下,如果您将所有者设置为代码中所示的self,我相信您仍然会收到错误。如果我错了,请纠正我,但我能够实现这一点的唯一方法是将所有者设置为“容器”对象。嘿,这对我有用,谢谢。我会说这有点奇怪,不完全是直观的-例如,我第一次忘了连接IBOutlet。看起来苹果应该让这个功能更容易访问。