I have a mask applied to a view using CAShapeLayer and UIBezierPath. I'd like to add a rounding effect to the line joins but it's not working. How do I round the corners of this shape?
You can plug the following into an Xcode playground.
import PlaygroundSupport
import UIKit
private class ProfileImageView: UIView {
private let imageView = UIImageView()
var image: UIImage?
override init(frame: CGRect) {
super.init(frame: frame)
imageView.clipsToBounds = true
imageView.backgroundColor = UIColor.black
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
addSubview(imageView)
imageView.topAnchor.constraint(equalTo: topAnchor).isActive = true
imageView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
imageView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
return nil
}
override func draw(_ rect: CGRect) {
let h = rect.height
let w = rect.width
let path = UIBezierPath()
let shapeLayer = CAShapeLayer()
path.move(to: .zero)
path.addLine(to: CGPoint(x: w-32, y: 0))
path.addLine(to: CGPoint(x: w, y: 32))
path.addLine(to: CGPoint(x: w, y: h))
path.addLine(to: CGPoint(x: 32, y: h))
path.addLine(to: CGPoint(x: 0, y: h-32))
path.close()
path.lineJoinStyle = .round
shapeLayer.lineJoin = .round
shapeLayer.path = path.cgPath
layer.mask = shapeLayer
imageView.image = image
}
}
class VC: UIViewController {
override func loadView() {
view = UIView()
view.backgroundColor = .gray
let imgView = ProfileImageView()
imgView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imgView)
imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
imgView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
imgView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64).isActive = true
imgView.heightAnchor.constraint(equalTo: view.widthAnchor, constant: -64).isActive = true
}
}
PlaygroundPage.current.liveView = VC()
CodePudding user response:
lineJoinStyle is only for stroked paths. Since yours is a mask, you need a filled path instead so I think you'll need to use path.addCurve to achieve rounded corners in your mask. Or depending on your shape and size you may be able to just apply lineWidth, strokeColor and lineJoinStyle to your CAShapeLayer and get the rounded effect you're looking for.
CodePudding user response:
Still trying to guess at your goal, but maybe this is what you're looking for?
private class ProfileImageView: UIImageView {
public var cornerRadius: Double = 16 {
didSet {
setNeedsLayout()
}
}
public var angleRadius: Double = 24 {
didSet {
setNeedsLayout()
}
}
public var angleIndent: CGFloat = 32 {
didSet {
setNeedsLayout()
}
}
private let shapeLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
contentMode = .scaleAspectFill
layer.mask = shapeLayer
}
override func layoutSubviews() {
super.layoutSubviews()
let rect = bounds
let path = CGMutablePath()
path.move(to: CGPoint(x: rect.minX, y: rect.midY))
path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY),
tangent2End: CGPoint(x: rect.maxX, y: rect.minY),
radius: cornerRadius)
path.addArc(tangent1End: CGPoint(x: rect.maxX - angleIndent, y: rect.minY),
tangent2End: CGPoint(x: rect.maxX, y: rect.minY angleIndent),
radius: angleRadius)
path.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.minY angleIndent),
tangent2End: CGPoint(x: rect.maxX, y: rect.maxY),
radius: angleRadius)
path.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY),
tangent2End: CGPoint(x: rect.minX, y: rect.maxY),
radius: cornerRadius)
path.addArc(tangent1End: CGPoint(x: rect.minX angleIndent, y: rect.maxY),
tangent2End: CGPoint(x: rect.minX, y: rect.maxY - angleIndent),
radius: angleRadius)
path.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.maxY - angleIndent),
tangent2End: CGPoint(x: rect.minX, y: rect.minY),
radius: angleRadius)
path.closeSubpath()
shapeLayer.path = path
}
}
class VC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
guard let img = UIImage(named: "sampleImage") else {
fatalError("Could not load sample image!!")
}
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 20
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
let imgView1 = ProfileImageView(frame: .zero)
imgView1.image = img
let imgView2 = ProfileImageView(frame: .zero)
imgView2.image = img
// top view uses default properties,
stackView.addArrangedSubview(imgView1)
// slightly different properties for the bottom view
imgView2.cornerRadius = 24
imgView2.angleRadius = 32
imgView2.angleIndent = 48
stackView.addArrangedSubview(imgView2)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
stackView.widthAnchor.constraint(equalTo: g.widthAnchor, constant: -100.0),
imgView1.heightAnchor.constraint(equalTo: imgView1.widthAnchor),
])
}
}

