Reputation: 491
I am trying to punch a circular hole through a UIView that is above a UIImageView, whereby the hole can see through to the image below (I would like to interact with this image through the hole with a GestureRecognizer later). I have 2 problems, I cannot get the circular hole to centre to the middle of the UIImageView (it is currently centred to the top left of the screen), and that the effect that I am getting is the opposite to what i am trying to achieve (Everything outside of the circle is visible). Below is my code. Please can someone advise?
class UploadProfileImageViewController: UIViewController {
var scrollView: ReadyToUseScrollView!
let container: UIView = {
let view = UIView()
view.backgroundColor = UIColor.black
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let imgView: UIImageView = {
let imgView = UIImageView()
imgView.backgroundColor = UIColor.black
imgView.contentMode = .scaleAspectFit
imgView.image = UIImage.init(named: "soldier")!
imgView.translatesAutoresizingMaskIntoConstraints = false
return imgView
}()
var overlay: UIView!
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
private func setup(){
view.backgroundColor = UIColor.white
setupViews()
}
override func viewDidLayoutSubviews() {
overlay.center = imgView.center
print("imgView.center: \(imgView.center)")
overlay.layer.layoutIfNeeded() // I have also tried view.layoutIfNeeded()
}
private func setupViews(){
let s = view.safeAreaLayoutGuide
view.addSubview(imgView)
imgView.topAnchor.constraint(equalTo: s.topAnchor).isActive = true
imgView.leadingAnchor.constraint(equalTo: s.leadingAnchor).isActive = true
imgView.trailingAnchor.constraint(equalTo: s.trailingAnchor).isActive = true
imgView.heightAnchor.constraint(equalTo: s.heightAnchor, multiplier: 0.7).isActive = true
overlay = Overlay.init(frame: .zero, center: imgView.center)
print("setup.imgView.center: \(imgView.center)")
view.addSubview(overlay)
overlay.translatesAutoresizingMaskIntoConstraints = false
overlay.topAnchor.constraint(equalTo: s.topAnchor).isActive = true
overlay.leadingAnchor.constraint(equalTo: s.leadingAnchor).isActive = true
overlay.trailingAnchor.constraint(equalTo: s.trailingAnchor).isActive = true
overlay.bottomAnchor.constraint(equalTo: imgView.bottomAnchor).isActive = true
}
private func deg2rad( number: Double) -> CGFloat{
let rad = number * .pi / 180
return CGFloat.init(rad)
}
}
class Overlay: UIView{
var path: UIBezierPath!
var viewCenter: CGPoint?
init(frame: CGRect, center: CGPoint) {
super.init(frame: frame)
self.viewCenter = center
setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setup(){
backgroundColor = UIColor.black.withAlphaComponent(0.8)
guard let path = createCirclePath() else {return}
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillRule = CAShapeLayerFillRule.evenOdd
self.layer.mask = shapeLayer
}
private func createCirclePath() -> UIBezierPath?{
guard let center = self.viewCenter else{return nil}
let circlePath = UIBezierPath()
circlePath.addArc(withCenter: center, radius: 200, startAngle: 0, endAngle: deg2rad(number: 360), clockwise: true)
return circlePath
}
private func deg2rad( number: Double) -> CGFloat{
let rad = number * .pi / 180
return CGFloat.init(rad)
}
}
CONSOLE:
setup.imgView.center: (0.0, 0.0)
imgView.center: (207.0, 359.0)
Upvotes: 0
Views: 40
Reputation: 4245
Try getting rid of the overlay you have and instead add the below UIView. It's basically a circular UIView with a giant black border, but it takes up the whole screen so the user can't tell. FYI, you need to use .frame to position items on the screen. The below puts the circle in the center of the screen. If you want the center of the image, replace self.view.frame with self. imgView.frame... Play around with circleSize and borderSize until you get the circle size you want.
let circle = UIView()
let circleSize: CGFloat = self.view.frame.height * 2 //must be bigger than the screen
let x = (self.view.frame.width / 2) - (circleSize / 2)
let y = (self.view.frame.height / 2) - (circleSize / 2)
circle.frame = CGRect(x: x, y: y, width: circleSize, height: circleSize)
let borderSize = (circleSize / 2) * 0.9 //the size of the inner circle will be circleSize - borderSize
circle.backgroundColor = .clear
circle.layer.cornerRadius = circle.frame.height / 2
circle.layer.borderColor = UIColor.black.cgColor
circle.layer.borderWidth = borderSize
view.addSubview(circle)
Upvotes: 1