将子层添加到导航栏与过渡冲突
Adding sublayer to navigation bar conflicts with transition
我需要在 UIInavigationBar
下面添加一个边框,我也希望它是全局的,所以我转到我的顶层 UIViewController
并在其中放置一个与此类似的方法:
- (void)setupNavigationBarBorder {
if (![self isKindOfClass:[CombinedViewController class]]) {
CGRect borderRect = CGRectMake(0, self.navigationController.navigationBar.frame.size.height-0.5f, self.navigationController.navigationBar.frame.size.width, 0.5f);
CALayer *border = [CALayer layer];
border.frame = borderRect;
border.name = @"border";
[border setBackgroundColor:[UIColor greenColor].CGColor];
[self.navigationController.navigationBar.layer addSublayer:border];
} else {
NSArray* sublayers = [NSArray arrayWithArray:self.navigationController.navigationBar.layer.sublayers];
for (CALayer *layer in sublayers) {
if ([layer.name isEqualToString:@"border"]) {
[layer removeFromSuperlayer];
}
}
}
}
这是一个简单的触发器,用于绘制和删除此子图层。非常简单。所以起初我决定将这段代码放在 viewDidLoad
中,但事实证明这不是最好的主意,因为我实际上是在修改 UINavigationBar
的全局状态。下一步是将此方法调用放到 viewWillAppear
中,大多数情况下它没问题,但是当我从这个 ComboFeedViewController
移动到应该有这个边框的那个时......好吧,它会立即被绘制出来。
我希望它在过渡结束时显示,或者在最好的情况下,与过渡一起出现。我怎样才能做到这一点?
请查看 Apple 的 Customizing UINavigationBar 示例代码。该示例涵盖了自定义导航栏方面的大量内容。
如果您想直观地修改导航栏,但只在导航堆栈的其中一个屏幕中显示该修改,那么最好的办法是挂钩这两种方法:
viewWillAppear:
用于修改栏中的内容
viewWillDisappear:
用于隐藏该修改
didAppear 或 didDisappear 方法也适用于您。但我发现挂钩到 willAppear/willDisappear 变体并尊重动画参数对我来说创造了奇迹。
这是从我的一个项目中取出的示例代码,它隐藏了导航栏下方的细线(边框)(如果你想在其中一个屏幕中的 UINavigationBar 下方粘贴一个 UIToolbar 以获得双倍尺寸,这很有用酒吧):
class ViewController: UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// We need to hide the hairline, so the bar appears
// continous with the toolbar below it.
navigationController?.navigationBar.setHairlineEnabled(false, animated: animated)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.navigationBar.setHairlineEnabled(true, animated: animated)
}
}
extension UINavigationBar {
/// Hides the hairline below the navigation bar by walking
/// the subview hierarchy and finding a 0.5 or 1 pt tall view.
func setHairlineEnabled(enabled: Bool, animated: Bool) {
guard subviews.count > 0 else { return }
let firstSubview = subviews[0]
for subview in firstSubview.subviews {
let height = subview.bounds.height
if height == CGFloat(0.5) || height == CGFloat(1.0) {
let work = {
subview.alpha = enabled ? CGFloat(1) : CGFloat(0)
}
if animated {
UIView.animateWithDuration(NSTimeInterval(UINavigationControllerHideShowBarDuration), animations: {
work()
})
} else {
work()
}
break
}
}
}
}
虽然批准的答案似乎解决了我的问题,但我在从我的应用程序的特定视图转换时遇到了一些问题。主要是因为 viewVillAppear
在 向后滑动 手势开始时立即触发。
这个缺陷太小了,甚至不值得花很多时间在它上面,但它仍然在我脑海中的某个地方,所以...我找到了一个名为 KMNavigationBarTransition
swizzles 方法,因此您可以为每个视图控制器设置不同的导航栏外观 - 这正是我想要的。
现在我可以简单地将我的 showBorder
和 hideBorder
自定义方法放在特定视图控制器的 viewDidLoad
中,一切都完美无缺!
我需要在 UIInavigationBar
下面添加一个边框,我也希望它是全局的,所以我转到我的顶层 UIViewController
并在其中放置一个与此类似的方法:
- (void)setupNavigationBarBorder {
if (![self isKindOfClass:[CombinedViewController class]]) {
CGRect borderRect = CGRectMake(0, self.navigationController.navigationBar.frame.size.height-0.5f, self.navigationController.navigationBar.frame.size.width, 0.5f);
CALayer *border = [CALayer layer];
border.frame = borderRect;
border.name = @"border";
[border setBackgroundColor:[UIColor greenColor].CGColor];
[self.navigationController.navigationBar.layer addSublayer:border];
} else {
NSArray* sublayers = [NSArray arrayWithArray:self.navigationController.navigationBar.layer.sublayers];
for (CALayer *layer in sublayers) {
if ([layer.name isEqualToString:@"border"]) {
[layer removeFromSuperlayer];
}
}
}
}
这是一个简单的触发器,用于绘制和删除此子图层。非常简单。所以起初我决定将这段代码放在 viewDidLoad
中,但事实证明这不是最好的主意,因为我实际上是在修改 UINavigationBar
的全局状态。下一步是将此方法调用放到 viewWillAppear
中,大多数情况下它没问题,但是当我从这个 ComboFeedViewController
移动到应该有这个边框的那个时......好吧,它会立即被绘制出来。
我希望它在过渡结束时显示,或者在最好的情况下,与过渡一起出现。我怎样才能做到这一点?
请查看 Apple 的 Customizing UINavigationBar 示例代码。该示例涵盖了自定义导航栏方面的大量内容。
如果您想直观地修改导航栏,但只在导航堆栈的其中一个屏幕中显示该修改,那么最好的办法是挂钩这两种方法:
viewWillAppear:
用于修改栏中的内容viewWillDisappear:
用于隐藏该修改
didAppear 或 didDisappear 方法也适用于您。但我发现挂钩到 willAppear/willDisappear 变体并尊重动画参数对我来说创造了奇迹。
这是从我的一个项目中取出的示例代码,它隐藏了导航栏下方的细线(边框)(如果你想在其中一个屏幕中的 UINavigationBar 下方粘贴一个 UIToolbar 以获得双倍尺寸,这很有用酒吧):
class ViewController: UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// We need to hide the hairline, so the bar appears
// continous with the toolbar below it.
navigationController?.navigationBar.setHairlineEnabled(false, animated: animated)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.navigationBar.setHairlineEnabled(true, animated: animated)
}
}
extension UINavigationBar {
/// Hides the hairline below the navigation bar by walking
/// the subview hierarchy and finding a 0.5 or 1 pt tall view.
func setHairlineEnabled(enabled: Bool, animated: Bool) {
guard subviews.count > 0 else { return }
let firstSubview = subviews[0]
for subview in firstSubview.subviews {
let height = subview.bounds.height
if height == CGFloat(0.5) || height == CGFloat(1.0) {
let work = {
subview.alpha = enabled ? CGFloat(1) : CGFloat(0)
}
if animated {
UIView.animateWithDuration(NSTimeInterval(UINavigationControllerHideShowBarDuration), animations: {
work()
})
} else {
work()
}
break
}
}
}
}
虽然批准的答案似乎解决了我的问题,但我在从我的应用程序的特定视图转换时遇到了一些问题。主要是因为 viewVillAppear
在 向后滑动 手势开始时立即触发。
这个缺陷太小了,甚至不值得花很多时间在它上面,但它仍然在我脑海中的某个地方,所以...我找到了一个名为 KMNavigationBarTransition
swizzles 方法,因此您可以为每个视图控制器设置不同的导航栏外观 - 这正是我想要的。
现在我可以简单地将我的 showBorder
和 hideBorder
自定义方法放在特定视图控制器的 viewDidLoad
中,一切都完美无缺!