Using Swift 4 and Xcode 10.1
My goal is to use an Array Stack to track my UIViewControllers as I animate between them. I've gotten most of it working, however when I transition a ViewController into view for the second time, it seems to swap places with the current view controller below.
The basic setup (storyboard) is a UIViewController that has a ContainerView (MainContainer) and 4 separate ViewControllers (VC1, VC2, VC3, VC4). Each VC also has a restorationId so that I can print it and track the position in the stack array.
At the start I display VC1, then I use buttons to test handling each VC.
Here's the code:
@IBOutlet weak var MainContainer: UIView!
var vc1:UIViewController!
var vc2:UIViewController!
var vc3:UIViewController!
var vc4:UIViewController!
var vcStack = Stack<UIViewController>()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
vc1 = self.storyboard?.instantiateViewController(withIdentifier: "VC1")
vc2 = self.storyboard?.instantiateViewController(withIdentifier: "VC2")
vc3 = self.storyboard?.instantiateViewController(withIdentifier: "VC3")
vc4 = self.storyboard?.instantiateViewController(withIdentifier: "VC4")
vcStack.push(vc1)
displayVC(content: vc1!)
}
func displayVC(content: UIViewController) {
// Works to change VCs - no transition
self.addChild(content)
content.view.translatesAutoresizingMaskIntoConstraints = false
self.MainContainer.addSubview(content.view)
NSLayoutConstraint.activate([
content.view.leftAnchor.constraint(equalTo: self.MainContainer.leftAnchor, constant: 0),
content.view.topAnchor.constraint(equalTo: self.MainContainer.topAnchor, constant: 0),
content.view.bottomAnchor.constraint(equalTo: self.MainContainer.bottomAnchor, constant: 0),
content.view.rightAnchor.constraint(equalTo: self.MainContainer.rightAnchor, constant: 0)
])
content.didMove(toParent: self)
}
func slideINTransition(newVC: UIViewController){
print("slide IN stack newVC: \(newVC.restorationIdentifier ?? "novalue")")
print("slide IN stack stack top: \(vcStack.top!.restorationIdentifier ?? "novalue")")
newVC.view.frame = vcStack.top!.view.frame;
vcStack.top!.view.superview?.insertSubview(newVC.view, aboveSubview: vcStack.top!.view)
newVC.view.transform = CGAffineTransform(translationX: vcStack.top!.view.frame.size.width, y: 0)
UIView.animate(withDuration: 0.5,
delay: 0.0,
options: UIView.AnimationOptions.curveEaseInOut,
animations: {
newVC.view.transform = CGAffineTransform(translationX: 0, y: 0)
},
completion:nil// { finished in
//self.vcStack.top!.present(newVC, animated: false, completion: nil) //throws an error when uncommented
//}
)
vcStack.push(newVC)
for junk in vcStack.array{
print("slide IN stack: \(junk.restorationIdentifier ?? "novalue")")
}
}
@IBAction func But_Back_Pressed(_ sender: UIButton) {
for junk in vcStack.array{
print("-----back but stack: \(junk.restorationIdentifier ?? "novalue")")
}
slideOUTTransition()
}
func slideOUTTransition(){
print("slideOUT count: \(vcStack.count)")
if (vcStack.count > 1) {
let visibleVC = vcStack.pop()
visibleVC!.view.transform = CGAffineTransform(translationX: 0, y: 0)
UIView.animate(withDuration: 0.5,
delay: 0.0,
options: UIView.AnimationOptions.curveEaseInOut,
animations: {
visibleVC!.view.transform = CGAffineTransform(translationX: visibleVC!.view.frame.size.width, y: 0)
},
completion: { finished in
visibleVC!.dismiss(animated: false, completion: nil)
}
)
for junk in vcStack.array{
print("slideOUT stack: \(junk.restorationIdentifier ?? "novalue")")
}
print("BACK pop count: \(vcStack.count)")
}
}
@IBAction func But_VC1_Pressed(_ sender: UIButton) {
if vcStack.hasElement(vc1){
print("Trans vc1 exists - doing nothing")
}else{
print("Trans vc1 unique")
slideINTransition(newVC: vc1)
}
}
@IBAction func But_VC2_Pressed(_ sender: UIButton) {
if vcStack.hasElement(vc2){
print("Trans vc2 exists - doing nothing")
}else{
print("Trans vc2 unique")
slideINTransition(newVC: vc2)
}
}
@IBAction func But_VC3_Pressed(_ sender: UIButton) {
if vcStack.hasElement(vc3){
print("Trans vc3 exists - doing nothing")
}else{
print("Trans vc3 unique")
slideINTransition(newVC: vc3)
}
}
@IBAction func But_VC4_Pressed(_ sender: UIButton) {
if vcStack.hasElement(vc4){
print("Trans vc4 exists - doing nothing")
}else{
print("Trans vc4 unique")
slideINTransition(newVC: vc4)
}
}
public struct Stack<T> where T: Equatable {
fileprivate var array = [T]()
public var isEmpty: Bool {
return array.isEmpty
}
public var count: Int {
return array.count
}
public mutating func push(_ element: T) {
array.append(element)
}
public mutating func pop() -> T? {
return array.popLast()
}
public func hasElement(_ element: T) -> Bool {
return array.contains(element)
}
public var top: T? {
return array.last
}
}
A simple test that fails:
Add VC2 by clicking the VC2 button, and it slides right-to-left over VC1 and VC2 is pushed onto the stack. Click the Back button to make VC2 slide backwards, left-to-right, to reveal VC1 again and VC2 is popped off the stack. So far this works - only VC1 is visible.
However, when I then click the VC2 button for the second time it appears to swap places with VC1. VC1 is immediately replaced with VC2, and then VC1 slides left-to-right over the top. Somehow it looks like VC2 and VC1 are swapped before animating.
Also of note - I can push VC2, VC3, and VC4 over each other, and push them onto the stack, and then click Back to remove them from view and it works. However, once any vc is then pushed again, the swapping occurs with whatever view is immediately below.
I track the stack array using print statements and it appears to work properly. The animations work properly as well so I must be doing something wrong with how I display/add the VCs....
Any ideas why each VC can be animated into view, then removed, but not animated into view again?