如何在 UITableViewCell 中为 UIViews 添加引力?

How to add gravity to UIViews in a UITableViewCell?

我正在尝试通过让重力作用于它们来为一些 UIView 设置动画。理想情况下,每个视图都将停留在 UITableViewCell 边界内,并且它们会相互反弹,并像边界一样反弹。但是,当我 运行 这段代码时,UIView 只是在原地浮动,只有在另一个视图恰好填充在同一位置时才会移动。

class AnimateTableViewCell: UITableViewCell {
    
    private var animator: UIDynamicAnimator!
    private var gravity: UIGravityBehavior!
    private var collision: UICollisionBehavior!
    
    private var views = [UIView]()
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        animator = UIDynamicAnimator(referenceView: self.contentView)
        let items = [UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill")]
        
        for item in items {
            let size = Int.random(in: 75 ... 180)
            self.setUpView(view: UIView(frame: CGRect(x: 100, y: 100, width: size, height: size)), image: item!, size: size/2)
        }

    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func applyGravity(to item: UIView) {
        gravity = UIGravityBehavior(items: [item])
        animator.addBehavior(gravity)
        
        collision = UICollisionBehavior(items: views)
        collision.translatesReferenceBoundsIntoBoundary = true
        animator.addBehavior(collision)
        
        let bounce = UIDynamicItemBehavior(items: views)
        bounce.elasticity = 0.3
    }
    
    private func setUpView(view: UIView, image: UIImage, size: Int) {
        contentView.addSubview(view)
        view.backgroundColor = image.averageColor
        view.layer.cornerRadius = view.frame.height/2
        
        let imageView = UIImageView(image: image)
        imageView.contentMode = .scaleAspectFit
        
        view.addSubview(imageView)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            imageView.heightAnchor.constraint(equalToConstant: CGFloat(size)),
            imageView.widthAnchor.constraint(equalToConstant: CGFloat(size)),
            imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        ])
        views.append(view)
        applyGravity(to: view)
    }
}

主要有两个问题。

首先,碰撞行为妨碍了您。注释掉这一行:

 animator.addBehavior(collision)

现在至少你会看到重力在起作用。

其次,你说的是

    gravity = UIGravityBehavior(items: [item])
    animator.addBehavior(gravity)

多次,每个项目一次。那是错误的。您必须只有 一个 包含所有项目的重力行为。

我只是重新安排了一些代码并更改了一些幻数,得到了一个中等偏上的结果;至少这应该让你开始(我使用了一个只有一个单元格的 table 视图,高度相当高):

class AnimateTableViewCell: UITableViewCell {
    
    private var animator: UIDynamicAnimator!
    private var gravity = UIGravityBehavior()
    private var collision = UICollisionBehavior()
    
    private var views = [UIView]()
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        animator = UIDynamicAnimator(referenceView: self.contentView)
        let items = [UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill"),UIImage(systemName: "star.fill")]
        for item in items {
            let size = Int.random(in: 20...40)
            self.setUpView(view: UIView(frame: CGRect(x: 100, y: 100, width: size, height: size)), image: item!, size: size)
        }
        animator.addBehavior(gravity)
        collision.translatesReferenceBoundsIntoBoundary = true
        animator.addBehavior(collision)
        let bounce = UIDynamicItemBehavior(items: views)
        bounce.elasticity = 0.3
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func applyGravity(to item: UIView) {
        gravity.addItem(item)
        collision.addItem(item)
    }
    
    private func setUpView(view: UIView, image: UIImage, size: Int) {
        contentView.addSubview(view)
        view.backgroundColor = .red
        view.layer.cornerRadius = view.frame.height/2
        
        let imageView = UIImageView(image: image)
        imageView.contentMode = .scaleAspectFit
        
        view.addSubview(imageView)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            imageView.heightAnchor.constraint(equalToConstant: CGFloat(size)),
            imageView.widthAnchor.constraint(equalToConstant: CGFloat(size)),
            imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        ])
        views.append(view)
        applyGravity(to: view)
    }
}