Using UIColor with CAShapeLayer

2019-10-05 17:24发布

I'm passing an RGB color to shapeLayer.fillColor and shapeLayer.strokeColor, and my code is crashing. Here's what I tried:

let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = (UIColor(red: 57, green: 65, blue: 101, alpha: 1) as! CGColor)
shapeLayer.strokeColor = (UIColor(red: 57, green: 65, blue: 101, alpha: 1) as! CGColor)

If I write my RGB color as:

UIColor(red: 57, green: 65, blue: 101, alpha: 1)

Xcode gives me a warning to change it to:

(UIColor(red: 57, green: 65, blue: 101, alpha: 1) as! CGColor)

error

When I run Xcode's suggested fix, my app crashes. Why?

3条回答
混吃等死
2楼-- · 2019-10-05 17:50

Fixing Your Code

You want something like this:

let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = UIColor(red: 57, green: 65, blue: 101, alpha: 1).cgColor
shapeLayer.strokeColor = UIColor(red: 57, green: 65, blue: 101, alpha: 1).cgColor

How it Works

A Warning About Inheritance

Xcode warning you about something for that assignment is correct, because you're assigning a UIColor to a CGColor property, but the "fix-me" suggestion is wrong. (Xcode might be doing this because it sees that the class names are similar, but not identical. It might be worth filing a radar.)

One place you'll see this a lot is typecasting a custom UITableViewCell subclass to the basic cell class, but that won't work here. CGColor does not inherit from UIColor, nor does UIColor inherit from CGColor. CGColor is a CFTypeRef and UIColor inherits from `NSObject.

One of These Is Not Like the Other

Now, why are these colors different?

CG stands for "Core Graphics" and is at a lower level than UIKit which defines the UIColor class. One color object is part of UIKit and the part of Core Graphics.

The documentation says that UIColor is:

An object that stores color data and sometimes opacity (alpha value)."

A little further down, the overview says that UIColor represents itself internally as a CGColor within the predetermined color space.

CGColor, on the other hand, is:

A set of components that define a color, with a color space specifying how to interpret them."

Conceptually, very similar, but not exactly the same thing. UIColor is defined in the sRGBA space (as noted here) but CGColor will contain info about what color space it's in.

Now, the animation layer you're using is part of Core Animation, which is yet another Apple framework. Core Animation isn't part of UIKit, and it's not "on top of" UIKit either, so its classes don't use UIColor. Core Animation uses CGColor for its representations of colors.

UIColor is so easy to instantiate, though, and we're in UIKit-land much of the time anyway. How can we customize CALayer and friends while working in our UIViews and UIViewControllers? Luckily, UIColor exposes the CGColor version of itself, with the .cgColor property.

A Note About Color Spaces

Some of the other answers note that you need to pass values between 0.0 and 1.0. This is true, but it's relevant to note that as of iOS 10, Apple has added some wider-color capabilities to iOS.

This means that it's actually possible to pass in values less than 0.0 and greater than 1.0. From the UIColor documentation:

When working in an extended color space, color values are not clamped to fit inside the color gamut, meaning that component values may be less than 0.0 or greater than 1.0. When displayed on an sRGB display, such colors are outside the gamut and will not be rendered accurately.

You probably won't need to worry about color spaces right now, but it's important to understand the fundamentals if you want to do things beyond showing a color onscreen. (Generating inverted or complementary colors are some examples.)

Session 712 from WWDC 2016 (link) covers this new API in depth, and I'd recommend that you take a look at it once you're more comfortable with the basics.

Finally, there's an Apple Tech Note (TN2313) that discusses this in more depth than I could ever imagine. If you want to learn a lot about colors and color spaces on iOS and macOS, read the tech note, entitled "Best Practices for Color Management in OS X and iOS."

查看更多
爷的心禁止访问
3楼-- · 2019-10-05 17:54

You are type casting UIColor as CGColor that is wrong. And RGB values must be the range from 0.0 to 1.0, Check the below code.

let circlePath = UIBezierPath(arcCenter: CGPoint(x: bounds.origin.x+60,y: bounds.origin.y+107), radius: CGFloat(6), startAngle: CGFloat(0), endAngle:CGFloat(7), clockwise: true)

let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
circlePath.lineCapStyle = .round

let color = UIColor(red: 57.0/255.0, green: 65.0/255.0, blue: 101.0/255.0, alpha: 1).cgColor

shapeLayer.fillColor = color
shapeLayer.strokeColor = color
shapeLayer.lineWidth = 3.0
self.layer.addSublayer(shapeLayer)
查看更多
forever°为你锁心
4楼-- · 2019-10-05 18:11

First of all the parameters red,green, blue and alpha must be in range from 0.0 to 1.0

Secondly, you must pass the cgColor to CGLayer, e.g.

let color = UIColor(red: 57.0/255.0, green: 65.0/255.0, blue: 101.0/255.0, alpha: 1)
shapeLayer.fillColor = color.cgColor
shapeLayer.strokeColor = color.cgColor
查看更多
登录 后发表回答