【问题标题】:How can I set the UINavigationbar with gradient color?如何使用渐变颜色设置 UINavigationbar?
【发布时间】:2015-06-17 06:48:19
【问题描述】:

我想将UINavigationbar backgroundColor 设置为渐变颜色,我想通过颜色数组设置它以创建渐变,理想情况下,作为UINavigationBar 中的可访问方法将其颜色更改为此渐变。

有什么建议吗? (除了手动设置一张图片作为导航栏的背景图片)

【问题讨论】:

    标签: ios uinavigationbar core-graphics


    【解决方案1】:

    详情

    • Xcode 11.4 (11E146), swift 5
    • 在 iOS 13.1、12.2、11.0.1 上测试

    解决方案

    class UINavigationBarGradientView: UIView {
    
        enum Point {
            case topRight, topLeft
            case bottomRight, bottomLeft
            case custom(point: CGPoint)
    
            var point: CGPoint {
                switch self {
                    case .topRight: return CGPoint(x: 1, y: 0)
                    case .topLeft: return CGPoint(x: 0, y: 0)
                    case .bottomRight: return CGPoint(x: 1, y: 1)
                    case .bottomLeft: return CGPoint(x: 0, y: 1)
                    case .custom(let point): return point
                }
            }
        }
    
        private weak var gradientLayer: CAGradientLayer!
    
        convenience init(colors: [UIColor], startPoint: Point = .topLeft,
                         endPoint: Point = .bottomLeft, locations: [NSNumber] = [0, 1]) {
            self.init(frame: .zero)
            let gradientLayer = CAGradientLayer()
            gradientLayer.frame = frame
            layer.addSublayer(gradientLayer)
            self.gradientLayer = gradientLayer
            set(colors: colors, startPoint: startPoint, endPoint: endPoint, locations: locations)
            backgroundColor = .clear
        }
    
        func set(colors: [UIColor], startPoint: Point = .topLeft,
                 endPoint: Point = .bottomLeft, locations: [NSNumber] = [0, 1]) {
            gradientLayer.colors = colors.map { $0.cgColor }
            gradientLayer.startPoint = startPoint.point
            gradientLayer.endPoint = endPoint.point
            gradientLayer.locations = locations
        }
    
        func setupConstraints() {
            guard let parentView = superview else { return }
            translatesAutoresizingMaskIntoConstraints = false
            topAnchor.constraint(equalTo: parentView.topAnchor).isActive = true
            leftAnchor.constraint(equalTo: parentView.leftAnchor).isActive = true
            parentView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
            parentView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
            guard let gradientLayer = gradientLayer else { return }
            gradientLayer.frame = frame
            superview?.addSubview(self)
        }
    }
    
    extension UINavigationBar {
        func setGradientBackground(colors: [UIColor],
                                   startPoint: UINavigationBarGradientView.Point = .topLeft,
                                   endPoint: UINavigationBarGradientView.Point = .bottomLeft,
                                   locations: [NSNumber] = [0, 1]) {
            guard let backgroundView = value(forKey: "backgroundView") as? UIView else { return }
            guard let gradientView = backgroundView.subviews.first(where: { $0 is UINavigationBarGradientView }) as? UINavigationBarGradientView else {
                let gradientView = UINavigationBarGradientView(colors: colors, startPoint: startPoint,
                                                               endPoint: endPoint, locations: locations)
                backgroundView.addSubview(gradientView)
                gradientView.setupConstraints()
                return
            }
            gradientView.set(colors: colors, startPoint: startPoint, endPoint: endPoint, locations: locations)
        }
    }
    

    用法

    navigationBar.setGradientBackground(colors: [.lightGray, .red], startPoint: .topLeft, endPoint: .bottomRight)
    

    【讨论】:

    • 您可以将您的UINavigationBar extension 改进为updatedFrame.size.height += self.frame.origin.y 以匹配新的iPhone X。
    • 已更新。谢谢!
    • .setBackgroundImage(, for: ) 在 iOS SDK 11 上无法正常工作,尤其是在导航栏中嵌入搜索栏的情况下。您可以改用 barTintColor 属性。基本上使用UIColor(patternImage:) 设置它,其中图案图像是您的渐变。
    • 这让我无法点击默认的后退按钮。要使后退按钮再次可点击,请在 guard 语句中添加 gradientView.isUserInteractionEnabled = false
    • 现在,在guard let gradientView 内部,我们已经硬编码了startPoint: .topLeft, endPoint: .bottomLeft。但也许使用传递给setGradientViewstartPointendPoint会更好
    【解决方案2】:

    创建渐变图层并将其添加为导航栏的背景。

        CAGradientLayer *gradient = [CAGradientLayer layer];
        gradient.frame = self.navigationController.navigationBar.bounds;
        gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor whiteColor] CGColor], (id)[[UIColor blackColor] CGColor], nil];
        [self.navigationController.navigationBar setBackgroundImage:[self imageFromLayer:gradient] forBarMetrics:UIBarMetricsDefault];
    

    用于从图层创建图像。

    - (UIImage *)imageFromLayer:(CALayer *)layer
    {
        UIGraphicsBeginImageContext([layer frame].size);
    
        [layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
    
        UIGraphicsEndImageContext();
    
        return outputImage;
    }
    

    另外,github 中有一个库:CRGradientNavigationBar 你也可以使用这个库。

    【讨论】:

      【解决方案3】:

      Swift 3Swift 4Swift 5 中:

      let gradient = CAGradientLayer()
      let sizeLength = UIScreen.main.bounds.size.height * 2
      let defaultNavigationBarFrame = CGRect(x: 0, y: 0, width: sizeLength, height: 64)
      
      gradient.frame = defaultNavigationBarFrame
      
      gradient.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
      
      UINavigationBar.appearance().setBackgroundImage(self.image(fromLayer: gradient), for: .default)
      

      从图层创建图像:

      func image(fromLayer layer: CALayer) -> UIImage {
          UIGraphicsBeginImageContext(layer.frame.size)
      
          layer.render(in: UIGraphicsGetCurrentContext()!)
      
          let outputImage = UIGraphicsGetImageFromCurrentImageContext()
      
          UIGraphicsEndImageContext()
      
          return outputImage!
      }
      

      Swift 2 中:

      let gradient = CAGradientLayer()
      let sizeLength = UIScreen.mainScreen().bounds.size.height * 2
      let defaultNavigationBarFrame = CGRectMake(0, 0, sizeLength, 64)
      
      gradient.frame = defaultNavigationBarFrame
      
      gradient.colors = [UIColor.whiteColor().CGColor, UIColor.blackColor().CGColor]
      
      UINavigationBar.appearance().setBackgroundImage(self.image(fromLayer: gradient), forBarMetrics: .Default)
      

      从图层创建图像:

      func image(fromLayer layer: CALayer) -> UIImage {    
          UIGraphicsBeginImageContext(layer.frame.size)
      
          layer.renderInContext(UIGraphicsGetCurrentContext()!)
      
          let outputImage = UIGraphicsGetImageFromCurrentImageContext()
      
           UIGraphicsEndImageContext()
      
          return outputImage!
      }
      

      【讨论】:

      • 是的。这个解决方案适用于我。由于我已经配置了一个UINavigationBarAppearance() 实例,并在其上设置了值,为了实现这个解决方案,我只需将我的navBarAppearance 实例的backgroundImage 属性设置为从他的image(layer: ) 方法返回的UIImage
      【解决方案4】:

      这是在 Swift 3.0 中不使用中间 CAGradientLayer 并仅使用 CoreGraphics 的解决方案。

      本质上,该方法会动态创建一个UIImage,并传递渐变颜色并对其进行设置。

      extension UINavigationBar
      {
          /// Applies a background gradient with the given colors
          func apply(gradient colors : [UIColor]) {
              var frameAndStatusBar: CGRect = self.bounds
              frameAndStatusBar.size.height += 20 // add 20 to account for the status bar
      
              setBackgroundImage(UINavigationBar.gradient(size: frameAndStatusBar.size, colors: colors), for: .default)
          }
      
          /// Creates a gradient image with the given settings
          static func gradient(size : CGSize, colors : [UIColor]) -> UIImage?
          {
              // Turn the colors into CGColors
              let cgcolors = colors.map { $0.cgColor }
      
              // Begin the graphics context
              UIGraphicsBeginImageContextWithOptions(size, true, 0.0)
      
              // If no context was retrieved, then it failed
              guard let context = UIGraphicsGetCurrentContext() else { return nil }
      
              // From now on, the context gets ended if any return happens
              defer { UIGraphicsEndImageContext() }
      
              // Create the Coregraphics gradient
              var locations : [CGFloat] = [0.0, 1.0]
              guard let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: cgcolors as NSArray as CFArray, locations: &locations) else { return nil }
      
              // Draw the gradient
              context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: [])
      
              // Generate the image (the defer takes care of closing the context)
              return UIGraphicsGetImageFromCurrentImageContext()
          }
      }
      

      defer 语句比以前的版本更干净。 请注意,CGGradient 自 iOS 8.0 起可用。

      此外,这会创建从左到右的渐变,调整drawLinearGradientstartend)的参数会移动位置。这取决于您的实施。

      【讨论】:

      • 优秀的扩展
      • 出于某种原因,这对我来说只有大约一半的时间。离开并重新进入同一个视图有时它会起作用,有时导航栏只是一个平面颜色。
      • @Maor 很可能,当 UINavigationBar 的框架为零(尚未布局)时,您尝试设置图像,这是此实现的一个弱点。
      【解决方案5】:

      对于 Swift 4.2

      extension UINavigationBar {
          func setGradientBackground(colors: [Any]) {
              let gradient: CAGradientLayer = CAGradientLayer()
              gradient.locations = [0.0 , 0.5, 1.0]
              gradient.startPoint = CGPoint(x: 0.0, y: 1.0)
              gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
      
              var updatedFrame = self.bounds
              updatedFrame.size.height += self.frame.origin.y
              gradient.frame = updatedFrame
              gradient.colors = colors;
              self.setBackgroundImage(self.image(fromLayer: gradient), for: .default)
          }
      
          func image(fromLayer layer: CALayer) -> UIImage {
              UIGraphicsBeginImageContext(layer.frame.size)
              layer.render(in: UIGraphicsGetCurrentContext()!)
              let outputImage = UIGraphicsGetImageFromCurrentImageContext()
              UIGraphicsEndImageContext()
              return outputImage!
          }
      }
      

      如何使用

         self.navigationController?.navigationBar.setGradientBackground(colors: [
                  UIColor.red.cgColor,
                  UIColor.green.cgColor,
                  UIColor.blue.cgColor
                  ])
      

      【讨论】:

      • 这很好,但是有没有办法使用 UIAppearance 在全局范围内应用它?
      • 如果在您的情况下,您在整个应用程序中都有通用的 GradientBackground 导航栏?
      • 所以这种情况下你可以直接在导航栏设置渐变图片
      【解决方案6】:

      斯威夫特 5

      • 在渐变中包含状态栏。
      • 不使用图片。
      • 使用透明度,因此内容通过导航栏可见。
      extension UINavigationBar {
      
          func addGradient(_ toAlpha: CGFloat, _ color: UIColor) {
              let gradient = CAGradientLayer()
              gradient.colors = [
                  color.withAlphaComponent(toAlpha).cgColor,
                  color.withAlphaComponent(toAlpha).cgColor,
                  color.withAlphaComponent(0).cgColor
              ]
              gradient.locations = [0, 0.8, 1]
              var frame = bounds
              frame.size.height += UIApplication.shared.statusBarFrame.size.height
              frame.origin.y -= UIApplication.shared.statusBarFrame.size.height
              gradient.frame = frame
              layer.insertSublayer(gradient, at: 1)
          }
      
      }
      

      【讨论】:

      • 谢谢,它可以工作,但我有一个问题,即后退按钮在按下后位于图层后面。有没有人遇到过类似的事情?
      • 这个例子不包括大标题、方向变化...
      【解决方案7】:

      SWIFT 5

      要使导航栏的水平渐变背景也与状态栏一起...只需将此代码放入您的视图控制器的viewDidLoad() 方法中。

          self.navigationItem.title = "Gradiant Back Ground"
          let gradientLayer = CAGradientLayer()
          var updatedFrame = self.navigationController!.navigationBar.bounds
          updatedFrame.size.height += UIApplication.shared.statusBarFrame.size.height
          gradientLayer.frame = updatedFrame
          gradientLayer.colors = [UIColor.green.cgColor, UIColor.blue.cgColor] // start color and end color
          gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0) // Horizontal gradient start
          gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0) // Horizontal gradient end
          UIGraphicsBeginImageContext(gradientLayer.bounds.size)
          gradientLayer.render(in: UIGraphicsGetCurrentContext()!)
          let image = UIGraphicsGetImageFromCurrentImageContext()
          UIGraphicsEndImageContext()
          self.navigationController!.navigationBar.setBackgroundImage(image, for: UIBarMetrics.default)
      

      输出渐变将如下所示。

      【讨论】:

      • 嘿,这不适用于具有大标题的导航栏。你修好了吗?
      【解决方案8】:

      在 Swift 3 中

      let gradient = CAGradientLayer()
      let sizeLength = UIScreen.main.bounds.size.height * 2
      let defaultNavigationBarFrame = CGRect(x: 0, y: 0, width: sizeLength, height: 64)
      gradient.frame = defaultNavigationBarFrame
      gradient.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
      UINavigationBar.appearance().setBackgroundImage(self.image(fromLayer: gradient), for: .default)
      
      func image(fromLayer layer: CALayer) -> UIImage {
          UIGraphicsBeginImageContext(layer.frame.size)
          layer.render(in: UIGraphicsGetCurrentContext()!)
          let outputImage = UIGraphicsGetImageFromCurrentImageContext()
          UIGraphicsEndImageContext()
          return outputImage!
      }
      

      【讨论】:

        【解决方案9】:

        同样适用于 iPhone X 的 Objective C 解决方案:

        - (void)addGradientToNavigationBar
        {
            CAGradientLayer *gradient = [CAGradientLayer layer];
            CGRect gradientFrame = self.navigationController.navigationBar.bounds;
            gradientFrame.size.height += [UIApplication sharedApplication].statusBarFrame.size.height;
            gradient.frame = gradientFrame;
            gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorGradientUp] CGColor], (id)[[UIColor colorGradientDown] CGColor], nil];
            [self.navigationController.navigationBar setBackgroundImage:[self imageFromLayer:gradient] forBarMetrics:UIBarMetricsDefault];
        }
        
        - (UIImage *)imageFromLayer:(CALayer *)layer
        {
            UIGraphicsBeginImageContext([layer frame].size);
        
            [layer renderInContext:UIGraphicsGetCurrentContext()];
            UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
        
            UIGraphicsEndImageContext();
        
            return outputImage;
        }
        

        【讨论】:

        • 感谢为我工作的人,如果有人想使用清晰的颜色,您可以使用:self.navigationController.navigationBar.shadowImage = [UIImage new]; self.navigationController.navigationBar.translucent = YES;
        【解决方案10】:

        这个来自 Lawrence Tan 的精彩教程  展示了如何使用 barTintColor 而不是 backgroundImage 设置渐变:https://medium.com/swift2go/add-gradient-to-navigation-bar-in-swift-9284fe91fea2

        总结

        CAGradientLayer 分机

        extension CAGradientLayer {
        
            class func primaryGradient(on view: UIView) -> UIImage? {
                let gradient = CAGradientLayer()
                let flareRed = UIColor(displayP3Red: 241.0/255.0, green: 39.0/255.0, blue: 17.0/255.0, alpha: 1.0)
                let flareOrange = UIColor(displayP3Red: 245.0/255.0, green: 175.0/255.0, blue: 25.0/255.0, alpha: 1.0)
                var bounds = view.bounds
                bounds.size.height += UIApplication.shared.statusBarFrame.size.height
                gradient.frame = bounds
                gradient.colors = [flareRed.cgColor, flareOrange.cgColor]
                gradient.startPoint = CGPoint(x: 0, y: 0)
                gradient.endPoint = CGPoint(x: 1, y: 0)
                return gradient.createGradientImage(on: view)
            }
        
            private func createGradientImage(on view: UIView) -> UIImage? {
                var gradientImage: UIImage?
                UIGraphicsBeginImageContext(view.frame.size)
                if let context = UIGraphicsGetCurrentContext() {
                    render(in: context)
                    gradientImage = UIGraphicsGetImageFromCurrentImageContext()?.resizableImage(withCapInsets: UIEdgeInsets.zero, resizingMode: .stretch)
                }
                UIGraphicsEndImageContext()
                return gradientImage
            }
        }
        

        应用渐变

        guard
            let navigationController = navigationController,
            let flareGradientImage = CAGradientLayer.primaryGradient(on: navigationController.navigationBar)
            else {
                print("Error creating gradient color!")
                return
            }
        
        navigationController.navigationBar.barTintColor = UIColor(patternImage: flareGradientImage)
        

        【讨论】:

          【解决方案11】:

          这是一个框架,用于不同的 UI 组件渐变包括 UINavigation 栏: enter link description here

          首先: 然后在它嵌入到 UINavigationController 的根 ViewController 中

          import SHNDStuffs
          

          把它放在你的 ViewDidLoad() 中

          class RootViewController: UIViewController {
              override func viewDidLoad() {
                  super.viewDidLoad()
          
                  SHNDNavigationBarGradient(firstColor: .darkGray,
                                            secondColor: .white,
                                            tintColor: .black,
                                            isHorizontal: true)
          }
          

          【讨论】:

            猜你喜欢
            • 2013-02-24
            • 1970-01-01
            • 1970-01-01
            • 2017-05-13
            • 2017-07-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-08-19
            相关资源
            最近更新 更多