动画更改导航控制器中的标题文本
Animate change to title text in Navigation Controller
我正在尝试对嵌入在 UINavigationController
中的详细信息 UITableViewController
的标题进行动画更改。
这两个关于堆栈溢出的问答似乎最相关...
- Animate nav bar title text change 乔丹 H 的 OP;
- Transition Navigation Bar Title,雅各布的 OP
我尝试了许多代码变体,主要是根据 Ashley Mills answer 和其他人的回答,但基本上我似乎无法使动画工作!
使用Xcode 10.2.
在Swift 5 中写笔记
我正在使用 Master-Detail UITableViewController
设置来管理列表和该列表中项目的详细信息。
我正在使用大标题...
navigationController?.navigationBar.prefersLargeTitles = true
...和一个搜索控制器...
navigationItem.searchController = <<mySearchController>>
navigationItem.hidesSearchBarWhenScrolling = false
definesPresentationContext = true
...全部在主视图控制器的 viewDidLoad()
方法中设置。
这是我所做的。
在我的项目中:
导入 QuartzCore 框架,根据 Jack Bellis 的评论 "you'll need to add the QuartzCore framework via [project]>[target]>Build Phases>Link Binaries>QuartzCore.framework.";
在主视图控制器中:
import QuartzCore
;
在细节视图控制器中:
在 viewDidAppear(_ animated: Bool)
方法中,编写类似于上述 OP 答案的 CATransition
代码(更改大标题,但没有动画)。
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let animationTransition = CATransition()
animationTransition.duration = 2.0
animationTransition.type = CATransitionType.push
animationTransition.subtype = CATransitionSubtype.fromTop
navigationController!.navigationBar.layer.add(animationTransition, forKey: "pushText")
navigationItem.title = <<NEW TITLE TEXT>>
}
在 viewDidAppear(_ animated: Bool)
方法中,编写上述 OP 答案中建议的 CATransition
代码的替代方案(仅在导航栏并且不更改大标题)。
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let animationTransition = CATransition()
animationTransition.duration = 2.0
animationTransition.type = CATransitionType.fade
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 44))
label.text = <<NEW TITLE TEXT>>
navigationItem.titleView = label
navigationItem.titleView!.layer.add(animationTransition, forKey: "fadeText")
}
我也试过包括 CAMediaTimingFunction
...
animationTransition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
我也试过调用 setNeedsLayout
和 setNeedsDisplay
...
navigationItem.titleView?.setNeedsLayout()
navigationController?.navigationBar.setNeedsDisplay()
为清楚起见:
- 标题文本确实发生了变化,但没有动画;
- 代码写在
viewDidAppear(_ animated:)
方法中,因为我想让用户看到转换;
- 用于测试的持续时间很长(2 秒)- 我可能会减少这个 if/when 动画正在运行;
- 使用故事板中的检查器,我通读了所有
UITableView
/Controller
和 UINavigationController
设置,看看我是否遗漏了什么。
有什么建议吗?
所以我已经解决了。
我必须在 UINavigationBar
的子视图中找到包含标题文本的特定层,然后为该层设置动画。结果正是我想要的。
这是我的答案 (Swift 5 iOS 12)...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if (newTitle ?? "").isEmpty == false { // only proceed with a valid value for newTitle.
// CATransition code
let titleAnimation = CATransition()
titleAnimation.duration = 0.5
titleAnimation.type = CATransitionType.push
titleAnimation.subtype = CATransitionSubtype.fromRight
titleAnimation.timingFunction = CAMediaTimingFunction.init(name: CAMediaTimingFunctionName.easeInEaseOut)
// this is a detail view controller, so we must grab the reference
// to the parent view controller's navigation controller
// then cycle through until we find the title labels.
if let subviews = parent?.navigationController?.navigationBar.subviews {
for navigationItem in subviews {
for itemSubView in navigationItem.subviews {
if let largeLabel = itemSubView as? UILabel {
largeLabel.layer.add(titleAnimation, forKey: "changeTitle")
}
}
}
}
// finally set the title
navigationItem.title = newTitle
}
注意:不需要import QuartzCore
。
...这是我确定必须更改的内容的过程...
This SO Q&A 如何在导航栏中设置多行大标题? (iOS11 的新功能)
帮助我确定了下面详述的过程,因此特别感谢原始 post 和@Krunal 的 answer。
使用相同的代码循环遍历 UINavigationBar
的子视图(如上所述),我使用打印到终端来识别各种 UINavigationItem
及其子视图。
counter = 0
if let subviews = parent?.navigationController?.navigationBar.subviews {
for navigationItem in subviews {
print("____\(navigationItem)")
for itemSubView in navigationItem.subviews {
counter += 1
print("_______\(itemSubView)")
}
}
}
print("COUNTER: \(counter)")
此代码在终端中产生了以下打印结果(对于模拟器中的 iPhone 8 Plus 运行 iOS 12.2)...
____<_UIBarBackground: 0x7f922740c000; frame = (0 -20; 414 116); userInteractionEnabled = NO; layer = <CALayer: 0x6000026ce1c0>>
_______<UIImageView: 0x7f922740c9c0; frame = (0 116; 414 0.333333); userInteractionEnabled = NO; layer = <CALayer: 0x6000026ce7c0>>
_______<UIVisualEffectView: 0x7f922740cbf0; frame = (0 0; 414 116); layer = <CALayer: 0x6000026ce880>>
____<_UINavigationBarLargeTitleView: 0x7f922740f390; frame = (0 44; 414 52); clipsToBounds = YES; layer = <CALayer: 0x6000026cd840>>
_______<UILabel: 0x7f9227499fe0; frame = (20.1667 3.66667; 206.333 40.6667); text = 'Event Details'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000005bf250>>
____<_UINavigationBarContentView: 0x7f922740d660; frame = (0 0; 414 44); clipsToBounds = YES; layer = <CALayer: 0x6000026cea00>>
_______<_UIButtonBarStackView: 0x7f92274984a0; frame = (302 0; 100 44); layer = <CALayer: 0x60000261cc60>>
_______<_UIButtonBarButton: 0x7f922749a5c0; frame = (0 0; 82.3333 44); layer = <CALayer: 0x60000261ee60>>
_______<UILabel: 0x7f922749a2d0; frame = (155 11.6667; 104.333 20.3333); text = 'Event Details'; alpha = 0; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000005bfc50>>
____<_UINavigationBarModernPromptView: 0x7f9227602db0; frame = (0 0; 0 50); alpha = 0; hidden = YES; layer = <CALayer: 0x6000026e4600>>
COUNTER: 2
我实际上应用了两次动画 - _UINavigationBarLargeTitleView
中的 UILabel
layer
和 _UINavigationBarContentView
中的 UILabel
layer
.然而,这似乎并不重要,因为当大标题首次出现时,内容视图中的标签(我假设是当大标题从屏幕上滚动时导航栏中的 "old style" 标题)隐藏在viewDidAppear
.
顺便说一下,如果您加入以下两行,您还会有 multi-line 个大标题:
largeLabel.numberOfLines = 0
largeLabel.lineBreakMode = .byWordWrapping
但是,我还没有想出如何为大标题框的尺寸增加设置动画,所以立即更改为两行或更多行,恕我直言会破坏标题更改的动画。
尚未在设备上测试,但似乎对 iPhone 和 iPad 模拟人生都有效。
如果您发现任何错误,请告诉我,我会更新我的答案。
我正在尝试对嵌入在 UINavigationController
中的详细信息 UITableViewController
的标题进行动画更改。
这两个关于堆栈溢出的问答似乎最相关...
- Animate nav bar title text change 乔丹 H 的 OP;
- Transition Navigation Bar Title,雅各布的 OP
我尝试了许多代码变体,主要是根据 Ashley Mills answer 和其他人的回答,但基本上我似乎无法使动画工作!
使用Xcode 10.2.
在Swift 5 中写笔记我正在使用 Master-Detail UITableViewController
设置来管理列表和该列表中项目的详细信息。
我正在使用大标题...
navigationController?.navigationBar.prefersLargeTitles = true
...和一个搜索控制器...
navigationItem.searchController = <<mySearchController>>
navigationItem.hidesSearchBarWhenScrolling = false
definesPresentationContext = true
...全部在主视图控制器的 viewDidLoad()
方法中设置。
这是我所做的。
在我的项目中:
导入 QuartzCore 框架,根据 Jack Bellis 的评论 "you'll need to add the QuartzCore framework via [project]>[target]>Build Phases>Link Binaries>QuartzCore.framework.";
在主视图控制器中:
import QuartzCore
;在细节视图控制器中:
在
viewDidAppear(_ animated: Bool)
方法中,编写类似于上述 OP 答案的CATransition
代码(更改大标题,但没有动画)。override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let animationTransition = CATransition() animationTransition.duration = 2.0 animationTransition.type = CATransitionType.push animationTransition.subtype = CATransitionSubtype.fromTop navigationController!.navigationBar.layer.add(animationTransition, forKey: "pushText") navigationItem.title = <<NEW TITLE TEXT>> }
在
viewDidAppear(_ animated: Bool)
方法中,编写上述 OP 答案中建议的CATransition
代码的替代方案(仅在导航栏并且不更改大标题)。override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let animationTransition = CATransition() animationTransition.duration = 2.0 animationTransition.type = CATransitionType.fade let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 44)) label.text = <<NEW TITLE TEXT>> navigationItem.titleView = label navigationItem.titleView!.layer.add(animationTransition, forKey: "fadeText") }
我也试过包括
CAMediaTimingFunction
...animationTransition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
我也试过调用
setNeedsLayout
和setNeedsDisplay
...navigationItem.titleView?.setNeedsLayout() navigationController?.navigationBar.setNeedsDisplay()
为清楚起见:
- 标题文本确实发生了变化,但没有动画;
- 代码写在
viewDidAppear(_ animated:)
方法中,因为我想让用户看到转换; - 用于测试的持续时间很长(2 秒)- 我可能会减少这个 if/when 动画正在运行;
- 使用故事板中的检查器,我通读了所有
UITableView
/Controller
和UINavigationController
设置,看看我是否遗漏了什么。
有什么建议吗?
所以我已经解决了。
我必须在 UINavigationBar
的子视图中找到包含标题文本的特定层,然后为该层设置动画。结果正是我想要的。
这是我的答案 (Swift 5 iOS 12)...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if (newTitle ?? "").isEmpty == false { // only proceed with a valid value for newTitle.
// CATransition code
let titleAnimation = CATransition()
titleAnimation.duration = 0.5
titleAnimation.type = CATransitionType.push
titleAnimation.subtype = CATransitionSubtype.fromRight
titleAnimation.timingFunction = CAMediaTimingFunction.init(name: CAMediaTimingFunctionName.easeInEaseOut)
// this is a detail view controller, so we must grab the reference
// to the parent view controller's navigation controller
// then cycle through until we find the title labels.
if let subviews = parent?.navigationController?.navigationBar.subviews {
for navigationItem in subviews {
for itemSubView in navigationItem.subviews {
if let largeLabel = itemSubView as? UILabel {
largeLabel.layer.add(titleAnimation, forKey: "changeTitle")
}
}
}
}
// finally set the title
navigationItem.title = newTitle
}
注意:不需要import QuartzCore
。
...这是我确定必须更改的内容的过程...
This SO Q&A 如何在导航栏中设置多行大标题? (iOS11 的新功能) 帮助我确定了下面详述的过程,因此特别感谢原始 post 和@Krunal 的 answer。
使用相同的代码循环遍历 UINavigationBar
的子视图(如上所述),我使用打印到终端来识别各种 UINavigationItem
及其子视图。
counter = 0
if let subviews = parent?.navigationController?.navigationBar.subviews {
for navigationItem in subviews {
print("____\(navigationItem)")
for itemSubView in navigationItem.subviews {
counter += 1
print("_______\(itemSubView)")
}
}
}
print("COUNTER: \(counter)")
此代码在终端中产生了以下打印结果(对于模拟器中的 iPhone 8 Plus 运行 iOS 12.2)...
____<_UIBarBackground: 0x7f922740c000; frame = (0 -20; 414 116); userInteractionEnabled = NO; layer = <CALayer: 0x6000026ce1c0>>
_______<UIImageView: 0x7f922740c9c0; frame = (0 116; 414 0.333333); userInteractionEnabled = NO; layer = <CALayer: 0x6000026ce7c0>>
_______<UIVisualEffectView: 0x7f922740cbf0; frame = (0 0; 414 116); layer = <CALayer: 0x6000026ce880>>
____<_UINavigationBarLargeTitleView: 0x7f922740f390; frame = (0 44; 414 52); clipsToBounds = YES; layer = <CALayer: 0x6000026cd840>>
_______<UILabel: 0x7f9227499fe0; frame = (20.1667 3.66667; 206.333 40.6667); text = 'Event Details'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000005bf250>>
____<_UINavigationBarContentView: 0x7f922740d660; frame = (0 0; 414 44); clipsToBounds = YES; layer = <CALayer: 0x6000026cea00>>
_______<_UIButtonBarStackView: 0x7f92274984a0; frame = (302 0; 100 44); layer = <CALayer: 0x60000261cc60>>
_______<_UIButtonBarButton: 0x7f922749a5c0; frame = (0 0; 82.3333 44); layer = <CALayer: 0x60000261ee60>>
_______<UILabel: 0x7f922749a2d0; frame = (155 11.6667; 104.333 20.3333); text = 'Event Details'; alpha = 0; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000005bfc50>>
____<_UINavigationBarModernPromptView: 0x7f9227602db0; frame = (0 0; 0 50); alpha = 0; hidden = YES; layer = <CALayer: 0x6000026e4600>>
COUNTER: 2
我实际上应用了两次动画 - _UINavigationBarLargeTitleView
中的 UILabel
layer
和 _UINavigationBarContentView
中的 UILabel
layer
.然而,这似乎并不重要,因为当大标题首次出现时,内容视图中的标签(我假设是当大标题从屏幕上滚动时导航栏中的 "old style" 标题)隐藏在viewDidAppear
.
顺便说一下,如果您加入以下两行,您还会有 multi-line 个大标题:
largeLabel.numberOfLines = 0
largeLabel.lineBreakMode = .byWordWrapping
但是,我还没有想出如何为大标题框的尺寸增加设置动画,所以立即更改为两行或更多行,恕我直言会破坏标题更改的动画。
尚未在设备上测试,但似乎对 iPhone 和 iPad 模拟人生都有效。
如果您发现任何错误,请告诉我,我会更新我的答案。