更改子视图的边界将其居中在其父视图中

时间:2015-01-20 20:20:39

标签: ios objective-c swift layout uiview

我一直在学习解释自定义ios控件的教程。这个教程没有问题,但我对框架/边界剪辑感到困惑(不是教程的一部分)。

我在故事板中的场景中有一个UIView实例。这个UIView的大小为120 x 120.我正在使用addSubview将自定义控件(扩展UIControl)添加到此容器视图中。我开始尝试为自定义控件的框架和边界设置不同的宽度和高度,这是控件的初始化器:

 public override init(frame: CGRect) {
     super.init(frame: frame)
     createSublayers()
 }

...并生成此结果(红色是父级,蓝色圈子是孩子):

full circle

现在我将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
}

这就产生了这个结果:

frame 和打印: 框架:(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
}

我明白了:

bounds 和打印:框架:(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()
    }
}

1 个答案:

答案 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。如果要重新定位它,请使用框架。请记住,框架是相对于父级的,而边界是相对于它的自身。