我正在使用非常方便的UIColor(patternImage:)
在带有Xcode 8.2的iOS 10应用中使用平铺模式创建一些CAShapeLayer
。平铺始终从视图的原点开始,如果您希望它从其他地方开始,这可能会很不方便。为了说明,这里是模拟器的截图(下面的代码):
左边的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
?
答案 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切片缩放。
以下是UIImage
和PatternLayer
的代码:
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)
}
}
}
输出: