通过在它们上方移动另一个 UIView 来获取所有 UIViews

Posted

技术标签:

【中文标题】通过在它们上方移动另一个 UIView 来获取所有 UIViews【英文标题】:Get all UIViews affected by moving another UIView above them 【发布时间】:2017-05-06 22:04:39 【问题描述】:

我有一个UIScrollView,其中包含许多大小不同的可拖动UIViews,大部分是正方形。这些UIViews 捕捉到屏幕上的 6 列网格。

当一个UIView(假设它的大小是132x132)卡入到位并且另一个UIView's 框架与第一个UIView 框架相交时,第二个框架应该移动到第一个框架之下,要么是全部要么是一半第一个UIView 的高度(取决于UIViews 相交的位置)。我通过使用CGRectIntersectsRect 以及CGRectGetMaXYCGRectGetMinY 来计算所需的移动距离。

但是,也有可能在已经受影响的 UIView 下方存在第三个甚至更多 UIViews,它也可能需要被移动。我的问题是我不能

a) 以正确的顺序(按其 Y 原点排序)将所有受影响的 UIViews 移动到移动的 UIView(而不是快照的 UIView)下方。 b) 仅基于它们上方的UIView 移动UIViews,而不受卡入到位的UIView 的影响(即使移动距离可能相同)。

我目前的尝试是在捕捉后获得一个与移动的UIView 相交的UIView,将UIView 和所需的移动距离作为字典添加到数组中,然后在其下方添加其他UIViews使用递归。然而,有时,UIViews 会因为它们在视图层次结构中的顺序而被错误地移动,或者它们甚至根本没有移动。

这是一个例子:

带有框架的 UIView A = 0 0; 269 132 带有框架的 UIView B = 0 205.5; 63.5 63.5 UIView C 与 frame = 137 137; 132 132 对齐到 0 0; 132 132 边框间距为 5 像素

UIViewC 卡入到位时,UIView A 应该移动到0 137; 269 132,因为所需的移动距离是 UIView C 的全高 + 边框间距。但是将UIView A 移动到新位置也会影响UIView B,然后将0 274; 63.5 63.5 移动到UIView A 的一半高度+边框间距,删除UIView A 和B 之间的空间。

这是另一个例子:

带有框架的 UIView A = 0 0; 269 132 带有框架的 UIView B = 0 205.5; 63.5 63.5 UIView C 与 frame = 0 274; 269 269 对齐到 0 68.5; 269 269

UIView C 移动到位时,UIView A 移动到 0 274; 269 132,然后影响 UIView B 并将其移动到 0 411; 63.5 63.5,删除之前 UIView A 和 B 之间的空间。

【问题讨论】:

在您的第一个示例中,您的意思是说您的 UIView A 的帧大小从 269,132 变为 132,132?那是对的吗?我假设这些是框架的宽度/高度。 在你的第二个例子中,在 UIView C 移动到位,UIView A 移动后,C 和 A 重叠? UIView 大小没有改变。那是个错误,我已经改正了。在第二个例子中,当 UIView C 移动到位时,它与 A 和 B 都重叠,因此需要向下移动。 【参考方案1】:

这是我正在使用的 Objective-C 实现,基于 Daniel T. 的回答和他的伪代码。我接受了他的回答,因为他提供了实际的解决方案。仅供参考。

- (void)moveAffectedViewsForView:(UIView*)movedView 
    NSMutableArray* stack = [NSMutableArray new];
    [stack addObject:movedView];

    while ([stack count] > 0) 
        UIView* current = [stack objectAtIndex:0];
        [stack removeObject:current];

        for (UIView* view in [self subviews]) 
            if (view != current && CGRectIntersectsRect(current.frame, view.frame)) 
                [stack addObject:view];

                CGFloat moveDistance = (CGRectGetMaxY(current.frame) - CGRectGetMinY(view.frame)) + 5;
                [view setFrame:CGRectOffset(view.frame, 0, moveDistance)];
             
        
    

【讨论】:

【参考方案2】:

您可能需要设置一个先进先出的堆栈。算法看起来像这样(不是有效的 Obj-C 代码!):

move viewC
[stack addObject: viewC]
while stack.isEmpty == false 
    current = stack[0]
    remove current from stack
    for view in allViews 
        if view != current and view needs to move because of current's position 
            [stack addObject: view]
            move view to correct position
        
    

上面将首先将 viewC 移动到它的新位置,然后扫描除 viewC 之外的所有视图,并在必要时将它们从 viewC 下移出。每个被移动的视图都将被添加到堆栈中。

然后它会遍历所有由于 viewC 的新位置而移动的视图,并检查是否有任何尚未移动的视图因为 那个 视图的新位置而必须移动。等等……

-- 编辑--

经过反思,我认为您不需要跟踪访问过哪些视图。我更新了算法以删除该步骤。

【讨论】:

以上是 Dijkstra 寻路算法的一种变体,但重新用于视图移动。 这个算法运行得非常好。比我尝试过的任何东西都要好得多。非常感谢您的帮助!

以上是关于通过在它们上方移动另一个 UIView 来获取所有 UIViews的主要内容,如果未能解决你的问题,请参考以下文章

在 `UIViewController` 上方添加 `UIView`

通过更改所有权oracle将表从一个模式移动到另一个模式?

将 UIView 从一个选项卡移动到另一个选项卡

通过包含另一个 firstResponder(一个 UITextView)来创建一个 firstResponder UIView

iOS 通过故事板将 UIView 锁定在导航栏下方和 UITableView 上方

使用约束自动移动 UIView