UIView的bringSubviewToFront:方法*不会*将视图置于最前面。

7
我正在实现一个简单的iOS纸牌游戏,允许用户以通常的方式拖动卡牌。卡牌用UIView子类CardView表示。所有卡牌视图都是SolitaireView的子视图,它们是兄弟视图。下面这段代码片段尝试将一张卡牌“置于前面”,使其在被拖动时位于所有其他视图之上:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
   UITouch *touch = [touches anyObject];
   if (touch.view.tag == CARD_TAG) {
      CardView *cardView = (CardView*) touch.view;
      ...
      [self bringSubviewToFront:cardView];
      ...
   }
}

不幸的是,在拖动过程中,卡牌的z-order保持不变。在下面的图片中,我正在拖动King。请注意,它在左侧图像中正确地位于Nine之上,但在右侧图像中错误地位于Two之下(实际上是整个堆栈之下):

enter image description here enter image description here

我还尝试了修改layer.zPosition属性,但无济于事。如何在拖动过程中将卡牌视图带到最前面?我感到困惑。


我发现 bringSubviewToFront: 会触发视图的 'needsLayout' 标志被设置。我的 layoutSubviews 方法也调用了 bringSubviewToFront:,因此将所有视图的 z-order 恢复到它们的初始状态! - wcochran
那么这个问题有什么解决方案呢?我也有同样的需求,就是从卡片列表中选择任何一个中间的卡片,它应该出现在前面。但是我的动画由于bringSubviewToFront:方法而无法正常工作。 - Mrunal
如果您坚持使用UIView子类,则需要标记正在拖动的卡片,以便layoutSubviews可以将其放置在顶部。实际上,我已经切换到使用CALayer,如下所述。 - wcochran
谢谢您的回复。您能否提供相关代码? - Mrunal
下面列出了一段代码片段。 - wcochran
@Mrunal 源代码现在位于 https://github.com/wcochran/solitaire-iOS - wcochran
1个回答

8

确认。 bringSubviewToFront:会导致调用layoutSubview。因为我的版本layoutSubviews设置了所有视图的z-顺序,这会撤销我在上面touchesBegan:withEvent代码中设置的z-顺序。苹果应在bringSubviewToFront文档中提到此副作用。

我创建了一个名为CardLayerCALayer子类,而不是使用UIView子类。我在我的KlondikeView子类中处理触摸,如下所示。 topZPosition是跟踪所有卡片最高zPosition的实例变量。请注意,修改zPosition通常是动画的 - 我在下面的代码中将其关闭:

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
   UITouch *touch = [touches anyObject];
   CGPoint touchPoint = [touch locationInView:self];
   CGPoint hitTestPoint = [self.layer convertPoint:touchPoint
                                           toLayer:self.layer.superlayer];
   CALayer *layer = [self.layer hitTest:hitTestPoint];

   if (layer == nil) return;

   if ([layer.name isEqual:@"card"]) {
     CardLayer *cardLayer = (CardLayer*) layer;
     Card *card = cardLayer.card;

     if ([self.solitaire isCardFaceUp:card]) {
        //...                                                                                                            
        [CATransaction begin]; // disable animation of z change                                                                  
        [CATransaction setValue:(id)kCFBooleanTrue
                         forKey:kCATransactionDisableActions];              
        cardLayer.zPosition = topZPosition++; // bring to highest z

        // ... if card fan, bring whole fan to top

        [CATransaction commit];
        //...                                                                                                            
     }
     // ...                                                                                                             
   }

}

4
我认为使用UIView来展示纸牌可能是一个不好的设计选择 - 我不认为它们真正是为此而设计的。我想退后一步,改用CALayer来展示。 - wcochran

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