以编程方式定位 UIToolbar 的最佳方式(有或没有 UIToolbarDelegate)?
Posted
技术标签:
【中文标题】以编程方式定位 UIToolbar 的最佳方式(有或没有 UIToolbarDelegate)?【英文标题】:Best way to position UIToolbar programmatically (with or without UIToolbarDelegate)? 【发布时间】:2018-02-28 04:11:17 【问题描述】:我正在 Playgound 中实现导航栏下方的分段控件。
这似乎是个经典问题,有人问过:
UISegmentedControl below UINavigationbar in ios 7 Add segmented control to navigation bar and keep title with buttons在UIBarPositioningDelegate
的文档中,它说,
UINavigationBarDelegate、UISearchBarDelegate 和 UIToolbarDelegate 协议扩展了这个协议以允许 这些条在屏幕上的位置。
在UIBarPosition的文档中:
案例顶部
指定栏位于其包含视图的顶部。
在UIToolbar.delegate的文档中:
当工具栏由 导航控制器。默认值为 nil。
我目前的解决方案如下(保留注释掉的代码以供参考和方便):
import UIKit
import PlaygroundSupport
class ViewController : UIViewController, UIToolbarDelegate
let toolbar : UIToolbar =
let ret = UIToolbar()
let segmented = UISegmentedControl(items: ["Good", "Bad"])
let barItem = UIBarButtonItem(customView: segmented)
ret.setItems([barItem], animated: false)
return ret
()
override func viewDidLoad()
super.viewDidLoad()
view.addSubview(toolbar)
// toolbar.delegate = self
override func viewDidLayoutSubviews()
toolbar.frame = CGRect(
x: 0,
y: navigationController?.navigationBar.frame.height ?? 0,
width: navigationController?.navigationBar.frame.width ?? 0,
height: 44
)
func position(for bar: UIBarPositioning) -> UIBarPosition
return .topAttached
//class Toolbar : UIToolbar
// override var barPosition: UIBarPosition
// return .topAttached
//
//
let vc = ViewController()
vc.title = "Try"
vc.view.backgroundColor = .red
// Another way to add toolbar...
// let segmented = UISegmentedControl(items: ["Good", "Bad"])
// let barItem = UIBarButtonItem(customView: segmented)
// vc.toolbarItems = [barItem]
// Navigation Controller
let navVC = UINavigationController(navigationBarClass: UINavigationBar.self, toolbarClass: UIToolbar.self)
navVC.pushViewController(vc, animated: true)
navVC.preferredContentSize = CGSize(width: 375, height: 640)
// navVC.isToolbarHidden = false
// Page setup
PlaygroundPage.current.liveView = navVC
PlaygroundPage.current.needsIndefiniteExecution = true
如您所见,这不使用UIToolbarDelegate
。
UIToolbarDelegate
(提供position(for:)
)如何在这种情况下发挥作用?由于我们总是可以定位自己(手动或使用自动布局),UIToolbarDelegate
的用例是什么?
@Leo Natan 在the first question link above 中的回答提到了UIToolbarDelegate
,但似乎工具栏放置在Interface Builder 中。
此外,如果我们不在这里使用UIToolbarDelegate
,为什么不直接使用UIView
而不是UIToolbar
?
【问题讨论】:
【参考方案1】:试试这个
UIView *containerVw = [[UIView alloc] initWithFrame:CGRectMake(0, 64, 320, 60)];
containerVw.backgroundColor = UIColorFromRGB(0xffffff);
[self.view addSubview:containerVw];
UIView *bottomView = [[UIView alloc] initWithFrame:CGRectMake(0, 124, 320, 1)];
bottomView.backgroundColor = [UIColor grayColor];
[self.view addSubview:bottomView];
UISegmentedControl *sg = [[UISegmentedControl alloc] initWithItems:@[@"Good", @"Bad"]];
sg.frame = CGRectMake(10, 10, 300, 40);
[view addSubview:sg];
for (UIView *view in self.navigationController.navigationBar.subviews)
for (UIView *subView in view.subviews)
[subView isKindOfClass:[UIImageView class]];
subView.hidden = YES;
【讨论】:
谢谢!但是如果我们确实使用 UIToolbar 和 UIToolbarDelegate 呢?请参阅我的问题的最后几段。【参考方案2】:通过设置工具栏的委托并让委托方法返回.top
,您可以在工具栏底部获得正常的阴影。如果您还将工具栏框架调整为高一点,它将覆盖导航栏的阴影,最终结果将是看起来更高的导航栏并添加了分段控件。
class ViewController : UIViewController, UIToolbarDelegate
lazy var toolbar: UIToolbar =
let ret = UIToolbar()
ret.delegate = self
let segmented = UISegmentedControl(items: ["Good", "Bad"])
let barItem = UIBarButtonItem(customView: segmented)
ret.setItems([barItem], animated: false)
return ret
()
override func viewDidLoad()
super.viewDidLoad()
view.addSubview(toolbar)
toolbar.delegate = self
override func viewDidLayoutSubviews()
toolbar.frame = CGRect(
x: 0,
y: navigationController?.navigationBar.frame.height - 1 ?? 0,
width: navigationController?.navigationBar.frame.width ?? 0,
height: toolbar.frame.height
)
func position(for bar: UIBarPositioning) -> UIBarPosition
return .top
【讨论】:
【参考方案3】:UIToolbarDelegate(提供位置(for:))如何在这种情况下发挥作用?既然我们总是可以定位自己(手动或使用自动布局),那么 UIToolbarDelegate 的用例是什么?
我真的不知道UIToolbarDelegate
是如何发挥作用的,如果你改变UINavigationController.toolbar
它会崩溃,“你不能手动设置UINavigationController管理的UIToolbar委托”,而且如果您尝试更改工具栏的约束或其translatesAutoresizingMaskIntoConstraints
属性,也会发生同样的情况。
此外,如果我们这里不使用 UIToolbarDelegate,为什么不直接使用普通的 UIView 而不是 UIToolbar?
这似乎是一个合理的问题。我想这个问题的答案是你有一个UIView
子类,它已经具有UIToolbar
的行为,那么我们为什么要创建另一个类似UIToolbar
的类,除非你只想要导航栏下方的一些视图。
我知道有 2 个选项。
1) 与Move UINavigationController's toolbar to the top to lie underneath navigation bar相关
当您必须在由您的NavigationController
管理的其他ViewControllers
中显示工具栏时,第一种方法可能会有所帮助。
您可以继承UINavigationController
,并在设置值时更改工具栏的Y轴位置。
import UIKit
private var context = 0
class NavigationController: UINavigationController
private var inToolbarFrameChange = false
var observerBag: [NSKeyValueObservation] = []
override func awakeFromNib()
super.awakeFromNib()
self.inToolbarFrameChange = false
override func viewDidLoad()
super.viewDidLoad()
observerBag.append(
toolbar.observe(\.center, options: .new) toolbar, _ in
if !self.inToolbarFrameChange
self.inToolbarFrameChange = true
toolbar.frame = CGRect(
x: 0,
y: self.navigationBar.frame.height + UIApplication.shared.statusBarFrame.height,
width: toolbar.frame.width,
height: toolbar.frame.height
)
self.inToolbarFrameChange = false
)
override func setToolbarHidden(_ hidden: Bool, animated: Bool)
super.setToolbarHidden(hidden, animated: false)
var rectTB = self.toolbar.frame
rectTB = .zero
2) 您可以创建自己的UIToolbar
并将其添加到UIViewController
的视图中。然后,将约束添加到安全区域的前导、尾随和顶部。
import UIKit
final class ViewController: UIViewController
private let toolbar = UIToolbar()
private let segmentedControl: UISegmentedControl =
let control = UISegmentedControl(items: ["Op 1", "Op 2"])
control.isEnabled = false
return control
()
override func loadView()
super.loadView()
setupToolbar()
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
navigationController?.navigationBar.hideBorderLine()
private func setupToolbar()
let barItem = UIBarButtonItem(customView: segmentedControl)
toolbar.setItems([barItem], animated: false)
toolbar.isTranslucent = false
toolbar.isOpaque = false
view.addSubview(toolbar)
toolbar.translatesAutoresizingMaskIntoConstraints = false
toolbar.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
toolbar.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
toolbar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
private extension UINavigationBar
func showBorderLine()
findBorderLine().isHidden = false
func hideBorderLine()
findBorderLine().isHidden = true
private func findBorderLine() -> UIImageView!
return self.subviews
.flatMap $0.subviews
.compactMap $0 as? UIImageView
.filter $0.bounds.size.width == self.bounds.size.width
.filter $0.bounds.size.height <= 2
.first
【讨论】:
以上是关于以编程方式定位 UIToolbar 的最佳方式(有或没有 UIToolbarDelegate)?的主要内容,如果未能解决你的问题,请参考以下文章