要将绿色小圆圈放在大圆圈的右上角,请执行以下操作:
-
使小圆成为大圆的子视图。
-
使用添加约束
.centerX
.trailing
在一个大圆圈里
multiplier
属于
0.8536
-
使用添加约束
.centerY
小圆的大小等于
.bottom
在一个大圆圈里
乘数
属于
0.1464
.
注:
乘数
s是用
三角学
通过查看单位圆并计算比率:
(distance from top of square containing unit circle)/(height of unit circle)
(distance from left edge of square containing unit circle)/(width of unit circle)
. 在下面的示例代码中,我提供了
func
computeMultipliers(angle:)
它计算任何
angle
以度为单位。准确避开角度
90
和
180
因为这可以创造出
0
哪一个
不喜欢。
以下是独立示例:
class ViewController: UIViewController {
var bigCircle: UIView!
var littleCircle: UIView!
override func viewDidLoad() {
super.viewDidLoad()
bigCircle = UIView()
bigCircle.translatesAutoresizingMaskIntoConstraints = false
bigCircle.backgroundColor = .red
view.addSubview(bigCircle)
bigCircle.widthAnchor.constraint(equalToConstant: 240).isActive = true
bigCircle.heightAnchor.constraint(equalToConstant: 240).isActive = true
littleCircle = UIView()
littleCircle.translatesAutoresizingMaskIntoConstraints = false
littleCircle.backgroundColor = .green
bigCircle.addSubview(littleCircle)
bigCircle.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
bigCircle.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
littleCircle.widthAnchor.constraint(equalToConstant: 60).isActive = true
littleCircle.heightAnchor.constraint(equalToConstant: 60).isActive = true
let (hMult, vMult) = computeMultipliers(angle: 45)
// position the little green circle using a multiplier on the right and bottom
NSLayoutConstraint(item: littleCircle!, attribute: .centerX, relatedBy: .equal, toItem: bigCircle!, attribute: .trailing, multiplier: hMult, constant: 0).isActive = true
NSLayoutConstraint(item: littleCircle!, attribute: .centerY, relatedBy: .equal, toItem: bigCircle!, attribute: .bottom, multiplier: vMult, constant: 0).isActive = true
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
bigCircle.layer.cornerRadius = 0.5 * bigCircle.frame.height
littleCircle.layoutIfNeeded()
littleCircle.layer.cornerRadius = 0.5 * littleCircle.frame.height
}
func computeMultipliers(angle: CGFloat) -> (CGFloat, CGFloat) {
let radians = angle * .pi / 180
let h = (1.0 + cos(radians)) / 2
let v = (1.0 - sin(radians)) / 2
return (h, v)
}
}
这是您的代码的修改版本。我添加了约束来设置小圆圈的大小,并移动了设置
cornerRadius
layoutSubviews()
:
class ProfilePictureView: UIView {
var bigCircle: UIView!
var borderCircle: UIView!
var littleCircle: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
private func initialize() {
bigCircle = UIView(frame: bounds)
bigCircle.backgroundColor = .red
addSubview(bigCircle)
borderCircle = UIView()
borderCircle.translatesAutoresizingMaskIntoConstraints = false
borderCircle.backgroundColor = .white
bigCircle.addSubview(borderCircle)
borderCircle.widthAnchor.constraint(equalTo: bigCircle.widthAnchor, multiplier: 1/3).isActive = true
borderCircle.heightAnchor.constraint(equalTo: bigCircle.heightAnchor, multiplier: 1/3).isActive = true
littleCircle = UIView()
littleCircle.translatesAutoresizingMaskIntoConstraints = false
littleCircle.backgroundColor = .green
borderCircle.addSubview(littleCircle)
littleCircle.widthAnchor.constraint(equalTo: borderCircle.widthAnchor, multiplier: 1/1.3).isActive = true
littleCircle.heightAnchor.constraint(equalTo: borderCircle.heightAnchor, multiplier: 1/1.3).isActive = true
littleCircle.centerXAnchor.constraint(equalTo: borderCircle.centerXAnchor).isActive = true
littleCircle.centerYAnchor.constraint(equalTo: borderCircle.centerYAnchor).isActive = true
let (hMult, vMult) = computeMultipliers(angle: 45)
// position the border circle using a multiplier on the right and bottom
NSLayoutConstraint(item: borderCircle!, attribute: .centerX, relatedBy: .equal, toItem: bigCircle!, attribute: .trailing, multiplier: hMult, constant: 0).isActive = true
NSLayoutConstraint(item: borderCircle!, attribute: .centerY, relatedBy: .equal, toItem: bigCircle!, attribute: .bottom, multiplier: vMult, constant: 0).isActive = true
}
override func layoutSubviews() {
super.layoutSubviews()
bigCircle.layer.cornerRadius = bigCircle.frame.height / 2
borderCircle.layoutIfNeeded()
borderCircle.layer.cornerRadius = borderCircle.frame.height / 2
littleCircle.layoutIfNeeded()
littleCircle.layer.cornerRadius = littleCircle.frame.height / 2
}
private func computeMultipliers(angle: CGFloat) -> (CGFloat, CGFloat) {
let radians = angle * .pi / 180
let h = (1.0 + cos(radians)) / 2
let v = (1.0 - sin(radians)) / 2
return (h, v)
}
}
背后的数学解释
计算机万用钳(角度:)
关于
是不是应该为
水平约束
垂直约束
0个
1
哪里
0个
顶部
0个
是
左边
1个
底部
垂直约束和
1个
是
水平约束的圆边缘。
乘法器是通过查看
the unit circle
1个
(0, 0)
在坐标系上。单位圆(根据定义)的好处是圆上的一条线(从原点开始)与圆相交的点是
(cos(angle), sin(angle))
从正开始测量角度
x-axis
逆时针到与圆相交的线。注意单位圆的宽度和高度分别为
2
.
sin(angle)
cos(angle)
各不相同
-1
到
.
方程式:
1 + cos(angle)
会有所不同
0个
到
2个
0个
到
1个
,我们把这个除以
2个
// compute the horizontal multiplier based upon the angle
let h = (1.0 + cos(radians)) / 2
在垂直方向,我们首先注意到坐标系是从数学意义上翻转的。在iOS中,
y
向下发展,但在数学上,
是的
向上生长。为此,垂直计算使用-
-
而不是
+
1 - sin(angle)
又一次,因为
sin
变化范围
到
,此计算将来自
0个
2个
,所以我们除以
2个
// compute the vertical multiplier based upon the angle
let h = (1.0 - sin(radians)) / 2
这给了我们想要的结果。当角度是
90个
.pi/2
弧度,
罪
1个
,所以垂直乘数为
. 当角度是
270
度(或
3*.pi/2
弧度,
罪
-1个
垂直乘数是
.
一旦你了解弧度是什么,弧度就是直观的。它们只是沿单位圆圆周的弧长。圆周长的公式是
circumference = 2 * .pi * radius
对于单位圆,周长是
2 * .pi
. 所以
360
度是
2*π