如何在没有导航控制器的情况下获取导航栏后退按钮

Posted

技术标签:

【中文标题】如何在没有导航控制器的情况下获取导航栏后退按钮【英文标题】:How to get navigation bar back button without navigation controller 【发布时间】:2018-01-07 19:55:26 【问题描述】:

我正在制作一个应用程序,并且有一个实例我不想使用导航控制器,而我只想使用导航栏。我想要一个后退按钮,但我无法得到它,所以有宽 V 形。

看起来像这样:

而不是这个:

没有导航控制器如何实现第二种效果?

【问题讨论】:

为什么不能使用 NavigationController?这感觉很hacky。如果您点击“返回”,会发生什么?应该有pop动画 见***.com/questions/26518716/… 是的,正如@rmaddy 所说,或者您为它创建自己的图像。当然,如果没有 UINavigationController,你也需要自己提供所有的后台功能。 如果你也提供一些代码会很好。 为什么要给代码? 【参考方案1】:

一种选择是将您自己的人字形渲染为UIImage 并将其设置为条形按钮的图像..

//
//  ViewController.swift
//  ***
//
//  Created by Brandon on 2018-01-07.
//  Copyright © 2018 XIO. All rights reserved.
//

import UIKit

class ViewController: UIViewController 

    override func viewDidLoad() 
        super.viewDidLoad()


        //Creating a custom navigation bar..
        let navigationBar = UINavigationBar()
        self.view.addSubview(navigationBar)
        NSLayoutConstraint.activate([
            navigationBar.leftAnchor.constraint(equalTo: self.view.leftAnchor),
            navigationBar.rightAnchor.constraint(equalTo: self.view.rightAnchor),
            navigationBar.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor)
        ])
        navigationBar.translatesAutoresizingMaskIntoConstraints = false

        //Creating a navigation bar item with title..
        let item = UINavigationItem(title: "Custom Navigation")

        //Creating the chevron (back-arrow)
        //let img = makeBackChevron(size: CGSize(width: 20.0, height: 20.0), colour: nil)! //UIColor.red

        //Creating the chevron (back-arrow) to look like Apple's..
        let img = makeBackChevron(thickness: 3.0, size: CGSize(width: 22.0, height: 44.0), colour: nil)! //UIColor.red

        //Creating the bar button.. Note: Add your own target and action..
        let barButton = UIBarButtonItem(image: img, style: .done, target: nil, action: nil)

        //Set the left bar button item to be the one we created
        //Then set the items to be part of the navigation bar we created..
        item.leftBarButtonItems = [barButton]
        navigationBar.setItems([item], animated: true)
    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    

    //Creates a chevron (back-arrow) image with size and colour..
    func makeBackChevron(size: CGSize, colour: UIColor? = nil) -> UIImage? 
        //Create a rendering context..
        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
        let ctx = UIGraphicsGetCurrentContext()

        //Create a chevron path with normalized 2D coordinates..
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 1.0, y: 0.0)) //top right..
        path.addLine(to: CGPoint(x: 0.75, y: 0.0)) //top left..
        path.addLine(to: CGPoint(x: 0.0, y: 0.5))  //left center of pointy arrow head..
        path.addLine(to: CGPoint(x: 0.75, y: 1.0)) //bottom left..
        path.addLine(to: CGPoint(x: 1.0, y: 1.0)) //bottom right..
        path.addLine(to: CGPoint(x: 0.25, y: 0.5)) //right center of pointy arrow head..
        path.close()

        //Scale the path to be the size specified..
        path.apply(CGAffineTransform(scaleX: size.width, y: size.height))

        //Set rendering colour..
        if let colour = colour 
            ctx?.setFillColor(colour.cgColor)
        

        //Draw the path to the image context..
        ctx?.addPath(path.cgPath)
        ctx?.fillPath()

        //Create the image from the context..
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        //If a colour was set, then always render the image with that colour.. else allow navigation bar or any view to `tint` the image..
        return colour != nil ? img?.withRenderingMode(.alwaysOriginal) : img
    

    //Closer to the Apple chevron.. Allows you to specify arrow-thickness..
    func makeBackChevron(thickness: CGFloat, size: CGSize, colour: UIColor? = nil) -> UIImage? 
        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
        let ctx = UIGraphicsGetCurrentContext()

        //Create a thin-line chevron with some left-padding..
        let padding: CGFloat = 0.20
        let path = UIBezierPath()
        path.move(to: CGPoint(x: padding + 0.5, y: 0.773))
        path.addLine(to: CGPoint(x: padding + 0.0, y: 0.5))
        path.addLine(to: CGPoint(x: padding + 0.5, y: 0.227))
        path.apply(CGAffineTransform(scaleX: size.width, y: size.height))

        //Use a stroke instead of a fill like previous algorithm..
        ctx?.setStrokeColor(colour?.cgColor ?? UIColor.white.cgColor)
        ctx?.addPath(path.cgPath)
        ctx?.setLineWidth(thickness) //Set arrow-thickness..
        ctx?.setLineJoin(.round) //Set line-join to round corners..
        ctx?.strokePath()

        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return colour != nil ? img?.withRenderingMode(.alwaysOriginal) : img
    

注意:我将文本的绘制留给读者作为练习;)

结果:

【讨论】:

编辑:添加了一种用于创建人字形的新算法,因此它看起来更接近 OP 帖子中的算法..(与 Apple 的非常相似)..如果您想更改人字形方向,您可以应用另一个旋转变换或否定比例。它不是“完美的”,也不像苹果的“完全”匹配,但它已经足够接近了!我不能花更多的时间在数字和计算上.. 对于那些正在寻找带标题的后退按钮的人(基于此解决方案):imgur.com/a/FPUKrVUlet img = makeBackChevron(thickness: 3.0, size: CGSize(width: 22.0, height: 44.0), colour: nil)! let backButton = UIButton(type: .custom) backButton.setImage(img, for: .normal) backButton.setTitle("Back", for: .normal) backButton.sizeToFit() backButton.addTarget(self, action: #selector(self.backButtonClick), for: .touchUpInside) self.navigationItem.leftBarButtonItems = [UIBarButtonItem(customView: backButton)]

以上是关于如何在没有导航控制器的情况下获取导航栏后退按钮的主要内容,如果未能解决你的问题,请参考以下文章

导航栏上的后退按钮仅显示没有单词的指示器

如何基于之前的 ViewController 创建导航栏后退按钮

如何更改导航栏后退按钮的字体和颜色

如果同时触发滑动手势和后退按钮,导航栏会变得很时髦

如何完全覆盖导航栏的后退按钮操作?

UIPageViewController 更改导航栏后退按钮标题