iOS -在动画块内交换两个UIView的位置

cgh8pdjw  于 2022-11-19  发布在  iOS
关注(0)|答案(3)|浏览(228)

我一直对这个问题感到很头痛,因为我知道对于一个事实应该有一个简单的解决办法,但我找不到正确的答案。
我有两个UIViews(我们称它们为A和B),我所要做的就是在按下按钮时交换它们的位置,即A视图向下移动到B位置,B视图向上移动到A位置。
Here's what I'm trying to achieve.
我试图通过交换我需要的元素的.center来实现这一点(因为A和B都是UIViews,其中包含一堆元素)。
然而,目前发生的情况是动画以完全相反的方式执行:A视图将在屏幕上向上移动,然后重新出现在屏幕底部,并在所需位置结束(B初始位置)。与B视图相同,它当前一直移动到屏幕底部,然后重新出现在顶部,并在所需位置结束动画(A初始位置)。
This is what is currently happening.
这是我到目前为止的代码(注意ownAccountTransferView在另一个文件中定义,下面的代码放在我的UIViewController中,存储该视图)。在两张卡之间交换数据(如标签等)后,我在按下按钮时调用此函数。

fileprivate func performSwitchAnimation() {

// NOTE: ownAccountTransferView is a reference to my view, think of it like self.view
        
 let fromAccountCardCenter = 
 self.ownAccountTransferView.fromAccountCardView.center
 let fromAccountTypeLabelCenter = 
 self.ownAccountTransferView.fromAccountTypeLabel.center
 let fromAccountTypeViewCenter = 
 self.ownAccountTransferView.fromAccountTypeView.center
 let fromAccountBalanceLabelCenter = 
 self.ownAccountTransferView.fromAccountBalanceLabel.center
 let fromAccountNameLabelCenter = 
 self.ownAccountTransferView.fromAccountNameLabel.center
 let fromAccountChevronCenter = 
 self.ownAccountTransferView.fromAccountChevronView.center
        
 let toAccountCardCenter = 
 self.ownAccountTransferView.toAccountCardView.center
 let toAccountTypeLabelCenter = 
 self.ownAccountTransferView.toAccountTypeLabel.center
 let toAccountTypeViewCenter = 
 self.ownAccountTransferView.toAccountTypeView.center
 let toAccountBalanceLabelCenter = 
 self.ownAccountTransferView.toAccountBalanceLabel.center
 let toAccountNameLabelCenter = 
 self.ownAccountTransferView.toAccountNameLabel.center
 let toAccountChevronCenter = 
 self.ownAccountTransferView.toAccountChevronView.center
        
  UIView.animate(withDuration: 1, delay: 0, options: []) {
            
   self.ownAccountTransferView.switchAccountsButton.isUserInteractionEnabled = false
            
       self.ownAccountTransferView.fromAccountCardView.center = toAccountCardCenter
       self.ownAccountTransferView.fromAccountTypeLabel.center = toAccountTypeLabelCenter
       self.ownAccountTransferView.fromAccountTypeView.center = toAccountTypeViewCenter
            self.ownAccountTransferView.fromAccountBalanceLabel.center = toAccountBalanceLabelCenter
       self.ownAccountTransferView.fromAccountNameLabel.center = toAccountNameLabelCenter
       self.ownAccountTransferView.fromAccountChevronView.center = toAccountChevronCenter
            
       self.ownAccountTransferView.toAccountCardView.center = fromAccountCardCenter
       self.ownAccountTransferView.toAccountTypeLabel.center = fromAccountTypeLabelCenter
       self.ownAccountTransferView.toAccountTypeView.center = fromAccountTypeViewCenter
       self.ownAccountTransferView.toAccountBalanceLabel.center = fromAccountBalanceLabelCenter
       self.ownAccountTransferView.toAccountNameLabel.center = fromAccountNameLabelCenter
       self.ownAccountTransferView.toAccountChevronView.center = fromAccountChevronCenter
            
        } completion: { isDone in
            if isDone {
                self.ownAccountTransferView.switchAccountsButton.isUserInteractionEnabled = true
            }
        }
        
    }

那么,如何使动画按我想要的方式工作-A视图位于底部,B视图位于顶部
先谢谢你。

**最新消息:我通过使用transform修复了这个问题。**尝试了很多次动画约束的混乱,尝试了顶部/底部约束之间的每一种可能的交互,但是它要么什么都不做,要么就只是bug我的视图,把它送进地狱。我甚至在和同事的支持电话中坚持动画约束。他们也是。然而,在谷歌搜索绝望措施后,我最终使用了viewController.myView.myLabel.transform = CGAffineTransform(translationX: 0, y: 236)

当然,您可以将其简化为:view.transform = CGAffineTransform(translationX: 0, y: 236)。这个值是任意的,我不确定它是否会在某一天崩溃或导致不希望的行为。我在iPhone 8和iPhone 13上测试了它,在这两个动画中表现都很好(A视图下降,B视图上升,点击切换并执行相反的操作,等等...)。
边注:如果你要使用这个,当你需要你的视图“回到”开始的地方时,你不需要复制/粘贴值。我分别用236和-236表示A和B,但是要恢复它们,我需要用-2和2,否则它会超级bug。我不知道为什么。但是它很有效!谢谢大家

7dl7o3gd

7dl7o3gd1#

将两个视图包含在父系视图中,并执行下列操作:

if let firstIndex = view.subviews.firstIndex(of: view1), let secondIndex = view.subviews.firstIndex(of: view2) {
        let firstViewFrame = view1.frame

        UIView.animate(withDuration: 1, animations: {
            self.view1.frame = self.view2.frame
            self.view2.frame = firstViewFrame
            self.view.exchangeSubview(at: firstIndex, withSubviewAt: secondIndex)
        })
    }

看看答案here
编辑:我可以使用Autolayout约束来实现上述动画。基本上我所做的是最初为两个视图设置固定约束。并设置两组变化的约束- startConstraints和endConstraints。只需激活和取消激活约束即可完成工作。Check out the demo.以下是我的代码:

class ViewController: UIViewController {
var view1: UIView = {
    let v = UIView()
    v.backgroundColor = .red
    return v
}()

var view2: UIView = {
    let v = UIView()
    v.backgroundColor = .blue
    return v
}()

var button: UIButton = {
    let b = UIButton()
    b.setTitle("Animate", for: .normal)
    b.setTitleColor(.black, for: .normal)
    return b
}()

var fixedConstraints: [NSLayoutConstraint]!
var startConstraints: [NSLayoutConstraint]!
var endConstraints: [NSLayoutConstraint]!

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(view1)
    view.addSubview(view2)
    view.addSubview(button)
    
    view1.translatesAutoresizingMaskIntoConstraints = false
    view2.translatesAutoresizingMaskIntoConstraints = false
    button.translatesAutoresizingMaskIntoConstraints = false
    
    fixedConstraints = [
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        view1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        view2.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        view1.heightAnchor.constraint(equalToConstant: 100),
        view1.widthAnchor.constraint(equalToConstant: 100),
        view2.heightAnchor.constraint(equalToConstant: 100),
        view2.widthAnchor.constraint(equalToConstant: 100),
    ]
    
    startConstraints = [
        view1.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0),
        view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view2.bottomAnchor),
    ]
    
    startConstraints.append(contentsOf: fixedConstraints)
    NSLayoutConstraint.activate(startConstraints)
    
    button.addTarget(self, action: #selector(animateSwitchingOfViews), for: .touchUpInside)
}

@objc
private func animateSwitchingOfViews(){
    endConstraints = [
        view2.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0),
        view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo:view1.bottomAnchor)
    ]
    endConstraints.append(contentsOf: fixedConstraints)
    
    UIView.animate(withDuration: 2, animations: {
        NSLayoutConstraint.deactivate(self.startConstraints)
        NSLayoutConstraint.activate(self.endConstraints)
        self.view.layoutIfNeeded()
    })
}

}

omjgkv6w

omjgkv6w2#

最新消息:我通过使用transform修复了它。我尝试了很多次动画约束,尝试了顶部/底部约束之间的每一种可能的交互,但它要么什么都不做,要么只是bug我的视图并将其发送到地狱。我甚至在与同事的支持电话中坚持动画约束。他们也是。然而,在谷歌搜索绝望措施后,最后我使用了viewController.myView.myLabel.transform = CGAffineTransform(转换X:0,y:236)。
当然,您可以将其简化为:转换= CGAffineTransform(平移X:0,y:236)。这个值是任意的,我不确定它是否会在某一天崩溃或导致不希望的行为。我在iPhone 8和iPhone 13上测试了它,在这两个动画中表现都很好(A视图下降,B视图上升,点击切换并执行相反的操作,等等...)。
边注:如果你要使用这个,当你需要你的视图“回到”它们开始的地方时,你不需要复制/粘贴值。我分别为A和B使用了236和-236,但是为了恢复它们,我需要使用-2和2,否则它会超级bug。我不知道为什么。但是它工作!谢谢大家

aemubtdh

aemubtdh3#

假设您想要“交换中心”,并且不想修改约束/常量,则***可以***使用变换。
但是,您可以像这样执行,而不是硬编码值:

// if the views are in their "original" positions
if ownAccountTransferView.transform == .identity {
    let xOffset = fromAccountTypeView.center.x - ownAccountTransferView.center.x
    let yOffset = fromAccountTypeView.center.y - ownAccountTransferView.center.y
    UIView.animate(withDuration: 1.0, animations: {
        self.ownAccountTransferView.transform = CGAffineTransform(translationX: xOffset, y: yOffset)
        self.fromAccountTypeView.transform = CGAffineTransform(translationX: -xOffset, y: -yOffset)
    })
} else {
    UIView.animate(withDuration: 1.0, animations: {
        self.ownAccountTransferView.transform = .identity
        self.fromAccountTypeView.transform = .identity
    })
}

相关问题