How do I place the objects in the array around the

2019-03-02 16:12发布

I have an array of buttons and when I append them to a view I want the to be positioned around a image view which is in the center. Based on how many objects there are in the array, I want them to be evenly spaced around the whole circle. Below is my attempt to do so. What am I doing wrong and how should I fix it? There is more than one button behind the moose.

enter image description here

var userbutton = [UIButton]()
var upimage = [UIImage]()
var locationpic = [AnyObject]()

 func locationsSet(){

    for (index, users) in upimage.enumerate() {

        let userbutton = UIButton()
        userbutton.addTarget(self, action: "buttonAction:", forControlEvents: .TouchUpInside)
        userbutton.frame = CGRectMake(100, 100, 50, 50)
        userbutton.layer.cornerRadius = userbutton.frame.size.width/2
        userbutton.clipsToBounds = true
        userbutton.setImage(users, forState: .Normal)

        let radians = CGFloat(M_PI) * 2.0 / CGFloat(upimage.count) * CGFloat(index)

        let centerx = self.view.bounds.width / 2.0
        let radius =  currentuserpic.frame.size.width / 2.0
        let centery = self.view.bounds.height / 2.0

        let pointx = centerx + cos(radians) * (radius + 40)
        let pointy = (centery) + (sin(radians)) * (radius + 40)

        userbutton.center.x = pointx
        userbutton.center.y = pointy

        self.userbutton.append(userbutton)
        self.view.addSubview(userbutton)

        print("x\(pointx)")
        print("y\(pointy)")


    }


}

标签: ios swift layout
2条回答
祖国的老花朵
2楼-- · 2019-03-02 16:45

How I would do this:

Create an extension to UIView to get the diagonal and radius. These are handy because we want our "satellites" to have predictable placing even when the "planet" isn't square.

extension UIView {

    var diagonal : CGFloat {
        return sqrt(pow(self.frame.width, 2) + pow(self.frame.height, 2))
    }

    var radius : CGFloat {
        return diagonal / 2
    }
}

This will return a point based on an angle and a distance from an origin. It uses dreadful trigonometry.

func getPoint(fromPoint point: CGPoint, atDistance distance: CGFloat, withAngleRadians angle:CGFloat) -> CGPoint {

    let x = point.x
    let y = point.y

    let dx = (distance * cos(angle))
    let dy = (distance * sin(angle))

    return CGPoint(x: (dx + x), y: (dy + y))
}

Now the real function. Generate a bunch of points in a circle pattern. I used a running sum for the angle instead of multiplying each time by the index. This just returns the centre points for the views.

func encirclePoint(point : CGPoint, distance:CGFloat, inParts parts: Int) -> [CGPoint] {

    let angle = 2 * CGFloat(M_PI) / CGFloat(parts) // critical part, you need radians for trigonometry
    var runningAngle : CGFloat = -(CGFloat(M_PI) / 2) // start at the top

    var points : [CGPoint] = []

    for _ in 0..<parts {
        let circlePoint = getPoint(fromPoint: point, atDistance: distance, withAngleRadians: runningAngle)
        points.append(circlePoint)
        runningAngle += angle
    }

    return points
}

Now you can create a simple function that takes a view, a margin and an array of "satellite" views. It will set their centre and add them to the superview of the view we used to input. It makes sense not to add them to the view itself since they might not be placed inside it.

func encircleView(view : UIView, withSubViews subViews : [UIView], withMargin margin : CGFloat) {

    guard !(subViews.isEmpty) else { // if there are no subviews : abort
        return
    }

    let distance = view.radius + margin
    let points = encirclePoint(view.center, distance: distance, inParts: subViews.count)

    guard subViews.count == points.count, let uberView = view.superview else { // if the count is not the same or there is no superview: abort
        return
    }

    for (point, subView) in zip(points, subViews) { subView.center = point }
}

Notice how I did nothing except for the centre calculations in these functions. Styling them goes in another function. This makes it super easy to maintain and debug.

I might even let the last function just return the subviews with updated frames and add them later.


enter image description here


Or negative margin :)

enter image description here

Gist

查看更多
SAY GOODBYE
3楼-- · 2019-03-02 16:45

A full circle is 2 * pi radians. You need to divide that by the number of items you have and multiply that by the index of the item you are currently processing. Use trig to find the location on the circle:

for (index, users) in upimage.enumerate() {
    let radians = CGFloat(M_PI) * 2.0 / CGFloat(upimage.count) * CGFloat(index)

    ......

    let centerx = self.view.bounds.width / 2.0
    let radius =  currentuserpic.frame.size.width / 2.0
    let centery = self.view.bounds.height / 2.0

    let pointx = centerx + cos(radians) * radius
    let pointy = centery + sin(radians) * radius

    ......
}
查看更多
登录 后发表回答