Xcode6中出现键盘时如何修复消失的视图?
How to Fix Disappearing Views When the Keyboard Appears in Xcode 6?
我在视频中加入了 link 来说明我遇到的问题:
https://dl.dropboxusercontent.com/u/39330138/Bug_Demo1.mov
有两个视图控制器,第一个不模糊且不太重要。单击加号按钮时,应用程序会转到一个新控制器(没有动画),在 prepareForSegue()
中,我使用 UIGraphicsBeginImageContext
和 UIGraphicsGetImageFromCurrentImageContext
从当前视图中捕获 UIImage
并将其传递给下一个。
当新视图出现时,我使用 UIVisualEffectView
创建一个模糊视图并将其作为子视图添加到 'background' 的图像视图中。然后,它的不透明度是动画的,同时 2 个视图和 2 个按钮在屏幕上用 UIView
动画和 springWithDamping
动画,给人一种视图模糊和项目在顶部动画的错觉。
顶部视图中嵌入了一个 UITextField
,当点击时调用 becomeFirstResponder()
并使所有覆盖(新会话、标签和按钮)视图包括嵌入在背景中的视觉效果视图图片视图消失。
我详细介绍的原因是因为我不确定问题到底是什么。但是,我怀疑它与 Xcode 6.
中的 AutoLayout/Size 类有关
有谁知道为什么会发生这种情况以及如何解决?
如果您需要更多信息,请告诉我。
谢谢!
编辑:
当我在单击 TextField 后记录视图时,所有的框架看起来都一样。
编辑 2:
这里是一个 link 演示项目,将包含视频中的所有功能:
https://dl.dropboxusercontent.com/u/39330138/DEMO%20APP.zip
这里发生了几件事,但罪魁祸首是您对 viewDidLayoutSubviews()
的使用。任何时候系统必须重新评估布局时都会调用它。我看到你用那种方法将 UIVisualEffectView
的 alpha
设置为 0
:
if !returningFromTagView {
blurView.alpha = 0
}
我认为您打算在视图出现之前只调用一次,因为我看到您在 viewDidAppear(animated: Bool)
中将 alpha
动画化为 1
。但是,无论何时系统出于任何原因重新评估布局,都会调用 viewDidLayoutSubviews()
并且 blurView
上的 alpha
将返回 0
如果 returningFromTagView
是 false
。这就是为什么当您调用键盘(触发布局重新评估)时,此视图消失的原因。 Xcode 还会警告您在控制台中制作 alpha
0
(它会破坏视觉效果,直到不透明度 returns 变为 1
)。将上面的代码放在 viewDidLoad()
方法中,您会看到 blurView
返回。 alpha
只需要在视图加载时设置为 0
一次。
其他视图的问题有点难看,但罪魁祸首又是您对 viewDidLayoutSubviews()
的使用。我想你很困惑为什么即使你已经非常彻底地使用 keyboardNotfication()
方法来设置框架,将视图放在前面,确保它们没有被隐藏,为什么视图没有出现,然后记录这一切。但是在 keyboardNotification()
方法完成后,布局系统再次被触发,我看到你在各处移动视图的框架:
if returningFromTagView {
setX(-titleView.frame.size.width, v: titleView)
setX(-tagView.frame.size.width, v: tagView)
setX(-(cancelButton.frame.size.width + 20 + nextButton.frame.size.width), v: cancelButton)
setX(-nextButton.frame.size.width, v: nextButton)
} else {
setX(-titleView.frame.size.width, v: titleView)
setX(view.frame.size.width, v: tagView)
setX(-cancelButton.frame.size.width, v: cancelButton)
setX(view.frame.size.width, v: nextButton)
}
每次更改布局时,您都会将视图移出屏幕!调用键盘后暂停程序,并使用 Xcode 6 强大的新捕获视图层次结构功能查看视图层次结构。它位于“调试”>“查看调试”>“捕获视图层次结构”中。这些观点只是躲在一边。
我想您只是在视图出现时尝试执行此操作一次,以支持您的过渡动画,但无论是视图刚刚出现还是出现键盘等小变化,它都会被调用。我建议您以其他方式实现这些动画,例如使用视图的转换或使用自动布局约束(尽管情节提要中缺少很多约束)来制作动画。 viewDidLayoutSubviews()
确实是一个在布局系统完成其工作后在布局中到处捏造东西的地方。您应该有充分的理由使用它。它有一个很好的特性,可以覆盖你的自动布局约束,让你在不玩弄约束的情况下为这些视图设置动画(因为该方法发生在 updateConstraints()
和 layoutSubviews()
方法之后),这就是为什么我们不能把上面的代码改为 viewWillAppear(animated: Bool)
之类的方法(因为自动布局约束会在稍后的布局期间抵消动画),但是 viewDidLayoutSubviews()
并不是一种支持基本动画的方法。
尽管如此,这里有一些简单的方法可以让您的应用程序再次运行并让您看到发生了什么:
为您的 NewSessionVC 视图控制器创建一个 属性 var comingFromSessionView: Bool
属性。在SessionVC的prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
中,添加nextVC.comingFromSessionView = true
然后把代码块从上面的viewDidLayoutSubviews()
改成这样:
if returningFromTagView {
setX(-titleView.frame.size.width, v: titleView)
setX(-tagView.frame.size.width, v: tagView)
setX(-(cancelButton.frame.size.width + 20 + nextButton.frame.size.width), v: cancelButton)
setX(-nextButton.frame.size.width, v: nextButton)
} else if comingFromSessionView {
setX(-titleView.frame.size.width, v: titleView)
setX(view.frame.size.width, v: tagView)
setX(-cancelButton.frame.size.width, v: cancelButton)
setX(view.frame.size.width, v: nextButton)
}
我们将在完成 viewDidAppear
期间将这些 Bool
切换为 false
:
override func viewDidAppear(animated: Bool) {
...
if returningFromTagView {
...
returningFromTagView = false
} else if comingFromSessionView {
...
comingFromSessionView = false
}
}
现在当键盘被召唤时,您的视图就在您离开它们的地方!
上面的代码不是很好。我宁愿远离 viewDidLayoutSubviews()
来制作这些动画。但希望你能看到现在发生了什么。你的 viewsDidLayoutSubviews()
一直在偷走你的观点。
我在视频中加入了 link 来说明我遇到的问题:
https://dl.dropboxusercontent.com/u/39330138/Bug_Demo1.mov
有两个视图控制器,第一个不模糊且不太重要。单击加号按钮时,应用程序会转到一个新控制器(没有动画),在 prepareForSegue()
中,我使用 UIGraphicsBeginImageContext
和 UIGraphicsGetImageFromCurrentImageContext
从当前视图中捕获 UIImage
并将其传递给下一个。
当新视图出现时,我使用 UIVisualEffectView
创建一个模糊视图并将其作为子视图添加到 'background' 的图像视图中。然后,它的不透明度是动画的,同时 2 个视图和 2 个按钮在屏幕上用 UIView
动画和 springWithDamping
动画,给人一种视图模糊和项目在顶部动画的错觉。
顶部视图中嵌入了一个 UITextField
,当点击时调用 becomeFirstResponder()
并使所有覆盖(新会话、标签和按钮)视图包括嵌入在背景中的视觉效果视图图片视图消失。
我详细介绍的原因是因为我不确定问题到底是什么。但是,我怀疑它与 Xcode 6.
中的 AutoLayout/Size 类有关有谁知道为什么会发生这种情况以及如何解决? 如果您需要更多信息,请告诉我。
谢谢!
编辑:
当我在单击 TextField 后记录视图时,所有的框架看起来都一样。
编辑 2:
这里是一个 link 演示项目,将包含视频中的所有功能: https://dl.dropboxusercontent.com/u/39330138/DEMO%20APP.zip
这里发生了几件事,但罪魁祸首是您对 viewDidLayoutSubviews()
的使用。任何时候系统必须重新评估布局时都会调用它。我看到你用那种方法将 UIVisualEffectView
的 alpha
设置为 0
:
if !returningFromTagView {
blurView.alpha = 0
}
我认为您打算在视图出现之前只调用一次,因为我看到您在 viewDidAppear(animated: Bool)
中将 alpha
动画化为 1
。但是,无论何时系统出于任何原因重新评估布局,都会调用 viewDidLayoutSubviews()
并且 blurView
上的 alpha
将返回 0
如果 returningFromTagView
是 false
。这就是为什么当您调用键盘(触发布局重新评估)时,此视图消失的原因。 Xcode 还会警告您在控制台中制作 alpha
0
(它会破坏视觉效果,直到不透明度 returns 变为 1
)。将上面的代码放在 viewDidLoad()
方法中,您会看到 blurView
返回。 alpha
只需要在视图加载时设置为 0
一次。
其他视图的问题有点难看,但罪魁祸首又是您对 viewDidLayoutSubviews()
的使用。我想你很困惑为什么即使你已经非常彻底地使用 keyboardNotfication()
方法来设置框架,将视图放在前面,确保它们没有被隐藏,为什么视图没有出现,然后记录这一切。但是在 keyboardNotification()
方法完成后,布局系统再次被触发,我看到你在各处移动视图的框架:
if returningFromTagView {
setX(-titleView.frame.size.width, v: titleView)
setX(-tagView.frame.size.width, v: tagView)
setX(-(cancelButton.frame.size.width + 20 + nextButton.frame.size.width), v: cancelButton)
setX(-nextButton.frame.size.width, v: nextButton)
} else {
setX(-titleView.frame.size.width, v: titleView)
setX(view.frame.size.width, v: tagView)
setX(-cancelButton.frame.size.width, v: cancelButton)
setX(view.frame.size.width, v: nextButton)
}
每次更改布局时,您都会将视图移出屏幕!调用键盘后暂停程序,并使用 Xcode 6 强大的新捕获视图层次结构功能查看视图层次结构。它位于“调试”>“查看调试”>“捕获视图层次结构”中。这些观点只是躲在一边。
我想您只是在视图出现时尝试执行此操作一次,以支持您的过渡动画,但无论是视图刚刚出现还是出现键盘等小变化,它都会被调用。我建议您以其他方式实现这些动画,例如使用视图的转换或使用自动布局约束(尽管情节提要中缺少很多约束)来制作动画。 viewDidLayoutSubviews()
确实是一个在布局系统完成其工作后在布局中到处捏造东西的地方。您应该有充分的理由使用它。它有一个很好的特性,可以覆盖你的自动布局约束,让你在不玩弄约束的情况下为这些视图设置动画(因为该方法发生在 updateConstraints()
和 layoutSubviews()
方法之后),这就是为什么我们不能把上面的代码改为 viewWillAppear(animated: Bool)
之类的方法(因为自动布局约束会在稍后的布局期间抵消动画),但是 viewDidLayoutSubviews()
并不是一种支持基本动画的方法。
尽管如此,这里有一些简单的方法可以让您的应用程序再次运行并让您看到发生了什么:
为您的 NewSessionVC 视图控制器创建一个 属性 var comingFromSessionView: Bool
属性。在SessionVC的prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
中,添加nextVC.comingFromSessionView = true
然后把代码块从上面的viewDidLayoutSubviews()
改成这样:
if returningFromTagView {
setX(-titleView.frame.size.width, v: titleView)
setX(-tagView.frame.size.width, v: tagView)
setX(-(cancelButton.frame.size.width + 20 + nextButton.frame.size.width), v: cancelButton)
setX(-nextButton.frame.size.width, v: nextButton)
} else if comingFromSessionView {
setX(-titleView.frame.size.width, v: titleView)
setX(view.frame.size.width, v: tagView)
setX(-cancelButton.frame.size.width, v: cancelButton)
setX(view.frame.size.width, v: nextButton)
}
我们将在完成 viewDidAppear
期间将这些 Bool
切换为 false
:
override func viewDidAppear(animated: Bool) {
...
if returningFromTagView {
...
returningFromTagView = false
} else if comingFromSessionView {
...
comingFromSessionView = false
}
}
现在当键盘被召唤时,您的视图就在您离开它们的地方!
上面的代码不是很好。我宁愿远离 viewDidLayoutSubviews()
来制作这些动画。但希望你能看到现在发生了什么。你的 viewsDidLayoutSubviews()
一直在偷走你的观点。