【问题标题】:How to add gradient to ASDisplayNode or ASButtonNode如何向 ASDisplayNode 或 ASButtonNode 添加渐变
【发布时间】:2017-04-10 10:56:32
【问题描述】:

我希望将渐变背景添加到 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 角度的情况下,如何添加背景?

谢谢

【问题讨论】:

  • 一般编辑节点的视图应该在主线程中,否则会抛出异常-'这个方法必须在主线程上调用'。我试图找到一个很酷的例子来展示如何以相对简单的方式做到这一点。

标签: ios gradient cagradientlayer asyncdisplaykit


【解决方案1】:

斯威夫特 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)

结果:

【讨论】:

    【解决方案2】:

    这是我完全基于 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() 函数中填写参数即可。然后把它当作一个普通的Node,让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
    

    【讨论】:

    • 除了圆角半径外完美运行。您可以通过 let path = UIBezierPath(roundedRect: bounds,cornerRadius: parameters.cornerRadius) path.addClip() 替换 context.clip(to: bounds)
    【解决方案3】:

    您在 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;
    

    【讨论】:

      【解决方案4】:
      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)
      

      【讨论】:

      • 如果框架发生变化,例如在旋转时,这将不起作用
      猜你喜欢
      • 2011-09-14
      • 1970-01-01
      • 1970-01-01
      • 2019-07-18
      • 2020-03-16
      • 1970-01-01
      • 1970-01-01
      • 2020-05-01
      • 2012-03-02
      相关资源
      最近更新 更多