I've been having a strong headache with this problem, because I know for a fact should be a simple solution but I can't find the right answer.
The idea is that I have two UIViews
(let's call them A and B). All I want to do is to swap their positions on button press, by the A view going down to B position, and B view going up to A position.
Here's what I'm trying to achieve.
I'm trying to achieve that by just swapping the .center
of the elements that I need (as A and B are both UIViews
that cointains a bunch of elements inside them). This is inside an animation block - UIView.animate
However, what is currently happening is that the animation is performing in exactly the opposite way: The A view would go up the screen, then re-appear in the bottom side of the screen and end up in the desired position (B initial position). Same thing with the B view, it's currently going all the way to the bottom of the screen, then re-appear at the top, and finish the animation in the desired position (A initial position).
This is what is currently happening.
This is my code so far (note that ownAccountTransferView
is defined in another file, and below code is placed in my UIViewController that stores that view). I call this function on button press, after swapping the the data (like labels and such) between the two cards.
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
}
}
}
So, how can I make the animation work the way I want - A view going to the bottom and B view going to the top?
Thanks in advance.
UPDATE: I fixed it by ussing transform. Tried messing around with animating constraints A LOT, tried every possible interaction between top/bottom constraints, but it would either not do anything at all or just bug my view and send it to hell. I even was insisting with animating constraints in support calls with my coworkers. And so were they.
However, after googling despair measures, I ended up using viewController.myView.myLabel.transform = CGAffineTransform(translationX: 0, y: 236)
.
Of course, you can reduce this to: view.transform = CGAffineTransform(translationX: 0, y: 236)
.
The value is arbitrary, and I'm not sure if it can some day break or cause undesired behaviour. I tested it on iPhone 8 and iPhone 13, and in both the animations performs just ok (A view goes down, B goes up, click switch and do the opposite, etc...).
Side note: If you gonna use this, you need to NOT copy/paste the values when you need your views to "come back" to where they were at the beginning. I used 236 and -236 for A and B respectively, but to restore them I need to use -2 and 2 or it super bugs out. Idk why tho. But it works! Thanks all
CodePudding user response:
Enclose both the views in a parent view and do the following:
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)
})
}
Check out the answer here.
EDIT: I was able to achieve the said animation using Autolayout constraints. Basically what I did was have fixed constraints for two views initially. And have two sets of changing constraints - startConstraints and endConstraints. Simply activating and deactivating the constraints did the job. Check out the demo. Here's my code:
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()
})
}
}
CodePudding user response:
UPDATE: I fixed it by ussing transform. Tried messing around with animating constraints A LOT, tried every possible interaction between top/bottom constraints, but it would either not do anything at all or just bug my view and send it to hell. I even was insisting with animating constraints in support calls with my coworkers. And so were they. However, after googling despair measures, I ended up using viewController.myView.myLabel.transform = CGAffineTransform(translationX: 0, y: 236).
Of course, you can reduce this to: view.transform = CGAffineTransform(translationX: 0, y: 236). The value is arbitrary, and I'm not sure if it can some day break or cause undesired behaviour. I tested it on iPhone 8 and iPhone 13, and in both the animations performs just ok (A view goes down, B goes up, click switch and do the opposite, etc...).
Side note: If you gonna use this, you need to NOT copy/paste the values when you need your views to "come back" to where they were at the beginning. I used 236 and -236 for A and B respectively, but to restore them I need to use -2 and 2 or it super bugs out. Idk why tho. But it works! Thanks all