如何删除特定的子视图?

21

我在我的ViewController中添加了一个子视图(ViewController):

Location *location = [[Location alloc] initWithNibName:@"Location" bundle:[NSBundle mainBundle]];
[self.subView addSubview:location.view];

如何删除此子视图?

我知道删除所有子视图的方式是:

for (UIView *subview in [self.view subviews]) {

  [subview removeFromSuperview];

}

你不应该像那样将另一个视图控制器的视图添加到现有视图控制器的视图中。 - Paul.s
我无法删除那个添加的子视图吗? - iWizard
你当然可以这样做,但我只是在观察一个潜在的痛点和许多棘手错误的根源。如果你想要移除特定的视图,为什么不创建一个 ivar,它将给你一个对 location 视图控制器或 view 本身的引用,这样你就不需要遍历子视图了... - Paul.s
请您能否详细解释一下,因为我不太理解。或者给我一个示例链接? - iWizard
3个回答

51

快速简单方法:给你的视图添加一个标签,以便稍后可以识别它:

Location *location = [[Location alloc] initWithNibName:@"Location" bundle:[NSBundle mainBundle]];
UIView *viewToAdd = location.view;
viewToAdd.tag = 17; //you can use any number you like
[self.view addSubview:viewToAdd];

然后,要进行删除:

UIView *viewToRemove = [self.view viewWithTag:17];
[viewToRemove removeFromSuperview];

一种更加简洁、快速、易于阅读和维护的替代方案是创建一个变量或属性来访问视图:

在接口中:

@property (nonatomic, weak) UIView *locationView;

在实现中:
Location *location = [[Location alloc] initWithNibName:@"Location" bundle:[NSBundle mainBundle]];
UIView *viewToAdd = location.view;
self.locationView = viewToAdd;
[self.view addSubview:viewToAdd];

然后,要移除:

[self.locationView removeFromSuperview];

话虽如此,评论者警告说要小心在其他 ViewControllers 的 Views 上操作。如果你想这么做,请了解一下 ViewController 包含的知识。


viewWithTag:= 呃。如果您需要一个视图句柄,为什么不直接给自己一个适当的句柄,而不是浏览视图层次结构并询问任何视图以获取任意数字,这些数字任何人都可以意外更改。 - Paul.s
2
因为这个回答解决了提问者的一般性问题,而不需要引入iVars或改变除这两个调用之外的任何其他内容。我很少标记我的视图(如果有的话,主要是为了调试),但在这里它终于有了用处。 - fzwo
那只会鼓励糟糕的设计...(还有当你回复别人时,请使用@功能,否则他们将无法收到通知,可能永远不会知道你已经回复了他们...) - Paul.s
@Paul.s 我同意这不是很好,但我认为你的帖子没有很好地解释什么(以及为什么),不能鼓励更好的设计(难道你没有收到通知吗?因为你的评论就在我的下面,而且你还评论了我的帖子?我承认我忘记了)。 - fzwo
不,这听起来有点傻,但只有在您评论我的问题/答案或使用@myName时,我才会收到通知。啊,我假设如果OP不太理解某些东西,他们会查阅资料,这是最好的学习方式。您可以始终通过添加常量而不是魔术数字来提高您答案的标准;)至少这样,我会觉得稍微好一点。 - Paul.s
@Paul.s 我明白,但我想尽可能保持简短易懂(这就是为什么我插入了不必要的本地变量来表示视图)。数字的常量或定义会增加混淆,我担心。 - fzwo

7

创建一个 ivar,它可以为你提供对新视图控制器的引用,也可以只是视图。我会选择在这里使用视图控制器。

添加一个属性并合成它。

// .h
@property (nonatomic, strong) Location *location;

// .m
@synthesize location = _location;

现在,当您创建位置时,请设置实例变量。
Location *location = [[Location alloc] initWithNibName:@"Location" bundle:[NSBundle mainBundle]];
self.location = location;

[self.subView addSubview:location.view];

现在稍后删除它。
[self.location.view removeFromSuperview];

顺便说一下

通常将一个视图控制器的视图添加到另一个视图中会是一条痛苦的路。如果想要了解更多相关内容,请参考Abusing UIViewControllers

你所命名的Location可能并不优秀,更合适的名称可能是LocationViewController或类似的名称。这样的一致性命名方式可以让其他人(或未来的你)能够轻松地阅读和理解这是一个视图控制器,而无需打开头文件。


关于命名(以及ViewControllers)的观点非常好。这个解决方案比我下面的更清晰。 - fzwo

2

你可以给视图设置一个唯一的标识符,以便进行识别。当你想要移除这个视图时,使用viewWithTag:(NSInteger)tag方法获取它并仅删除此视图。

Location *location = [[Location alloc] initWithNibName:@"Location" bundle:[NSBundle mainBundle]];
location.tag = 8001; // 8001 is an exemple
[self.subView addSubview:location.view];

然后

UIView * v = [self.subView viewWithTag:8001];
if (nil != v) {
    [v removeFromSuperview];
}

在Objective-C中,nil检查是不必要的;否则,这只是我的答案的重复 - @Paul.s已经给出了最干净的答案。 - fzwo
我们几乎在同一时间完成了。但是好的写作可以解释清楚事情;-) 我不喜欢通过不检查发送的消息来滥用系统。如果不必要或者没有必要,就不要给对象发送消息。这是无用的,在大型循环环境中可能会付出巨大的代价。 - Vaseltior
向 nil 发送消息几乎没有比检查 (foo != nil) 更大的开销,因为 objc_msgSend 首先会检查并提前返回。这 不是 滥用系统,而是使用系统的美妙之处。检查 Null 是其他语言的做法。 - fzwo
有时候你需要进行高性能计算,有时候则不需要。这就是为什么我保持这种混合而仍然基础的编程方式。这将帮助你的继任者理解你正在做什么。你有任何线索或链接表明 objc_msgSend 首先会检查吗?我对 ObjC 的基础非常感兴趣。 - Vaseltior
不要陷入过早优化的陷阱:仅在进行了所有其他优化之后,优化掉objc_msgSend才有意义。这里是@bbum撰写的一篇深入文章,以及iPhone 1上由Mike Ash进行的性能评估。还可以查看http://stackoverflow.com/questions/2962466/how-much-overhead-does-a-msg-send-call-incur的优秀答案,特别是两个未被接受的答案。 - fzwo
显示剩余2条评论

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