4

There are probably more than 5 questions just like this one, but none of them solve the problem.

I want to center a circle on a view. It's not happening despite trying all kinds of methods through similar questions like this one.

I'm not sure if this happens because I set the translatesAutoresizingMaskIntoConstraints = false. I tried playing around with the center and anchor points of the shape layer but crazy behavior happens. I tried putting this code in viewDidLayouSubviews. Didn't work. What else can I do?

colorSizeGuide is the view of which I'm trying to center my layer in.

func setupConstraints() {
    // other constraints set up here
    colorSizeGuide.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    colorSizeGuide.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
    colorSizeGuide.widthAnchor.constraint(equalToConstant: 30).isActive = true
    colorSizeGuide.heightAnchor.constraint(equalToConstant: 30).isActive = true
}

func setupColorSizeGuide() {
    shape.path = UIBezierPath(ovalIn: colorSizeGuide.frame).cgPath
    shape.position = self.colorSizeGuide.center
    shape.anchorPoint = CGPoint(x: 0.5, y: 0.5)

    shape.strokeColor = (UIColor.black).cgColor
    shape.fillColor = (UIColor.clear).cgColor
    shape.lineWidth = 1.0
    colorSizeGuide.layer.addSublayer(shape)

} 
rocky raccoon
  • 514
  • 1
  • 8
  • 23
  • If all you want is a circle, have you considered just setting corner radius on a UIView's layer and adding that view as a subview? – Lucas Derraugh Dec 15 '16 at 07:09
  • CAShapeLayer, which inherits from CALayer, does not use Auto Layout. **BUT**... Every UIView (along with those that inherit from it) **DO** have a layer property attached to it. –  Dec 15 '16 at 07:12
  • @LucasDerraugh I was thinking about it, but then I would have to recalculate the corner radius if I want to make it bigger. **I should have mentioned that I'm using a slider to make the circle bigger or smaller**. – rocky raccoon Dec 15 '16 at 16:10

1 Answers1

3

There are a few options you can do.

  1. If you're not in a scrolling environment (e.g. tableView) use corner radius of half of the width of the rectangle on the view, then the view will be cropped to a circle - this is not particularly fast or customizable, but it gets the job done.

enter image description here

Playground Code:

import UIKit
import PlaygroundSupport

let frame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100)
let view: UIView = UIView(frame: frame)
view.backgroundColor = .magenta
view.layer.cornerRadius = frame.size.width / 2
PlaygroundPage.current.liveView = view
  1. You can make your custom UIView subclass implement the layerClass method and return CAShapeLayer there. This will mean that self.layer of your view will actually be a shape layer. You can set a path of a circle there and there you go. If you want to center it in a view just make sure to use bounds coordinates of the view when defining the UIBezierPath.

enter image description here

Playground code:

import UIKit
import PlaygroundSupport

let frame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100)
let view: UIView = UIView(frame: frame)
PlaygroundPage.current.liveView = view

class ShapeView: UIView {

    override class var layerClass: AnyClass { return CAShapeLayer.self }

    override init(frame: CGRect) {
        super.init(frame: frame)
        (layer as? CAShapeLayer)?.fillColor = UIColor.red.cgColor
        (layer as? CAShapeLayer)?.strokeColor = UIColor.green.cgColor
        (layer as? CAShapeLayer)?.path = UIBezierPath(ovalIn: frame).cgPath
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

let sv = ShapeView(frame: frame)
view.addSubview(sv)
  1. Make a non-view shape layer, by doing CAShapeLayer.init. Position your on your view in its layoutSubviews method (or viewDidLayoutSubviews if you're doing it in the view controller) and set a proper frame to the shape layer, like it was a view.

enter image description here

Playground Code:

import UIKit
import PlaygroundSupport

let frame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100)
let smallFrame: CGRect = CGRect(x: 0, y: 0, width: 10, height: 10)
let view: UIView = UIView(frame: frame)
view.backgroundColor = .magenta
PlaygroundPage.current.liveView = view



let sl = CAShapeLayer()
sl.path = UIBezierPath(ovalIn: smallFrame).cgPath
sl.fillColor = UIColor.red.cgColor
sl.strokeColor = UIColor.green.cgColor

sl.frame.origin.x = 30
sl.frame.origin.y = 30

view.layer.addSublayer(sl)
Alistra
  • 5,177
  • 2
  • 30
  • 42