如何使用渐变颜色设置 UINavigationbar?
Posted
技术标签:
【中文标题】如何使用渐变颜色设置 UINavigationbar?【英文标题】:How can I set the UINavigationbar with gradient color? 【发布时间】:2015-06-17 06:48:19 【问题描述】:我想将UINavigationbar
backgroundColor
设置为渐变颜色,我想通过颜色数组设置它以创建渐变,理想情况下,作为UINavigationBar
中的可访问方法将其颜色更改为此渐变。
有什么建议吗? (除了手动设置一张图片作为导航栏的背景图片)
【问题讨论】:
【参考方案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
。但也许使用传递给setGradientView
的startPoint
和endPoint
会更好【参考方案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 3、Swift 4 和 Swift 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 起可用。
此外,这会创建从左到右的渐变,调整drawLinearGradient
(start
和end
)的参数会移动位置。这取决于您的实施。
【讨论】:
优秀的扩展 出于某种原因,这对我来说只有大约一半的时间。离开并重新进入同一个视图有时它会起作用,有时导航栏只是一个平面颜色。 @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)
【讨论】:
以上是关于如何使用渐变颜色设置 UINavigationbar?的主要内容,如果未能解决你的问题,请参考以下文章
如何在IOS 11中为新的大型条设置渐变颜色的UINavigationbar?