Home > Software design >  How do I determine the arcCenter for a UIBezierPath relative to its parent view?
How do I determine the arcCenter for a UIBezierPath relative to its parent view?

Time:09-24

I am using a CAShapeLayer with UIBezierPath to make a circular progress bar. The code requires a center value based on a CGPoint.

let circleLayer = CAShapeLayer()
let circlePath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)

I have set the value manually as follows:

let center = CGPoint(x: 70, y: 70)

However those values are relative to the superView and I need to make it relative to its parent UIView.

All other elements in the UIView are constrained using programmatic UI code like this:

sampleText.translatesAutoresizingMaskIntoConstraints = false
sampleText.topAnchor.constraint(equalTo: directionsTitle.bottomAnchor, constant: 0).isActive = true
sampleText.leadingAnchor.constraint(equalTo: timerFooter.leadingAnchor, constant: 8).isActive = true
sampleText.trailingAnchor.constraint(equalTo: timerFooter.trailingAnchor, constant: -8).isActive = true

Fixed values may work fine on one device but will be out of alignment on a larger or smaller device. My question is, how do I determine the values for center of the UIView that will meet the values required of CGPoint?

I have tried this:

let centerX = view.centerXAnchor
let centerY = view.centerYAnchor
        
let center = CGPoint(x: centerX, y: centerY)

However, that throws an error: No exact matches in call to initializer

The following image reflects where the shape placement is relative to. The blue box indicates the desired UIView area where I want to position the circle.

enter image description here

Advice from wiser minds would be greatly appreciated.

CodePudding user response:

Ah, ok, I think I understand what the problem is that you're facing.

I think your issue is that you're trying to use the CAShapeLayer like it is a UIView. When in reality they don't behave like that at all.

What you should be doing is creating a UIView subclass and putting the CAShapeLayer in there. Now you only need to worry about how the CAShapeLayer fits into its own UIView subclass.

To "position the layer" into some other view you actually don't need to worry about that at all. You just place the new UIView subclass and the layer will go along with it.

e.g.

class MyProgressView: UIView {
  let circleLayer: CAShapeLayer

  // add the circleLayer
  // add properties for customising the circle layer
  // Position the circle layer in the centre of the MyProgressView frame
  // I'd suggest using `layoutSubviews` or something for this.
  // etc...
}

Now... to position your "CircleLayer" you actually don't need to. You just position the MyProgressView where you want it to be and the layer is part of that so will go along for the ride.

From your screenshot it looks like you want the view with the blue rectangle to contain the circleLayer. If that is the case then make the blue rectangle view your custom UIView subclass and ... done. It will now contain the circle view.

RayWenderlich has some good stuff about customising views with CALayer... https://www.raywenderlich.com/10317653-calayer-tutorial-for-ios-getting-started#toc-anchor-002

CodePudding user response:

It sounds like you want the midpoint of the UIView's bounds I think that's yourView.bounds.midX and yourView.bounds.midY

A view's bounds is its own coordinate space. A view's frame is its location in its parent space.

UIView has methods like convertPoint:toCoordinateSpace: and friends for converting points and rects between those spaces.

  • Related