在Swift iOS中制作3个点的动画

uxhixvfz  于 12个月前  发布在  iOS
关注(0)|答案(2)|浏览(192)

我有一个标签和一个图像视图。我的目标是有3个点动画前面的标签,并在底部的标签在年底,如图所示

我已经能够使这个设计与点移动罚款12promax只不过我不能弄清楚如何使这个工作总是在不同的手机屏幕尺寸的预期.我如何编码,所以无论屏幕大小或uilabel字体大小它会达到相同的结果?

Animate ImageView(3点)

func showAnimatingDotsInImageView(dots: UIImageView)
 {
        let newX = view.bounds.width / 896 * 20
        let lay = CAReplicatorLayer()
        lay.frame = CGRect(x: newX,y: 0,width: dots.bounds.width,height: dots.bounds.height)
        let bar = CALayer()
        bar.frame = CGRect(x: 0,y: (dots.bounds.height/2) + 8 ,width: 8,height: 8)  //make the objs smaller or bigger
        bar.cornerRadius = bar.frame.width / 2  //make a circle, if you uncomment this you will get rects
        bar.backgroundColor = UIColor.black.cgColor   //colour of the objs
        lay.addSublayer(bar)
        lay.instanceCount = 3   //How many instances / objs do you want to see
        lay.instanceTransform = CATransform3DMakeTranslation(15, 0, 0) //1st arg is the spacing between the instances
        let anim = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
        anim.fromValue = 1.0
        anim.toValue = 0.2
        anim.duration = 1
        anim.repeatCount = .infinity
        bar.add(anim, forKey: nil)
        lay.instanceDelay = anim.duration / Double(lay.instanceCount)
    
        dots.layer.addSublayer(lay)    // add to the view

        }

字符串
“从主菜单中检索框”是一个UILabel,点在UIImageView上动画显示

lawou6xi

lawou6xi1#

你已经很接近了......只要做一些改变就能让你达到你想要的目的。
如果你有一个UILabel和一个UIImageView(或普通的UIView)限制在标签的右边缘,你应该能够定位你的CAReplicatorLayer,而不必担心“屏幕大小”。
看看我是如何修改你的代码的:

// baseline = to put the bottom of the dots at the baseline of the text in the label
// dotXOffset = gap between end of label and first dot
// dotSize = dot width and height
// dotSpacing = gap between dots
func showAnimatingDotsInImageView(dotsView: UIView, baseline: CGFloat, dotXOffset: CGFloat, dotSize: CGFloat, dotSpacing: CGFloat) {
    let lay = CAReplicatorLayer()
    let bar = CALayer()
    bar.frame = CGRect(x: dotXOffset, y: baseline - dotSize, width: dotSize, height: dotSize)
    bar.cornerRadius = bar.frame.width / 2  // we want round dots
    bar.backgroundColor = UIColor.black.cgColor
    lay.addSublayer(bar)
    lay.instanceCount = 3   //How many instances / objs do you want to see
    lay.instanceTransform = CATransform3DMakeTranslation(dotSpacing, 0, 0) //1st arg is the spacing between the instances
    let anim = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
    anim.fromValue = 1.0
    anim.toValue = 0.2
    anim.duration = 1
    anim.repeatCount = .infinity
    bar.add(anim, forKey: nil)
    lay.instanceDelay = anim.duration / Double(lay.instanceCount)
    dotsView.layer.addSublayer(lay)    // add to the view
}

字符串
下面是一个完整的例子:

class SimpleViewController: UIViewController {
    
    let testLabel = UILabel()
    let testDotsView = UIView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        testLabel.font = .systemFont(ofSize: 24.0)
        
        testLabel.text = "Retrieving boxes"
        
        // so we can see the label frame
        testLabel.backgroundColor = .cyan
        
        testLabel.translatesAutoresizingMaskIntoConstraints = false
        testDotsView.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(testLabel)
        view.addSubview(testDotsView)
        
        // always respect safe area
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // let's constrain the label
            //  40-pts from Leading
            //  40-pts from Bottom
            testLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            testLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0),
            
            // constrain dots view to
            //  Top of label
            //  Trailing of label
            testDotsView.topAnchor.constraint(equalTo: testLabel.topAnchor),
            testDotsView.leadingAnchor.constraint(equalTo: testLabel.trailingAnchor, constant: 0.0),
            // dots image view Width and Height can be 0 (we can draw the layer outside the bounds)
            testDotsView.heightAnchor.constraint(equalToConstant: 0.0),
            testDotsView.widthAnchor.constraint(equalToConstant: 0.0),
            
        ])
        
        // get the label font's baseline y-value
        let bl: CGFloat = testLabel.font.ascender
        showAnimatingDotsInImageView(dotsView: testDotsView, baseline: bl, dotXOffset: 4.0, dotSize: 4.0, dotSpacing: 8.0)
    }
    
    // baseline = to put the bottom of the dots at the baseline of the text in the label
    // dotXOffset = gap between end of label and first dot
    // dotSize = dot width and height
    // dotSpacing = gap between dots
    func showAnimatingDotsInImageView(dotsView: UIView, baseline: CGFloat, dotXOffset: CGFloat, dotSize: CGFloat, dotSpacing: CGFloat) {
        let lay = CAReplicatorLayer()
        let bar = CALayer()
        bar.frame = CGRect(x: dotXOffset, y: baseline - dotSize, width: dotSize, height: dotSize)
        bar.cornerRadius = bar.frame.width / 2  // we want round dots
        bar.backgroundColor = UIColor.black.cgColor
        lay.addSublayer(bar)
        lay.instanceCount = 3   //How many instances / objs do you want to see
        lay.instanceTransform = CATransform3DMakeTranslation(dotSpacing, 0, 0) //1st arg is the spacing between the instances
        let anim = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
        anim.fromValue = 1.0
        anim.toValue = 0.2
        anim.duration = 1
        anim.repeatCount = .infinity
        bar.add(anim, forKey: nil)
        lay.instanceDelay = anim.duration / Double(lay.instanceCount)
        dotsView.layer.addSublayer(lay)    // add to the view
    }

}

编辑-在回应“克里山库马尔”评论.

要让动画在从后台返回时继续,您需要添加通知观察器。
对于“动画点”视图,使用自定义的UIView子类要容易得多,所以这里有一个简单的例子:

class DotsView: UIView {
    
    // baseline = to put the bottom of the dots at the baseline of the text in the label
    // dotXOffset = gap between end of label and first dot
    // dotSize = dot width and height
    // dotSpacing = gap between dots

    public var baseline: CGFloat = 0
    public var dotXOffset: CGFloat = 4.0
    public var dotSize: CGFloat = 4.0
    public var dotSpacing: CGFloat = 8.0

    private let lay = CAReplicatorLayer()
    private let bar = CALayer()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        lay.addSublayer(bar)
        layer.addSublayer(lay)
    }
    public func beginAnimating() {
        bar.frame = CGRect(x: dotXOffset, y: baseline - dotSize, width: dotSize, height: dotSize)
        // we want round dots
        bar.cornerRadius = bar.frame.width / 2.0
        bar.backgroundColor = UIColor.black.cgColor
        //How many instances / objs we want to see
        lay.instanceCount = 3
        //1st arg is the spacing between the instances
        lay.instanceTransform = CATransform3DMakeTranslation(dotSpacing, 0, 0)
        let anim = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
        anim.fromValue = 1.0
        anim.toValue = 0.2
        anim.duration = 1
        anim.repeatCount = .infinity
        bar.add(anim, forKey: nil)
        // so the dots animate in sequence
        lay.instanceDelay = anim.duration / Double(lay.instanceCount)
    }
    public func stopAnimating() {
        layer.removeAllAnimations()
    }
}


以及显示如何使用它的示例控制器,包括当应用程序在前台和后台之间移动时启动/停止动画:

class SimpleViewController: UIViewController {
    
    let testLabel = UILabel()
    
    // custom DotsView
    let testDotsView = DotsView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemBackground
        
        testLabel.font = .systemFont(ofSize: 24.0)
        
        testLabel.text = "Retrieving boxes"
        
        // so we can see the label frame
        testLabel.backgroundColor = .cyan
        
        testLabel.translatesAutoresizingMaskIntoConstraints = false
        testDotsView.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(testLabel)
        view.addSubview(testDotsView)
        
        // always respect safe area
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // let's constrain the label
            //  40-pts from Leading
            //  40-pts from Bottom
            testLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            testLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0),
            
            // constrain dots view to
            //  Top of label
            //  Trailing of label
            testDotsView.topAnchor.constraint(equalTo: testLabel.topAnchor),
            testDotsView.leadingAnchor.constraint(equalTo: testLabel.trailingAnchor, constant: 0.0),
            // dots image view Width and Height can be 0 (we can draw the layer outside the bounds)
            testDotsView.heightAnchor.constraint(equalToConstant: 0.0),
            testDotsView.widthAnchor.constraint(equalToConstant: 0.0),
            
        ])
        
        // get the label font's baseline y-value
        testDotsView.baseline = testLabel.font.ascender
        
        // use defaults or set values here
        //testDotsView.dotXOffset = 4.0
        //testDotsView.dotSize = 4.0
        //testDotsView.dotSpacing = 8.0
        
        testDotsView.beginAnimating()

        // we want to
        //  Stop the Dots animation when the app goes into the Background, and
        //  Start the Dots animation when the app Enters the Foreground
        NotificationCenter.default.addObserver(self, selector: #selector(myEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(myEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
    }
    
    @objc func myEnterBackground() {
        testDotsView.stopAnimating()
    }
    @objc func myEnterForeground() {
        testDotsView.beginAnimating()
    }
    
}

w51jfk4q

w51jfk4q2#

//3点动画完整代码
import Foundation导入UIKit
public class FIOProgressView {

var containerView = UIView()
var progressView = UIView()
var dotsContainerView = UIView()

open class var shared: FIOProgressView {
    struct Static {
        static let instance: FIOProgressView = FIOProgressView()
    }
    return Static.instance
}

open func showProgressView(_ view: UIView) {
    
    containerView.frame = view.frame
    containerView.center = view.center
    containerView.backgroundColor = UIColor(hex: 0xffffff, alpha: 0.3)
    
    progressView.frame = CGRect(x: 0, y: 0, width: 120, height: 40)
    progressView.center = view.center
    progressView.backgroundColor = .clear
    progressView.clipsToBounds = true
    progressView.layer.cornerRadius = 10
    
    dotsContainerView.frame = CGRect(x: 0, y: 0, width: 120, height: 40)
    dotsContainerView.center = CGPoint(x: progressView.bounds.width / 2, y: progressView.bounds.height / 2)
    
    createDots()
    
    progressView.addSubview(dotsContainerView)
    containerView.addSubview(progressView)
    view.addSubview(containerView)
}

private func createDots() {
    let dotSize: CGFloat = 20
    let spacing: CGFloat = 20
    
    for i in 0..<3 {
        let dot = UIView(frame: CGRect(x: CGFloat(i) * (dotSize + spacing), y: 10, width: dotSize, height: dotSize))
        dot.backgroundColor = UIColor(displayP3Red: 92/255, green: 46/255, blue: 126/255, alpha: 1.0)
        dot.layer.cornerRadius = dotSize / 2
        
        dotsContainerView.addSubview(dot)
        animateDot(dot, delay: TimeInterval(i) * 0.3)
    }
}

private func animateDot(_ dot: UIView, delay: TimeInterval) {
      UIView.animate(withDuration: 0.5, delay: delay, options: [.autoreverse, .repeat], animations: {
          dot.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
      }, completion: nil)
  }

open func hideProgressView() {
    DispatchQueue.main.async {
        self.containerView.removeFromSuperview()
    }
}

字符串
}

相关问题