我一直在学习解释自定义ios控件的教程。这个教程没有问题,但我对框架/边界剪辑感到困惑(不是教程的一部分)。
我在故事板中的场景中有一个UIView实例。这个UIView的大小为120 x 120.我正在使用addSubview
将自定义控件(扩展UIControl)添加到此容器视图中。我开始尝试为自定义控件的框架和边界设置不同的宽度和高度,这是控件的初始化器:
public override init(frame: CGRect) {
super.init(frame: frame)
createSublayers()
}
...并生成此结果(红色是父级,蓝色圈子是孩子):
现在我将init更改为:
public override init(frame: CGRect) {
super.init(frame: frame)
createSublayers()
self.frame.size.width = 40
self.frame.size.height = 40
println("frame: \(self.frame)")
println("bounds: \(self.bounds)")
self.clipsToBounds = true
}
这就产生了这个结果:
和打印: 框架:(0.0,0.0,40.0,40.0) 界限:(0.0,0.0,40.0,40.0)
但是当我将initliazer更改为:
public override init(frame: CGRect) {
super.init(frame: frame)
createSublayers()
self.bounds.size.width = 40
self.bounds.size.height = 40
println("frame: \(self.frame)")
println("bounds: \(self.bounds)")
self.clipsToBounds = true
}
我明白了:
和打印:框架:(40.0,40.0,40.0,40.0) 界限:(0.0,0.0,40.0,40.0)
我似乎无法理解为什么当我更改其边界时此视图以其父视图为中心。究竟是什么导致了它?是'框架'总是朝着它的中心剪短?或者,在修改边界后,此视图以其父视图为中心,并导致更新框架'?是否可以更改某些属性?例如,我怎么能设法将它放在左上角(与修改'框架'时的方式完全一样)?非常感谢!
编辑:
class ViewController: UIViewController {
@IBOutlet var knobPlaceholder: UIView!
@IBOutlet var valueLabel: UILabel!
@IBOutlet var valueSlider: UISlider!
@IBOutlet var animateSwitch: UISwitch!
var knob: Knob!
override func viewDidLoad() {
super.viewDidLoad()
knob = Knob(frame: knobPlaceholder.bounds)
knobPlaceholder.addSubview(knob)
knobPlaceholder.backgroundColor = UIColor.redColor();
}
@IBAction func sliderValueChanged(slider: UISlider) {
}
@IBAction func randomButtonTouched(button: UIButton) {
}
}
旋钮:
public class Knob: UIControl {
private let knobRenderer = KnobRenderer()
private var backingValue: Float = 0.0
/** Contains the receiver’s current value. */
public var value: Float {
get {
return backingValue
}
set {
setValue(newValue, animated: false)
}
}
/** Sets the receiver’s current value, allowing you to animate the change visually. */
public func setValue(value: Float, animated: Bool) {
if value != backingValue {
backingValue = min(maximumValue, max(minimumValue, value))
}
}
/** Contains the minimum value of the receiver. */
public var minimumValue: Float = 0.0
/** Contains the maximum value of the receiver. */
public var maximumValue: Float = 1.0
public override init(frame: CGRect) {
super.init(frame: frame)
createSublayers()
self.bounds.size.width = 40
self.bounds.size.height = 40
println("frame: \(self.frame)")
println("bounds: \(self.bounds)")
self.clipsToBounds = true
}
public required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createSublayers() {
knobRenderer.update(bounds)
knobRenderer.strokeColor = tintColor
knobRenderer.startAngle = -CGFloat(M_PI * 11.0 / 8.0);
knobRenderer.endAngle = CGFloat(M_PI * 3.0 / 8.0);
knobRenderer.pointerAngle = knobRenderer.startAngle;
knobRenderer.lineWidth = 2.0
knobRenderer.pointerLength = 6.0
layer.addSublayer(knobRenderer.trackLayer)
layer.addSublayer(knobRenderer.pointerLayer)
}
}
private class KnobRenderer {
let trackLayer = CAShapeLayer()
let pointerLayer = CAShapeLayer()
var strokeColor: UIColor {
get {
return UIColor(CGColor: trackLayer.strokeColor)
}
set(strokeColor) {
trackLayer.strokeColor = strokeColor.CGColor
pointerLayer.strokeColor = strokeColor.CGColor
}
}
var lineWidth: CGFloat = 1.0 {
didSet {
update();
}
}
var startAngle: CGFloat = 0.0 {
didSet {
update();
}
}
var endAngle: CGFloat = 0.0 {
didSet {
update()
}
}
var backingPointerAngle: CGFloat = 0.0
var pointerAngle: CGFloat {
get { return backingPointerAngle }
set { setPointerAngle(newValue, animated: false) }
}
func setPointerAngle(pointerAngle: CGFloat, animated: Bool) {
backingPointerAngle = pointerAngle
}
var pointerLength: CGFloat = 0.0 {
didSet {
update()
}
}
init() {
trackLayer.fillColor = UIColor.clearColor().CGColor
pointerLayer.fillColor = UIColor.clearColor().CGColor
}
func updateTrackLayerPath() {
let arcCenter = CGPoint(x: trackLayer.bounds.width / 2.0, y: trackLayer.bounds.height / 2.0)
let offset = max(pointerLength, trackLayer.lineWidth / 2.0)
let radius = min(trackLayer.bounds.height, trackLayer.bounds.width) / 2.0 - offset;
trackLayer.path = UIBezierPath(arcCenter: arcCenter, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true).CGPath
}
func updatePointerLayerPath() {
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: pointerLayer.bounds.width - pointerLength - pointerLayer.lineWidth / 2.0, y: pointerLayer.bounds.height / 2.0))
path.addLineToPoint(CGPoint(x: pointerLayer.bounds.width, y: pointerLayer.bounds.height / 2.0))
pointerLayer.path = path.CGPath
}
func update(bounds: CGRect) {
let position = CGPoint(x: bounds.width / 2.0, y: bounds.height / 2.0)
trackLayer.bounds = bounds
trackLayer.position = position
pointerLayer.bounds = bounds
pointerLayer.position = position
update()
}
func update() {
trackLayer.lineWidth = lineWidth
pointerLayer.lineWidth = lineWidth
updateTrackLayerPath()
updatePointerLayerPath()
}
}
答案 0 :(得分:0)
在您的更新功能中,您将视图居中。
func update(bounds: CGRect) {
**let position = CGPoint(x: bounds.width / 2.0, y: bounds.height / 2.0)**
trackLayer.bounds = bounds
trackLayer.position = position
pointerLayer.bounds = bounds
pointerLayer.position = position
update()
}
如果你把它拿出来,那么你的视野将不再是中心。设置框架的原因是将其留在左上角,而设置边界导致它位于中心是因为设置边界x / y不会覆盖框架x / y。当您设置框架时,稍后您的代码仅设置边界,因此框架x / y永远不会被覆盖,因此视图保留在左上角。但是,在第二个框架中没有设置x / y,所以我猜它是为你设置的边界,所以它是居中的。
我建议不要为视图设置边界x / y,因为它应该始终为0,0。如果要重新定位它,请使用框架。请记住,框架是相对于父级的,而边界是相对于它的自身。