Reputation: 1919
I am looking to add a gradient background to an ASDisplayNode/ASButtonNode. I have tried creating a gradient layer and adding it as a sublayer like this -
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = button.frame;
gradient.colors = @[[UIColor redColor], [UIColor blueColor]];
[button.view.layer insertSublayer:gradient atIndex:0];
where button
is of type ASButtonNode
, but this just gives a white background to the button. I haven't been able to find much documentation for achieving this either.
How can I go about adding a background given an array of UIColor
and a CGFloat
angle?
Thanks
Upvotes: 2
Views: 2130
Reputation: 482
extension ASDisplayNode {
func gradient(from color1: UIColor, to color2: UIColor) {
DispatchQueue.main.async {
let size = self.view.frame.size
let width = size.width
let height = size.height
let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = [color1.cgColor, color2.cgColor]
gradient.locations = [0.0 , 1.0]
gradient.startPoint = CGPoint(x: 0.0, y: height/2)
gradient.endPoint = CGPoint(x: 1.0, y: height/2)
gradient.cornerRadius = 30
gradient.frame = CGRect(x: 0.0, y: 0.0, width: width, height: height)
self.view.layer.insertSublayer(gradient, at: 0)
}
}
}
Example:
node.gradient(from: .white, to: .red)
Upvotes: 0
Reputation: 61
Here's my completely Texture/AsyncDisplayKit based implementation for this. This allows for a completely generic Gradient Node class that does not fix any values for the entire class, where other solutions thus far forces, because drawRect is a class function, not an instance function.
class GradientNode: ASDisplayNode {
private let startUnitPoint: CGPoint
private let endUnitPoint: CGPoint
private let colors: [UIColor]
private let locations: [CGFloat]?
override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled isCancelledBlock: () -> Bool, isRasterizing: Bool) {
guard let parameters = parameters as? GradientNode else {
CCLog.assert("Expected type SimpleGradientNode to be returned")
return
}
// Calculate the start and end points
let startUnitX = parameters.startUnitPoint.x
let startUnitY = parameters.startUnitPoint.y
let endUnitX = parameters.endUnitPoint.x
let endUnitY = parameters.endUnitPoint.y
let startPoint = CGPoint(x: bounds.width * startUnitX + bounds.minX, y: bounds.height * startUnitY + bounds.minY)
let endPoint = CGPoint(x: bounds.width * endUnitX + bounds.minX, y: bounds.height * endUnitY + bounds.minY)
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
context.clip(to: bounds)
guard let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: parameters.colors.map { $0.cgColor } as CFArray,
locations: parameters.locations) else {
CCLog.assert("Unable to create CGGradient")
return
}
context.drawLinearGradient(gradient,
start: startPoint,
end: endPoint,
options: CGGradientDrawingOptions.drawsAfterEndLocation)
context.restoreGState()
}
init(startingAt startUnitPoint: CGPoint, endingAt endUnitPoint: CGPoint, with colors: [UIColor], for locations: [CGFloat]? = nil) {
self.startUnitPoint = startUnitPoint
self.endUnitPoint = endUnitPoint
self.colors = colors
self.locations = locations
super.init()
}
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
return self
}
All one needs to do is to create a GradientNote and fill in the parameters in the init() function. And then treat it as a regular Node and let ASDK do the rest of the work!
coverTitleBackgroundNode = GradientNode(startingAt: CGPoint(x: 0.5, y: 1.0),
endingAt: CGPoint(x: 0.5, y: 0.0),
with: [UIColor.black.withAlphaComponent(Constants.CoverTitleBackgroundBlackAlpha), UIColor.clear])
coverTitleBackgroundNode!.isLayerBacked = true
coverTitleBackgroundNode!.isOpaque = false
automaticallyManagesSubnodes = true
Upvotes: 1
Reputation: 185
Swift 3.
extension UIView {
func gradient(color1: UIColor, color2: UIColor) -> CAGradientLayer {
let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = [color1.cgColor, color2.cgColor]
gradient.locations = [0.0 , 1.0]
gradient.startPoint = CGPoint(x: 0.0, y: 0.0)
gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
gradient.frame = CGRect(x: 0.0, y: 0.0, width: frame.size.width, height: frame.size.height)
return gradient
}
}
Example:
let gradient = self.cardGradientNode.view.gradient(
color1: gradient.start,
color2: gradient.end
)
self.cardGradientNode.view.layer.insertSublayer(gradient, at: 0)
Upvotes: 3
Reputation: 652
You've asked the same question on AsyncDisplayKit's GitHub issues: https://github.com/facebookarchive/AsyncDisplayKit/issues/3253
You've been answered with 2 possibilities:
1: Create a CAGradientLayer and add it as sublayer of the node
2: Override the + (void)draw... method of the node and draw the gradient there.
The first option must be in the main thread, so that wouldn't grant us the mighty powers of ASDK. The second option is more complex, but I can post some example from the ASDK example projects.
and
https://www.raywenderlich.com/124311/asyncdisplaykit-2-0-tutorial-getting-started
1. Subclass ASDisplayNode
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface GradientNode : ASDisplayNode
@end
2. Override drawRect method (Basic gradient with 2 colors - black to transparent)
@implementation GradientNode
+(void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:
(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:
(BOOL)isRasterizing{
CGFloat locations[2];
NSMutableArray *colors = [NSMutableArray arrayWithCapacity:2];
[colors addObject:(id)[[UIColor clearColor] CGColor]];
locations[0] = 0.0;
[colors addObject:(id)[[UIColor blackColor] CGColor]];
locations[1] = 1.0;
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,
(CFArrayRef)colors, locations);
CGContextDrawLinearGradient(ctx, gradient, CGPointZero,
CGPointMake(bounds.size.width, bounds.size.height), 0);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
@end
3. Setuping the Node (Important! when using gradient with clear colors, otherwise it draws an opaqued black view)
_gradientNode = [GradientNode new];
_gradientNode.layerBacked = YES;
_gradientNode.opaque = NO;
Upvotes: 0