Greg Greg - 20 days ago 7
iOS Question

how do I rotate a group of buttons programmatically in Swift?

I am trying to arrange numbered buttons programmatically in a circle. NSLayoutConstraint anchors the view while a subClass of UIView creates the circle of buttons. The frame rotates about the centre but buttons rotate inconsistently. Text orientation is the same on all but one rotation.

for example


enter image description here

My Swift code for arranging buttons improves on an earlier effort in Objective C. I have also followed up a few helpful suggestions for rotating a view about the screen centre including this one.

I'd be grateful if anyone can show me how to improve my code so the UI is consistent for every screen size and orientation.

Here is the Viewcontroller

import UIKit

class ViewController: UIViewController {

var circle: CircleView?

override func viewDidLoad() {
super.viewDidLoad()

let circle = CircleView()
circle.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(circle)

let horizontalConstraint = circle.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let verticalConstraint = circle.centerYAnchor.constraint(equalTo: view.centerYAnchor)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint])
}
}


... and the subclass of UIView

import UIKit

class CircleView: UIView {

// MARK: Initialization

let points: Int = 10 // 80 25 16 10 5
let dotSize: CGFloat = 60 // 12 35 50 60 100
let radius: CGFloat = 48 // 72 70 64 48 45

var arcPoint = CGFloat(M_PI * -0.5) // clockwise from 12+ (not 3+)!

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

override init(frame: CGRect) {
super.init(frame: frame)

drawUberCircle()
drawBoundaryCircles()
}


... the second last function draws the coloured circular background

func drawUberCircle() {

// Create a CAShapeLayer

let shapeLayer = CAShapeLayer()

// give Bezier path layer properties
shapeLayer.path = createBezierPath().cgPath

shapeLayer.strokeColor = UIColor.cyan.cgColor
shapeLayer.fillColor = UIColor.cyan.cgColor
shapeLayer.lineWidth = 1.0
self.layer.addSublayer(shapeLayer)
}
func createBezierPath() -> UIBezierPath {

let path = UIBezierPath(arcCenter: CGPoint(x: 0, y: 0),
radius: radius * 2,
startAngle: CGFloat(M_PI * -0.5),
endAngle: CGFloat(M_PI * 1.5),
clockwise: true)
return path
}


... while the last function draws buttons in a circular arc

func drawBoundaryCircles() {

for index in 1...points {
let point: CGPoint = makeBoundaryPoint()
drawButton(point: point, index: index)
}
}


func makeBoundaryPoint() -> (CGPoint) {
arcPoint += arcAngle()
print(arcPoint)
let point = CGPoint(x: 0 + (radius * 2 * cos(arcPoint)), y: 0 + (radius * 2 * sin(arcPoint)))
return (point)
}


func arcAngle() -> CGFloat {
return CGFloat(2.0 * M_PI) / CGFloat(points)
}


func drawButton(point: CGPoint, index: Int) {
let myButton = UIButton(type: .custom) as UIButton
myButton.frame = CGRect(x: point.x - (dotSize/2), y: point.y - (dotSize/2), width: dotSize, height: dotSize)
myButton.backgroundColor = UIColor.white
myButton.layer.cornerRadius = dotSize / 2
myButton.layer.borderWidth = 1
myButton.layer.borderColor = UIColor.black.cgColor
myButton.clipsToBounds = true
myButton.titleLabel!.font = UIFont(name: "HelveticaNeue-Thin", size: dotSize/2)
myButton.setTitleColor(UIColor.red, for: .normal)
myButton.setTitle(String(index), for: .normal)
myButton.tag = index;
myButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
addSubview(myButton)
}


func buttonAction(myButton: UIButton) {
let sender:UIButton = myButton
print("Button \(sender.tag) was tapped")
}
}

Answer

For an iphone, this behavior is correct. You usually do not support rotation for upside down because it makes it confusing if the user has to answer a call; its in the apple HIG guidelines. Look at your project settings on the general tab under device orientation. By default Upside Down is disabled. Check it if you want to support Upside Down rotation.

Comments