【问题标题】:Best way to position UIToolbar programmatically (with or without UIToolbarDelegate)?以编程方式定位 UIToolbar 的最佳方式(有或没有 UIToolbarDelegate)?
【发布时间】:2018-02-28 04:11:17
【问题描述】:

我正在 Playgound 中实现导航栏下方的分段控件。

这似乎是个经典问题,有人问过:

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

【问题讨论】:

    标签: ios swift cocoa-touch uitoolbar uitoolbarposition


    【解决方案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
          }
      }
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-05
        • 1970-01-01
        • 2015-12-18
        相关资源
        最近更新 更多