【问题标题】:Loading UIView from a nib file without guesswork无需猜测即可从 nib 文件加载 UIView
【发布时间】:2011-07-21 19:12:28
【问题描述】:

好的,这是另一个问题。

我正在创建一个名为 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 的视图控制器?

提前感谢您的想法。

干杯,

罗格

【问题讨论】:

    标签: ios memory-management uiview uiviewcontroller


    【解决方案1】:

    我认为您的解决方案 #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,并将其 File's Owner 类设置为 ProgressViewContainer。现在您可以创建从您的 nib 加载的 ProgressView。

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

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

    【讨论】:

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

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

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

    【讨论】:

      【解决方案3】:

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

      • 创建一个名为 ProgressView 的 UIView 子类
      • 创建一个名为 ProgressView.xib 的 nib 文件来描述您的进度视图。
      • 选择 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
      }
      

      【讨论】:

        【解决方案4】:

        我最终为此向 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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-05-05
          • 1970-01-01
          • 1970-01-01
          • 2011-02-08
          • 1970-01-01
          • 2014-09-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多