如何在Swift中更改UIColor patternImage的阶段?

时间:2017-01-23 07:51:57

标签: ios swift calayer uicolor cashapelayer

我正在使用非常方便的UIColor(patternImage:)在带有Xcode 8.2的iOS 10应用中使用平铺模式创建一些CAShapeLayer。平铺始终从视图的原点开始,如果您希望它从其他地方开始,这可能会很不方便。为了说明,这里是模拟器的截图(下面的代码):

Tiling problem

左边的CAShapeLayer从(0,0)开始,所以一切都很好。右边的那个是(110,50),所以它在中间分开。这是代码:

let firstBox = CAShapeLayer()
firstBox.fillColor = UIColor(patternImage: UIImage(named: "test-image")!).cgColor
view.layer.addSublayer(firstBox)
firstBox.path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 100, height: 100)).cgPath

let secondBox = CAShapeLayer()
secondBox.fillColor = UIColor(patternImage: UIImage(named: "test-image")!).cgColor
view.layer.addSublayer(secondBox)
secondBox.path = UIBezierPath(rect: CGRect(x: 110, y: 50, width: 100, height: 100)).cgPath

我想调整右侧CAShapeLayer的模式的相位,以便两个图块都显示正面。 Apple的documentation用于UIColor(patternImage :)有助于为此目的引用一个函数:

  

要更改相位,请将颜色设为当前颜色,然后使用   setPatternPhase(_:)用于改变阶段。

听起来很简单!但是我很难实现它。我不太确定“使颜色成为当前颜色”意味着什么。在尝试将填充颜色分配给图层之前和之后,我尝试获取当前上下文并在其上调用setPatternPhase

UIGraphicsGetCurrentContext()?.setPatternPhase(CGSize(width: 25, height: 25))

没有明显的影响。我尝试了对包含UIView进行子类化并在其drawRect:方法中设置阶段,如this answer中所述。但是在Swift中不存在drawRect:,因此我尝试了draw(_ rect:)draw(_ layer:, in:)。两个函数都被调用,但没有明显的效果。

class PatternView: UIView {
    override func draw(_ rect: CGRect) {
        UIGraphicsGetCurrentContext()?.setPatternPhase(CGSize(width: 25, height: 25))
        super.draw(rect)
    }
    override func draw(_ layer: CALayer, in ctx: CGContext) {
        ctx.setPatternPhase(CGSize(width: 25, height: 25))
        super.draw(layer, in: ctx)
    }
}

在Dave Weston的建议中,我使用UIImage的{​​{1}}来设置当前笔划并填写当前上下文,然后再调用.set()。不幸的是,输出不受影响。这是我试过的代码:

setPatternPhase

如何将绘制的模式的相位转换为let secondBoxColor = UIColor(patternImage: UIImage(named: "test-image")!) secondBoxColor.set() UIGraphicsGetCurrentContext()?.setPatternPhase(CGSize(width: 50, height: 50)) let secondBox = CAShapeLayer() secondBox.fillColor = secondBoxColor.cgColor view.layer.addSublayer(secondBox) secondBox.path = UIBezierPath(rect: CGRect(x: 110, y: 50, width: 100, height: 100)).cgPath

2 个答案:

答案 0 :(得分:0)

要使您的模式成为当前颜色,您应该在包含模式的set()实例上调用UIColor实例方法。这会将颜色配置为当前笔触和当前上下文的填充颜色。

然后,根据Apple的文档,setPatternPhase应该有效。

答案 1 :(得分:0)

我还没有能够解决这个问题,但我想我会分享我使用的解决方法,万一它对任何人都有用。

据我所知,CAShapeLayer在一个隐秘的隐藏位置进行渲染,忽略display()所谓的draw()CALayerDelegate函数使用。因此,您永远无法访问CGContext用于呈现的内容,因此无法调用setPatternPhase()

setPatternPhase()的具体用例是将图案与CAShapeLayer的左上角对齐,所以我找到了另一种方法。 它不允许您设置任意阶段。

我所做的是创建一个名为CALayer的新CAPatternLayer子类,其中UIImage为tile,CGPath为填充。它委托给名为CALayerDelegate的{​​{1}}类,该类提供CAPatternLayerDelegate函数。请求绘图时,代理会创建一个临时draw(layer: in ctx:),用平铺图像填充它,然后将其渲染到UIImageView的上下文。

这样做的一个明显的副作用是你可以使用带有帽子插入的CALayer,这允许在平铺中心切片的情况下进行9切片缩放。

以下是UIImagePatternLayer的代码:

PatternLayerDelegate

这是一个正在使用的例子:

class CAPatternLayer: CALayer {
    var image: UIImage?
    var path: CGPath? {
        didSet {
            if let path = self.path {
                self.frame = path.boundingBoxOfPath
                // shift the path to 0,0 since you built position into the frame
                var translation = CGAffineTransform(translationX: -path.boundingBoxOfPath.origin.x, y: -path.boundingBoxOfPath.origin.y)
                let shiftedPath = path.copy(using: &translation)
                // use the shifted version
                self.path = shiftedPath
                self.maskLayer.path = shiftedPath
            }
        }
    }
    let maskLayer: CAShapeLayer = CAShapeLayer()

    override init() {
        super.init()
        self.delegate = CAPatternLayerDelegate.sharedInstance
        self.setNeedsDisplay()
        self.mask = self.maskLayer
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    convenience init(path: CGPath, image: UIImage) {
        self.init()
        defer {
            self.image = image
            self.path = path
        }
    }
}

class CAPatternLayerDelegate: NSObject, CALayerDelegate {
    static let sharedInstance = CAPatternLayerDelegate()
    func draw(_ layer: CALayer, in ctx: CGContext) {
        // cast layer to a CAPatternLayer so you can access properties
        if let layer = layer as? CAPatternLayer, let image = layer.image, let path = layer.path {
            // create a UIImageView to display the image, then render it to the context
            let imageView = UIImageView()
            // if a path bounding box was set, use it, otherwise draw over the whole layer
            imageView.bounds = path.boundingBoxOfPath
            imageView.image = image
            imageView.layer.render(in: ctx)
        }
    }
}

输出:

9-slice tiled pattern