Как добавить градиент в ASDisplayNode или ASButtonNode

Я ищу, чтобы добавить градиентный фон в ASDisplayNode/ASButtonNode. Я попытался создать слой градиента и добавить его в качестве подслоя, как это -

CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = button.frame;
gradient.colors = @[[UIColor redColor], [UIColor blueColor]];
[button.view.layer insertSublayer:gradient atIndex:0];

где button имеет тип ASButtonNode, но это просто дает белый фон для кнопки. Мне также не удалось найти много документации для достижения этой цели.

Как я могу добавить фон с массивом UIColor и CGFloat угол?

Спасибо

4 ответа

Решение

Свифт 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
          } 
    }

Пример:

let gradient = self.cardGradientNode.view.gradient(
    color1: gradient.start, 
    color2: gradient.end
)
self.cardGradientNode.view.layer.insertSublayer(gradient, at: 0)

Результат: введите описание изображения здесь

Вот моя полностью основанная на Texture/AsyncDisplayKit реализация для этого. Это позволяет создать полностью универсальный класс Gradient Node, который не фиксирует никаких значений для всего класса, где до сих пор действуют другие решения, поскольку drawRect является функцией класса, а не функцией экземпляра.

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
    }

Все, что нужно сделать, это создать GradientNote и заполнить параметры в функции init(). А затем относитесь к нему как к обычному узлу, и пусть ASDK сделает всю остальную работу!

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

Пример:

node.gradient(from: .white, to: .red)

Вы задали тот же вопрос о проблемах AsyncDisplayKit в GitHub: https://github.com/facebookarchive/AsyncDisplayKit/issues/3253

Вам ответили 2 варианта:

1. Создайте CAGradientLayer и добавьте его в качестве подслоя узла

2: Переопределите метод + (void)draw... для узла и нарисуйте там градиент.

Первый вариант должен быть в главном потоке, чтобы не дать нам могущественных полномочий ASDK. Второй вариант более сложный, но я могу опубликовать пример из примеров проектов ASDK.

на основе https://github.com/facebookarchive/AsyncDisplayKit/blob/master/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m

а также

https://www.raywenderlich.com/124311/asyncdisplaykit-2-0-tutorial-getting-started

1. Подкласс ASDisplayNode

#import <AsyncDisplayKit/AsyncDisplayKit.h>

@interface GradientNode : ASDisplayNode

@end

2. Переопределить метод drawRect (основной градиент с 2 цветами - от черного к прозрачному)

@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. Настройка узла (важно! При использовании градиента с чистыми цветами, в противном случае он отображает непрозрачный черный вид)

_gradientNode = [GradientNode new];
_gradientNode.layerBacked = YES;
_gradientNode.opaque = NO;
Другие вопросы по тегам