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),
])
}
}