将子视图放置在圆形视图的边缘

时间:2019-07-15 10:35:07

标签: ios swift uiview autolayout position

我正在尝试创建类似下面的模型的个人资料图片视图。它带有一个小绿点,表示用户的在线状态。

enter image description here

我正在以编程方式创建视图,因此可以重用它。下面是到目前为止的代码。

import UIKit

@IBDesignable
class ProfileView: UIView {

    fileprivate var imageView: UIImageView!
    fileprivate var onlineStatusView: UIView!
    fileprivate var onlineStatusDotView: UIView!


    @IBInspectable
    var image: UIImage? {
        get { return imageView.image }
        set { imageView.image = newValue }
    }

    @IBInspectable
    var shouldShowStatusDot: Bool = true


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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initialize()
    }

    private func initialize() {
        backgroundColor = .clear

        imageView = UIImageView(frame: bounds)
        imageView.backgroundColor = .lightGray
        imageView.clipsToBounds = true
        imageView.layer.cornerRadius = imageView.frame.height / 2
        addSubview(imageView)

        onlineStatusView = UIView(frame: CGRect(x: 0, y: 0, width: (bounds.height / 5), height: (bounds.height / 5)))
        onlineStatusView.backgroundColor = .white
        onlineStatusView.clipsToBounds = true
        onlineStatusView.layer.cornerRadius = onlineStatusView.frame.height / 2
        addSubview(onlineStatusView)

        onlineStatusDotView = UIView(frame: CGRect(x: 0, y: 0, width: (onlineStatusView.bounds.height / 1.3), height: (onlineStatusView.bounds.height / 1.3)))
        onlineStatusDotView.center = onlineStatusView.center
        onlineStatusDotView.backgroundColor = UIColor(red: 0.17, green: 0.71, blue: 0.45, alpha: 1.0)
        onlineStatusDotView.clipsToBounds = true
        onlineStatusDotView.layer.cornerRadius = onlineStatusDotView.frame.height / 2
        onlineStatusView.addSubview(onlineStatusDotView)
    }
}

enter image description here

让我迷失的是如何将绿点视图固定在图像视图右上角的圆形边缘上。显然,视图的框架不是圆形的,因此我无法弄清楚在这种情况下要使用的自动布局约束。而且我也不想对值进行硬编码,因为它必须根据图像视图的大小移动。

我必须设置哪些自动布局约束才能将其放置在正确的位置?

我也在这里上传了demo project

2 个答案:

答案 0 :(得分:1)

使用以下命令更改初始化函数: 您可以在给定的图像链接中看到结果...

  private func initialize() {
    backgroundColor = .clear

    imageView = UIImageView(frame: bounds)
    imageView.backgroundColor = .lightGray
    imageView.clipsToBounds = true
    imageView.layer.cornerRadius = imageView.frame.height / 2
    addSubview(imageView)

    onlineStatusView = UIView(frame: CGRect(x: 0, y: 0, width: (bounds.height / 5), height: (bounds.height / 5)))
    onlineStatusView.center = CGPoint(x: bounds.width / 7, y: bounds.height / 7)
    onlineStatusView.backgroundColor = .white
    onlineStatusView.clipsToBounds = true
    onlineStatusView.layer.cornerRadius = onlineStatusView.frame.height / 2
    addSubview(onlineStatusView)

    onlineStatusDotView = UIView(frame: CGRect(x: 0, y: 0, width: (onlineStatusView.bounds.height / 1.3), height: (onlineStatusView.bounds.height / 1.3)))
      onlineStatusDotView.center = CGPoint(x: onlineStatusView.frame.width / 2, y: onlineStatusView.frame.height / 2)
    onlineStatusDotView.backgroundColor = UIColor(red: 0.17, green: 0.71, blue: 0.45, alpha: 1.0)
    onlineStatusDotView.clipsToBounds = true
    onlineStatusDotView.layer.cornerRadius = onlineStatusDotView.frame.height / 2
    onlineStatusView.addSubview(onlineStatusDotView)
}

答案 1 :(得分:1)

将小绿色圆圈放在大圆圈的右上角:

  1. 使小圆圈成为大圆圈的子视图。
  2. 添加一个约束,使小圆的.centerX等于大圆的.trailingmultiplier0.8536
  3. 添加一个约束,使小圆的.centerY等于大圆的.bottommultiplier0.1464

注意:使用三角学通过观察单位圆并计算比率multiplier和{{ 1}}。在下面的示例代码中,我提供了一个名为(distance from top of square containing unit circle)/(height of unit circle)的{​​{1}},它可以计算任意(distance from left edge of square containing unit circle)/(width of unit circle)的度数。避免精确地funccomputeMultipliers(angle:)的角度,因为这会产生自动版式不喜欢的angle乘数。


这是一个独立的示例:

90

image of sample code running in the simulator


这是您代码的修改版本。我添加了一些约束来设置小圆圈的大小,并移动了将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)
    }
}

second image with white border