代码之家  ›  专栏  ›  技术社区  ›  BartoszCichecki

嵌入在NavigationItem中的断开的UISearchBar动画

  •  21
  • BartoszCichecki  · 技术社区  · 7 年前

    如下图所示,有两个UIViewController,一个接一个,都有搜索栏。问题是动画,当搜索栏在第一个视图控制器上可见但在第二个视图控制器上不可见时,动画很难看。搜索栏所占据的区域保持在屏幕上,然后突然消失。

    Demo

    代码非常基本(项目中没有进行其他更改):

    (我主要用C语言编写,因此这段代码中可能有错误。)

    ViewController。swift:

    import UIKit
    
    class ViewController: UITableViewController, UISearchResultsUpdating {
    
    override func loadView() {
        super.loadView()
    
        definesPresentationContext = true;
    
        navigationController?.navigationBar.prefersLargeTitles = true;
        navigationItem.largeTitleDisplayMode = .automatic;
        navigationItem.title = "VC"
    
        tableView.insetsContentViewsToSafeArea = true;
        tableView.dataSource = self;
    
        refreshControl = UIRefreshControl();
        refreshControl?.addTarget(self, action: #selector(ViewController.handleRefresh(_:)), for: UIControlEvents.valueChanged)
        tableView.refreshControl = refreshControl;
    
        let stvc = UITableViewController();
        stvc.tableView.dataSource = self;
    
        let sc = UISearchController(searchResultsController: stvc);
        sc.searchResultsUpdater = self;
        navigationItem.searchController = sc;
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCell(withIdentifier: "cell1");
        if (cell == nil) {
            cell = UITableViewCell(style: .default, reuseIdentifier: "cell1");
        }
        cell?.textLabel?.text = "cell " + String(indexPath.row);
        return cell!;
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20;
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let vc = ViewController();
        navigationController?.pushViewController(vc, animated: true);
    }
    
    @objc func handleRefresh(_ refreshControl: UIRefreshControl) {
        DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2), execute: {
            refreshControl.endRefreshing();
        })
    }
    
    func updateSearchResults(for searchController: UISearchController) {
    }
    }
    

    AppDelegate。swift:

    import UIKit
    
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    
        window = UIWindow(frame: UIScreen.main.bounds);
        window?.rootViewController = UINavigationController(rootViewController: ViewController());
        window?.makeKeyAndVisible();
    
        UINavigationBar.appearance().barTintColor = UIColor.red;
    
        return true
    }
    }
    

    7 回复  |  直到 6 年前
        1
  •  18
  •   silicon_valley    7 年前

    看起来苹果仍需要解决在新的大标题风格中使用UISearchBar的问题。如果 UIViewController navigationItem.searchController 设置后,动画效果良好。在两个实例之间导航时 UIViewController 如果两者都有一个searchController集,那么就会出现您描述的导航栏高度跳到哪里的问题。

    您可以通过创建 UISearchController viewDidAppear 被调用(而不是在 loadView )和设置 导航项。搜索控制器 至零开启 viewDidDisappear

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    
        DispatchQueue.main.async {
            let stvc = UITableViewController()
            stvc.tableView.dataSource = self
    
            let sc = UISearchController(searchResultsController: stvc)
            sc.searchResultsUpdater = self
            self.navigationItem.searchController = sc
        }
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
    
        self.navigationItem.searchController = nil
    }
    

    异步调度的原因是当设置 导航项。搜索控制器

    Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Only one palette with a top boundary edge can be active outside of a transition. Current active palette is <_UINavigationControllerManagedSearchPalette: 0x7fad67117e80; frame = (0 116; 414 0); layer = <CALayer: 0x60400002c8e0>>'
    

    我知道这只是一个解决方法,但希望这能暂时帮助你,直到苹果解决了在两个视图控制器之间导航的问题 设置他们的 navigationItem .

        2
  •  1
  •   Andrew Bennet    5 年前

    这个 accepted answer navigationItem 如果第一个搜索栏处于活动状态,则在推送的视图控制器中。

    answer by stu ,但不需要干预约束。该方法是在序列点确定搜索栏是否可见。如果是,我们指示目标视图控制器使其搜索栏在加载时可见。这意味着导航项动画的行为正确:

    Search bar correctly animating

    UIViewController1 UIViewController2 哪里 1 2

    class ViewController1: UITableViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let searchController = UISearchController(searchResultsController: nil)
            searchController.obscuresBackgroundDuringPresentation = false
            navigationItem.searchController = searchController
    
            definesPresentationContext = true
        }
    
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if let viewController2 = segue.destination as? ViewController2, let searchController = navigationItem.searchController {
    
                // If the search bar is visible (but not active, which would make it visible but at the top of the view)
                // in this view controller as we are preparing to segue, instruct the destination view controller that its
                // search bar should be visible from load.
                viewController2.forceSearchBarVisibleOnLoad = !searchController.isActive && searchController.searchBar.frame.height > 0
            }
        }
    }
    
    class ViewController2: UITableViewController {
    
        var forceSearchBarVisibleOnLoad = false
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let searchController = UISearchController(searchResultsController: nil)
            searchController.obscuresBackgroundDuringPresentation = false
            navigationItem.searchController = searchController
    
            // If on load we want to force the search bar to be visible, we make it so that it is always visible to start with
            if forceSearchBarVisibleOnLoad {
                navigationItem.hidesSearchBarWhenScrolling = false
            }
        }
    
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            // When the view has appeared, we switch back the default behaviour of the search bar being hideable.
            // The search bar will already be visible at this point, thus achieving what we aimed to do (have it
            // visible during the animation).
            navigationItem.hidesSearchBarWhenScrolling = true
        }
    }
    
    
        3
  •  0
  •   stu    5 年前

    我解决这个问题的方法是更新保持 UISearchBar UIViewController

    下面的代码假设您有一个包含 UISearchController 实例 调用的子类 searchController

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
    
        if
            animated
                && !searchController.isActive
                && !searchController.isEditing
                && navigationController.map({$0.viewControllers.last != self}) ?? false,
            let searchBarSuperview = searchController.searchBar.superview,
            let searchBarHeightConstraint = searchBarSuperview.constraints.first(where: {
                $0.firstAttribute == .height
                    && $0.secondItem == nil
                    && $0.secondAttribute == .notAnAttribute
                    && $0.constant > 0
            }) {
    
            UIView.performWithoutAnimation {
                searchBarHeightConstraint.constant = 0
                searchBarSuperview.superview?.layoutIfNeeded()
            }
        }
    }
    

    您可以删除 performWithoutAnimation layoutIfNeeded

    我希望苹果能在稍后的iOS版本中解决这个问题,在撰写本文时,当前的版本是12.1.4。

        4
  •  0
  •   David Kadlcek    5 年前

    VC1

    override func viewDidLoad() {
       if #available(iOS 11.0, *) {
            navigationItem.hidesSearchBarWhenScrolling = false
            navigationItem.searchController = searchController
        }
    }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if #available(iOS 11.0, *) {
            navigationItem.hidesSearchBarWhenScrolling = true
        }
    }
    

    VC2 ,使用相同的代码。

    这将比set更清楚地解决您的问题 serchController nil

        5
  •  0
  •   Tim Hazhyi    5 年前

        6
  •  0
  •   Cường Nguyễn    4 年前

    在花了几天时间解决和调查之后,我也遇到了同样的问题。最后,我创建了一个自定义视图,并用该视图替换标题视图。 iOS 11是个糟糕的苹果。

        7
  •  -4
  •   shiju86.v    6 年前

    我在viewDidLoad()中添加了这段代码,当我移动到选项卡的b/w时,它正在工作

    searchController.dimsBackgroundDuringPresentation