重新排列UIView的子视图

10

在我的应用程序中,我想将一个子视图置于最前面,然后稍后再将其放回到原来的图层位置。代码应该很简单:

将子视图置于最前面(在我的自定义UIView类内部):

[self.superview bringSubviewToFront:self];

很简单。我将原始的z位置存储在一个实例变量中,称为您猜对了,zPosition。因此,在-bringSubviewToFront:之前的行是:

zPosition = [self.superview.subviews indexOfObject:self];

所以,我用来将子视图置于最前面的所有代码是:

zPosition = [self.superview.subviews indexOfObject:self];
[self.superview bringSubviewToFront:self];

这个代码的功能符合预期。问题在于当我尝试把子视图放回原来的位置时出现了问题。我的操作很简单:

[self.superview exchangeSubviewAtIndex:zPosition withSubviewAtIndex:
    [self.superview.subviews indexOfObject:self]];

使用这个代码,如果我有两个子视图,那么会发生以下情况:

假设我有视图A和视图B。 视图A在视图B上方。 我点击视图B,它就会到最前面。 我再次点击视图B(它应该回到原来的位置),但什么也不发生,所以现在它在视图A上面。 如果我现在点击视图A,它就会到最前面,但是当我再次点击它(所以它应该回到原来的z位置:低于视图B),所有兄弟视图都会消失!

有人看出可能导致这个问题的原因吗?


也许你可以从一个虚拟的、空的子视图开始。然后你可以来回切换这个子视图。我知道这不是最优雅的方法... - Nicolas Miari
4个回答

12

使用exchangeSubviewAtIndex可能会将视图放回正确的位置,但它还会交换另一个视图到顶部,这不是你最开始想要的结果。你可能需要像这样做,而不是使用exchangeSubviewAtIndex:

[self retain];
UIView *superview = self.superview;
[self removeFromSuperview];
[superview insertSubview:self atIndex:zPosition];
[self release];

调用 [self removeFromSuperview] 会导致 self 被释放,因为一旦它从其父视图中移除,就没有任何对象再持有它了。 - eric.mitchell
也许你可以将这个任务委托给父视图(和/或负责此层次结构的视图控制器)? - Husker Jeff
@Rickay,在对象的方法之一正在主线程中运行时,如何使self被释放?iOS不会在树的顶部之前切断树。 :) 这将是iOS非常不一致和危险的行为,你不觉得吗?这将导致数百个不必要的崩溃...线程的运行循环在进程期间保持当前对象处于活动状态! - holex
你说得对:在同时处理多个任务时不应该回复编程答案! - eric.mitchell
你能提供另一种在启用ARC时的解决方案吗?我尝试了这里(https://dev59.com/AGsz5IYBdhLWcg3wlY-9)中的"AntiARCRetain"定义,但没有成功。 - Eduard Feicho

12
没有必要从父视图中移除: [self.superview insertSubview:self atIndex:zPosition];

2

[Swift解决方案]

像其他人说的一样,没有必要移除和重新添加子视图。

我发现最方便的方法是:

superView.insertSubview(subviewYouWantToReorder, aboveSubview: subviewWhichShouldBeBelow)

0

这个问题和答案对我非常有帮助。

我有一个要求,需要在视图堆栈中放置一个遮罩,该遮罩位于上方和下方的视图之间,并且我想保持它的动态性。 也就是说,一个视图可以告诉它是否隐藏。

我使用以下算法重新排序视图。感谢下面的AW101提供了“无需删除视图”的实现。

这是我的算法:

- (void) insertOverlay {

    // Remember above- and belowcounter
    int belowpos = 0, abovepos = 0;

    // Controller mainview
    UIView *mainview = [self currentMainView];

    // Iterate all direct mainview subviews
    for (UIView* view in mainview.subviews) {
        if ([self isAboveOverlay:view]) {
            // Re-insert as aboveview
            [mainview insertSubview:view atIndex:belowpos + (abovepos++)];
        }
        else {
            // Re-insert as belowview
            [mainview insertSubview:view atIndex:belowpos++];
        }
    }

    // Put overlay in between above and below.
    [mainview insertSubview:_overlay atIndex:belowpos];
}

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