在 UIViewController 之间使用数组堆栈和动画会产生意外行为
Using array stack and animating between UIViewControllers yields unexpected behavior
使用 Swift 4 和 Xcode 10.1
我的目标是使用数组堆栈来跟踪我的 UIViewControllers,因为我在它们之间设置动画。
我已经完成了大部分工作,但是当我第二次将 ViewController 转换到视图中时,它似乎与下面的当前视图控制器交换了位置。
基本设置(故事板)是一个 UIViewController,它有一个 ContainerView (MainContainer) 和 4 个独立的 ViewControllers(VC1、VC2、 VC3,VC4)。每个 VC 也有一个 restorationId 以便我可以打印它并跟踪堆栈数组中的位置。
一开始我显示VC1,然后我用按钮来测试处理每个VC。
代码如下:
@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
}
}
一个失败的简单测试:
通过单击 VC2 按钮添加 VC2,它从右向左滑动到 VC1 上,然后 VC2 被压入堆栈.单击后退按钮使 VC2 从左向右向后滑动,再次显示 VC1,然后 VC2 从堆栈中弹出。到目前为止这是有效的 - 只有 VC1 是可见的。
但是,当我第二次单击 VC2 按钮时,它似乎与 VC1 交换了位置。 VC1 立即替换为 VC2,然后 VC1 从左到右滑过顶部。
不知何故,看起来 VC2 和 VC1 在动画之前被交换了。
另请注意 - 我可以将 VC2、VC3 和 VC4 相互压入,并将它们压入堆栈,然后单击返回以删除他们从视图和它的工作原理。但是,一旦任何 vc 被再次推送,交换就会发生在紧邻下方的任何视图中。
我使用 print 语句跟踪堆栈数组,它似乎工作正常。动画也能正常工作,所以我一定是在 display/add VC 的方式上做错了....
知道为什么每个 VC 可以动画显示,然后删除,但不能再次动画显示吗?
我认为在视图控制器包含方面有很多地方可以改进(无意冒犯!)但要暂时解决您的问题,请执行以下操作:
而不是在 slideOUTTransition()
的完成块中调用 visibleVC!.dismiss(animated: false, completion: nil)
(您没有提供任何内容,因此您不必关闭任何内容)从父视图中删除视图并重置其转换:
// visibleVC!.dismiss(animated: false, completion: nil)
visibleVC!.view.removeFromSuperview()
visibleVC!.view.transform = .identity
使用 Swift 4 和 Xcode 10.1
我的目标是使用数组堆栈来跟踪我的 UIViewControllers,因为我在它们之间设置动画。 我已经完成了大部分工作,但是当我第二次将 ViewController 转换到视图中时,它似乎与下面的当前视图控制器交换了位置。
基本设置(故事板)是一个 UIViewController,它有一个 ContainerView (MainContainer) 和 4 个独立的 ViewControllers(VC1、VC2、 VC3,VC4)。每个 VC 也有一个 restorationId 以便我可以打印它并跟踪堆栈数组中的位置。
一开始我显示VC1,然后我用按钮来测试处理每个VC。
代码如下:
@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
}
}
一个失败的简单测试:
通过单击 VC2 按钮添加 VC2,它从右向左滑动到 VC1 上,然后 VC2 被压入堆栈.单击后退按钮使 VC2 从左向右向后滑动,再次显示 VC1,然后 VC2 从堆栈中弹出。到目前为止这是有效的 - 只有 VC1 是可见的。
但是,当我第二次单击 VC2 按钮时,它似乎与 VC1 交换了位置。 VC1 立即替换为 VC2,然后 VC1 从左到右滑过顶部。 不知何故,看起来 VC2 和 VC1 在动画之前被交换了。
另请注意 - 我可以将 VC2、VC3 和 VC4 相互压入,并将它们压入堆栈,然后单击返回以删除他们从视图和它的工作原理。但是,一旦任何 vc 被再次推送,交换就会发生在紧邻下方的任何视图中。
我使用 print 语句跟踪堆栈数组,它似乎工作正常。动画也能正常工作,所以我一定是在 display/add VC 的方式上做错了....
知道为什么每个 VC 可以动画显示,然后删除,但不能再次动画显示吗?
我认为在视图控制器包含方面有很多地方可以改进(无意冒犯!)但要暂时解决您的问题,请执行以下操作:
而不是在 slideOUTTransition()
的完成块中调用 visibleVC!.dismiss(animated: false, completion: nil)
(您没有提供任何内容,因此您不必关闭任何内容)从父视图中删除视图并重置其转换:
// visibleVC!.dismiss(animated: false, completion: nil)
visibleVC!.view.removeFromSuperview()
visibleVC!.view.transform = .identity