ios 如何使用渐变色设置 UINavigationbar?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30884170/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 06:28:13  来源:igfitidea点击:

How can I set the UINavigationbar with gradient color?

iosuinavigationbarcore-graphics

提问by Ghanshyam Tomar

I want to set the UINavigationbarbackgroundColorto a gradient color where I would like to set it via an array of colors to create a Gradient, ideally, as accessible methods inside UINavigationBarto change its color to this gradient.

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

Any suggestions? (Aside from setting an image manually as the background image of the navigation bar)

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

采纳答案by Ashish Kakkad

Create gradient layer and add it as background of navigation bar.

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

    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];

For creating image from layer.

用于从图层创建图像。

- (UIImage *)imageFromLayer:(CALayer *)layer
{
    UIGraphicsBeginImageContext([layer frame].size);

    [layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return outputImage;
}

One more thing, there is one library available in github : CRGradientNavigationBaryou can also use this library.

还有一件事,github 中有一个可用的库:CRGradientNavigationBar你也可以使用这个库。

回答by Vasily Bodnarchuk

Details

细节

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

Solution

解决方案

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 { 
navigationBar.setGradientBackground(colors: [.lightGray, .red], startPoint: .topLeft, endPoint: .bottomRight)
.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: {
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)
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) } }

Usage

用法

func image(fromLayer layer: CALayer) -> UIImage {
    UIGraphicsBeginImageContext(layer.frame.size)

    layer.render(in: UIGraphicsGetCurrentContext()!)

    let outputImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return outputImage!
}

回答by pableiros

In Swift 3, Swift 4and Swift 5:

Swift 3Swift 4Swift 5 中

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)

For creating image from layer:

从图层创建图像:

func image(fromLayer layer: CALayer) -> UIImage {    
    UIGraphicsBeginImageContext(layer.frame.size)

    layer.renderInContext(UIGraphicsGetCurrentContext()!)

    let outputImage = UIGraphicsGetImageFromCurrentImageContext()

     UIGraphicsEndImageContext()

    return outputImage!
}

In Swift 2:

Swift 2 中

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 { 
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!
    }
}
.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() } }

For creating image from layer:

从图层创建图像:

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

回答by Can

This is the solution without using an intermediate CAGradientLayer, and just using CoreGraphics, in Swift 3.0.

这是在 Swift 3.0 中不使用中间件CAGradientLayer而只使用CoreGraphics,的解决方案。

Essentially, the method creates a UIImageon the fly with the gradient colors passed and sets it.

本质上,该方法UIImage使用传递的渐变颜色动态创建并设置它。

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!
}

The deferstatement makes this so much clean than prior versions. Be noted that CGGradientare available since iOS 8.0.

defer声明使这比以前的版本更干净。请注意,CGGradient自 iOS 8.0 起可用。

Also, this creates the gradient from left to right, tweaking the parameters of drawLinearGradient(startand end) moves the locations. This is up for your implementation.

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

回答by Hardik Thakkar

For Swift 4.2

对于 Swift 4.2

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)
    }

}

How to use

如何使用

    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)

回答by Niklas

In Swift 3

在斯威夫特 3

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
    }
}

回答by Jimmy_m

Swift 5

斯威夫特 5

  • Includes status bar in gradient.
  • Doesn't use an image.
  • Uses transparency so content is visible through nav bar.
  • 包括渐变状态栏。
  • 不使用图像。
  • 使用透明度,因此通过导航栏可以看到内容。
guard
    let navigationController = navigationController,
    let flareGradientImage = CAGradientLayer.primaryGradient(on: navigationController.navigationBar)
    else {
        print("Error creating gradient color!")
        return
    }

navigationController.navigationBar.barTintColor = UIColor(patternImage: flareGradientImage)

回答by Azharhussain Shaikh

SWIFT 5

快速 5

To make the horizontal gradient background of the navigation bar along with status bar too.. just put this code in your viewcontroller's viewDidLoad()method.

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

- (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;
}

Gradient will look like this

渐变看起来像这样

output Gradient will look like this.

输出渐变看起来像这样。

CHEERS!

干杯!

回答by Kévin Renella

This amazing tutorial from Lawrence Tan ? shows how to set a gradient using barTintColorand no backgroundImage: https://medium.com/swift2go/add-gradient-to-navigation-bar-in-swift-9284fe91fea2

这个来自 Lawrence Tan 的精彩教程?显示如何使用barTintColor和 no设置渐变backgroundImagehttps: //medium.com/swift2go/add-gradient-to-navigation-bar-in-swift-9284fe91fea2

Summary

概括

CAGradientLayerextension

CAGradientLayer延期

##代码##

Apply the gradient

应用渐变

##代码##

回答by Denis Kutlubaev

Objective C solution that works on iPhone X as well:

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

##代码##