在iOS 11中使用大型标题时调整条形按钮项的位置

时间:2017-07-26 05:13:39

标签: ios swift ios11 swift4

我正在使用iOS 11的大标题导航栏,但是当我添加一个条形按钮项时,它看起来很奇怪,位于与原始标题导航栏相同的位置。我想在标题很大时向下移动条形按钮项目,并在导航栏不再大时将其移回原始位置。这样做的最佳方式是什么?

这是显示条形按钮项目的奇怪位置的图像

largeTitleNavbar

我可以使用viewWillLayoutSubviews()动态获取导航栏高度,但我无法使用setTitlePositionAdjustment

更改小节按钮项的位置
override func viewWillLayoutSubviews() {
    guard let navbarHeight = self.navigationController?.navigationBar.frame.height else{ return }
}

5 个答案:

答案 0 :(得分:5)

为了解决我自己的问题,我只添加了一个按钮作为导航栏的子视图,并将右侧和底部约束设置为导航栏。现在,当导航栏改变大小时,该按钮将上下移动。但是,这需要在您从此视图控制器显示segue的任何视图控制器中删除该按钮。因此,我在按钮上添加了1的标记,并将其从其他视图控制器的superview中删除。这是解决它的最简单方法,我发现它是最简单的方法。

设置右键:

func setupNavBar() {

    self.title = "Home"
    self.navigationController?.navigationBar.prefersLargeTitles = true
    self.navigationController?.navigationBar.isTranslucent = false

    let searchController = UISearchController(searchResultsController: nil)
    self.navigationItem.searchController = searchController

    let rightButton = UIButton()
    rightButton.setTitle("Right Button", for: .normal)
    rightButton.setTitleColor(.purple, for: .normal)
    rightButton.addTarget(self, action: #selector(rightButtonTapped(_:)), for: .touchUpInside)
    navigationController?.navigationBar.addSubview(rightButton)
    rightButton.tag = 1
    rightButton.frame = CGRect(x: self.view.frame.width, y: 0, width: 120, height: 20)

    let targetView = self.navigationController?.navigationBar

    let trailingContraint = NSLayoutConstraint(item: rightButton, attribute:
        .trailingMargin, relatedBy: .equal, toItem: targetView,
                         attribute: .trailingMargin, multiplier: 1.0, constant: -16)
    let bottomConstraint = NSLayoutConstraint(item: rightButton, attribute: .bottom, relatedBy: .equal,
                                    toItem: targetView, attribute: .bottom, multiplier: 1.0, constant: -6)
    rightButton.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([trailingContraint, bottomConstraint])

}

要从任何show segued视图控制器中删除它:

func removeRightButton(){
    guard let subviews = self.navigationController?.navigationBar.subviews else{return}
    for view in subviews{
        if view.tag != 0{
            view.removeFromSuperview()
        }
    }
} 

在viewWillAppear函数

中调用这两个函数

答案 1 :(得分:3)

您要做的是设置BarButtonItem的标题位置调整。将以下行添加到viewWillAppear func。使用verticalhorizontal值进行游戏,以获得您喜欢的layout

navigationItem.rightBarButtonItem?.setTitlePositionAdjustment(.init(horizontal: 10, vertical: 20), for: UIBarMetrics.default)

https://developer.apple.com/documentation/uikit/uibarbuttonitem/1617149-settitlepositionadjustment

答案 2 :(得分:1)

  

好的方法是你可以调整导航标题,如果它的大,那么你的酒吧   按钮将自动调整。这是代码。还有iOS邮件   应用程序做同样的事情供你参考。

func adjustsTitle() {
 guard let font = UIFont(name: "Helvetica-Medium", size: 16) else { return }
 let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
 label.textColor = UIColor.black
 label.textAlignment = .center
 label.text = navigationItem.title
 label.adjustsFontSizeToFitWidth = true
 navigationItem.titleView = label
}

更新了答案 如果要调整标题下方的按钮(如果它增长),那么在这种情况下,您需要在导航栏上加载自定义视图。

//Hide back button. Since you are going to have custom button
navigationItem.hidesBackButton = true
//Increase the height based on your view intrinsic content size
navigationController?.navigationBar.frame.size.height = 100
guard let yourCustomView = UINib(nibName: "yourCustomXib", bundle: nil).instantiate(withOwner: nil, options: nil).first as? YourCustomView else {
 fatalError("Missing yourCustomXib")
}
navigationController?.navigationBar.addSubview(yourCustomView)

答案 3 :(得分:0)

我进行了一些挖掘,最终想到了与Messages应用程序中相同的行为(这意味着该按钮位于navigationBar下方,而不是上方)。 唯一缺少的部分是当UIBarButtonItem出现时发生的动画/模糊效果。

警告:我当前的解决方案是使用私有类(名为_UINavigationBarLargeTitleView),因此,Apple可能会拒绝您的应用……

// Make sure you have a `navigationBar`
guard let navigationBar = navigationController?.navigationBar else {
    return
}
// Make sure you get the correct class from the string, the class itself is not exposed…
guard let UINavigationBarLargeTitleView = NSClassFromString("_UINavigationBarLargeTitleView") else {
    return
}
// Then, you need to find the subview of type `_UINavigationBarLargeTitleView` :
navigationBar.subviews.forEach { subview in
    if subview.isKind(of: UINavigationBarLargeTitleView.self) {
        // If you have it, add whatever button you want (some example below)
        subview.addSubview(largeTitleViewRightBarButton)

        // Constrain it as you want
        NSLayoutConstraint.activate([
            largeTitleViewRightBarButton.bottomAnchor.constraint(equalTo: subview.bottomAnchor, constant: -10),
            largeTitleViewRightBarButton.trailingAnchor.constraint(
                equalTo: subview.trailingAnchor,
                constant: -view.directionalLayoutMargins.trailing
            )
        ])
    }
}

// Finally, the magic happens with one scrollView delegate method :
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView.contentOffset.y >= -103 { // Moving up
        navigationItem.rightBarButtonItem = rightBarButtonItem
    } else { // Moving down
        navigationItem.rightBarButtonItem = nil
    }
}

这是我制作按钮的方式:

private(set) lazy var image: UIImage? = {
    let config = UIImage.SymbolConfiguration(pointSize: 28, weight: .semibold, scale: .default)
    let image = UIImage(systemName: "magnifyingglass.circle.fill", withConfiguration: config)

    return image
}()

private(set) lazy var largeTitleViewRightBarButton: UIButton = {
    let button = UIButton(type: .custom)

    button.translatesAutoresizingMaskIntoConstraints = false
    button.imageView?.tintColor = R.color.appDodgerBlue()
    button.setImage(image, for: .normal)
    button.addTarget(presenter, action: #selector(presenter.onSearchRequested), for: .touchUpInside)

    return button
}()

private(set) lazy var rightBarButtonItem: UIBarButtonItem = {
    let barButtonItem = UIBarButtonItem(
        image: image,
        style: .plain,
        target: presenter,
        action: #selector(presenter.onSearchRequested)
    )
    return barButtonItem
}()

Demo

答案 4 :(得分:0)

如果有人仍在寻找如何在SwiftUI中执行此操作。我做了一个package named NavigationBarLargeTitleItems来解决这个问题。它模仿您在AppStore和Messages-app中看到的行为。

在此示例中,您将看到一个配置文件图标,但您也可以使用诸如“创建配方”按钮之类的文本。如果您希望在滚动时将其显示为标准导航栏项目,则需要使用GeometryReader。如果您想举个例子,请告诉我。

请注意,要实现此行为,我们需要添加到“ _UINavigationBarLargeTitleView”(这是一个私有类),因此在提交到App Store时可能会拒绝您的应用。

对于那些不喜欢链接或只想复制/粘贴的人,我还将在此处包括完整的相关源代码。

扩展名:

@app.route('/', methods=['POST', 'GET'])
def calc():
    if request.method == 'POST':
        if request.form['btn'] == 'calculate':
            t = int(request.form.get('t'))
            v = int(request.form.get('v'))
            calc = t*v
        else:
            calc = ''
        return render_template('wind.html', title='Chill Calc', calc=calc)
    return render_template('wind.html', title='Chill Calc')

用法:

// Copyright © 2020 Mark van Wijnen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the “Software”), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import SwiftUI

public extension View {
    func navigationBarLargeTitleItems<L>(trailing: L) -> some View where L : View {
        overlay(NavigationBarLargeTitleItems(trailing: trailing).frame(width: 0, height: 0))
    }
}

fileprivate struct NavigationBarLargeTitleItems<L : View>: UIViewControllerRepresentable {
    typealias UIViewControllerType = Wrapper
    
    private let trailingItems: L
    
    init(trailing: L) {
        self.trailingItems = trailing
    }
    
    func makeUIViewController(context: Context) -> Wrapper {
        Wrapper(representable: self)
    }
    
    func updateUIViewController(_ uiViewController: Wrapper, context: Context) {
    }
    
    class Wrapper: UIViewController {
        private let representable: NavigationBarLargeTitleItems?
        
        init(representable: NavigationBarLargeTitleItems) {
            self.representable = representable
            super.init(nibName: nil, bundle: nil)
        }
        
        required init?(coder: NSCoder) {
            self.representable = nil
            super.init(coder: coder)
        }
                
        override func viewWillAppear(_ animated: Bool) {
            guard let representable = self.representable else { return }
            guard let navigationBar = self.navigationController?.navigationBar else { return }
            guard let UINavigationBarLargeTitleView = NSClassFromString("_UINavigationBarLargeTitleView") else { return }
           
            navigationBar.subviews.forEach { subview in
                if subview.isKind(of: UINavigationBarLargeTitleView.self) {
                    let controller = UIHostingController(rootView: representable.trailingItems)
                    controller.view.translatesAutoresizingMaskIntoConstraints = false
                    subview.addSubview(controller.view)
                    
                    NSLayoutConstraint.activate([
                        controller.view.bottomAnchor.constraint(
                            equalTo: subview.bottomAnchor,
                            constant: -15
                        ),
                        controller.view.trailingAnchor.constraint(
                            equalTo: subview.trailingAnchor,
                            constant: -view.directionalLayoutMargins.trailing
                        )
                    ])
                }
            }
        }
    }
}

预览

enter image description here