如何使 CAShape 层适合 UIImage 视图?
Posted
技术标签:
【中文标题】如何使 CAShape 层适合 UIImage 视图?【英文标题】:How to fit CAShape Layer to UIImage View? 【发布时间】:2021-10-18 10:03:47 【问题描述】:我正在解析 SVG 图像文件并将其路径转换为 CAShapeLayer,之后我将此 CAShapeLayer 添加到 UIImage View 的图层。 它工作正常,但问题是它不适合 UIImage 视图。 那是输出。 Image 1。 这是向 UIImageView 添加图层的代码
if let svgURL = Bundle.main.url(forResource: "image", withExtension: "svg")
let paths = SVGBezierPath.pathsFromSVG(at: svgURL)
for path in paths
items.append(path)
let layer = createLayer(path: path)
layer.frame = self.backgroundIV.bounds
self.backgroundIV.layer.addSublayer(layer)
// Create Layer from Paths Method
fileprivate func createLayer(path: SVGBezierPath) -> CAShapeLayer
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
if let any = path.svgAttributes["stroke"]
shapeLayer.strokeColor = (any as! CGColor)
if let any = path.svgAttributes["fill"]
shapeLayer.fillColor = (any as! CGColor)
return shapeLayer
我搜索并添加了这两行代码,我得到了这个结果
let scale = CGFloat(0.5)
for path in paths
path.apply(CGAffineTransform(scaleX: scale, y: scale))
items.append(path)
let layer = createLayer(path: path)
layer.frame = self.backgroundIV.bounds
self.backgroundIV.layer.addSublayer(layer)
这是输出。 Image 2
我不知道如何根据uiImage View bounds计算scaleX
或scaleY
(在CGAffineTransform
方法中使用)值
编辑 注意:我对 CAShapeLayer 应用了触摸支持, 那是代码。
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
guard let point = touches.first?.location(in: self.backgroundIV),
let layers = self.backgroundIV.layer.sublayers
else return
var hitLayers: [CAShapeLayer] = []
selectedLayer?.lineWidth = CGFloat(0)
for subLayer in layers
if let thisLayer = subLayer as? CAShapeLayer,
let pth = thisLayer.path
let layerPoint: CGPoint = thisLayer.convert(point, from: self.backgroundIV.layer)
if pth.contains(layerPoint)
// undoModel.append()
hitLayers.append(thisLayer)
selectedLayer = hitLayers.last
selectedLayer?.strokeColor = UIColor.red.cgColor
selectedLayer?.lineWidth = CGFloat(3)
undo.append(.init(stokeColor: nil, selectedLayer: selectedLayer, points: []))
if ((selectedLayer?.frame.contains(point)) != nil)
isDragged = true
【问题讨论】:
我想知道是否不是因为您正在检查的尺寸:self.backgroundIV.bounds
不是最后一个。或者它是?你能打印出来检查一下吗?如果不是最后一个,请检查 layoutSubviews,并在此处更新图层框架/比例。
我相信您使用的是 PocketSVG,那么最好使用提供的 SVGImageView。
@ChanOnly123 在我的情况下它不起作用。
@AliAkbar - 假设您使用的是PocketSVG
(基于您之前的问题),您是否有理由不使用它的SVGImageView
?
【参考方案1】:
根据您更新的问题,使用视图边界计算比例
var paths = [SVGBezierPath]()
override func viewDidLoad()
super.viewDidLoad()
if let svgURL = Bundle.main.url(forResource: "Freesample", withExtension: "svg")
paths = SVGBezierPath.pathsFromSVG(at: svgURL)
backgroundIV.layer.borderColor = UIColor.black.withAlphaComponent(0.3).cgColor
backgroundIV.layer.borderWidth = 1
override func viewDidLayoutSubviews()
super.viewDidLayoutSubviews()
let svgBounds = SVGBoundingRectForPaths(paths)
var scale = CGFloat.zero
if backgroundIV.bounds.width < backgroundIV.bounds.height
scale = backgroundIV.bounds.width / svgBounds.width
else
scale = backgroundIV.bounds.height / svgBounds.height
self.backgroundIV.layer.sublayers?.forEach $0.removeFromSuperlayer()
for path in paths
path.apply(.init(scaleX: scale, y: scale))
let layer = createLayer(path: path)
layer.frame = self.backgroundIV.bounds
self.backgroundIV.layer.addSublayer(layer)
【讨论】:
感谢您的回复,它可以工作,但我忘了告诉我我对图层应用了触摸支持,所以 UIImage 不会处理它。 谢谢,一切正常。【参考方案2】:这与我在上一个关于检测多层触摸的问题中给出的答案基本相同。
不同之处在于,此示例使用SVGImageView
,而不是使用您的代码来创建/添加图层,它是PocketSVG
的一部分:
class BoxesViewController: UIViewController
var backgroundIV: SVGImageView!
var detectMode: DetectMode = .All
var detectType: DetectType = .ShapePath
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = .systemYellow
guard let svgURL = Bundle.main.url(forResource: "roundedboxes", withExtension: "svg") else
fatalError("SVG file not found!!!")
backgroundIV = SVGImageView.init(contentsOf: svgURL)
backgroundIV.frame = view.bounds
backgroundIV.contentMode = .scaleAspectFit
backgroundIV.autoresizingMask = [.flexibleWidth, .flexibleHeight]
let modeControl = UISegmentedControl(items: ["All", "Top Most", "Bottom Most"])
let typeControl = UISegmentedControl(items: ["Shape Path", "Shape Bounding Box"])
modeControl.translatesAutoresizingMaskIntoConstraints = false
typeControl.translatesAutoresizingMaskIntoConstraints = false
backgroundIV.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(modeControl)
view.addSubview(typeControl)
view.addSubview(backgroundIV)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
modeControl.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
modeControl.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
modeControl.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
typeControl.topAnchor.constraint(equalTo: modeControl.bottomAnchor, constant: 40.0),
typeControl.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
typeControl.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
backgroundIV.topAnchor.constraint(equalTo: typeControl.bottomAnchor, constant: 40.0),
backgroundIV.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
backgroundIV.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
backgroundIV.heightAnchor.constraint(equalTo: backgroundIV.widthAnchor),
])
modeControl.addTarget(self, action: #selector(modeChanged(_:)), for: .valueChanged)
typeControl.addTarget(self, action: #selector(typeChanged(_:)), for: .valueChanged)
modeControl.selectedSegmentIndex = 0
typeControl.selectedSegmentIndex = 0
// so we can see the frame of the image view
backgroundIV.backgroundColor = .white
@objc func modeChanged(_ sender: UISegmentedControl) -> Void
switch sender.selectedSegmentIndex
case 0:
detectMode = .All
case 1:
detectMode = .TopMost
case 2:
detectMode = .BottomMost
default:
()
@objc func typeChanged(_ sender: UISegmentedControl) -> Void
switch sender.selectedSegmentIndex
case 0:
detectType = .ShapePath
case 1:
detectType = .ShapeBounds
default:
()
fileprivate func createLayer(path: SVGBezierPath) -> CAShapeLayer
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
if let any = path.svgAttributes["stroke"]
shapeLayer.strokeColor = (any as! CGColor)
if let any = path.svgAttributes["fill"]
shapeLayer.fillColor = (any as! CGColor)
return shapeLayer
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
guard let point = touches.first?.location(in: self.backgroundIV),
// make sure backgroundIV has sublayers
let layers = self.backgroundIV.layer.sublayers
else return
var hitLayers: [CAShapeLayer] = []
// loop through all sublayers
for subLayer in layers
// make sure
// it is a CAShapeLayer
// it has a path
if let thisLayer = subLayer as? CAShapeLayer,
let pth = thisLayer.path
// clear the lineWidth... we'll reset it after getting the hit layers
thisLayer.lineWidth = 0
// convert touch point from backgroundIV.layer to thisLayer
let layerPoint: CGPoint = thisLayer.convert(point, from: self.backgroundIV.layer)
if detectType == .ShapePath
// does the path contain the point?
if pth.contains(layerPoint)
hitLayers.append(thisLayer)
else if detectType == .ShapeBounds
if pth.boundingBox.contains(layerPoint)
hitLayers.append(thisLayer)
if detectMode == .All
hitLayers.forEach layer in
layer.strokeColor = UIColor.cyan.cgColor
layer.lineWidth = 3
else if detectMode == .TopMost
if let layer = hitLayers.last
layer.strokeColor = UIColor.cyan.cgColor
layer.lineWidth = 3
else if detectMode == .BottomMost
if let layer = hitLayers.first
layer.strokeColor = UIColor.cyan.cgColor
layer.lineWidth = 3
现在 SVG 文件被缩放以适应视图:
【讨论】:
以上是关于如何使 CAShape 层适合 UIImage 视图?的主要内容,如果未能解决你的问题,请参考以下文章
iPhone 编程:如何使 UIImageView 中的 UIImage 以实际大小显示?
如何使 UIScrollView 缩放到 UIImage 的大小?