本案例设计思路没什么难的地方,只要掌握了CAShapeLayer +
在vc中测试方式:
UIBezierPath
效果图如下:
与基本动画 的使用,设计出来完全没问题,直接奉送代码:
import UIKit /// wegt结构体来控制提示文本的信息 struct TextTipsInfo { var position : CGRect var color : UIColor var font : UIFont var text : String var fontSize : CGFloat } /// 类似安卓Toast对象 class ToastView: UIView { lazy var textLayer : (TextTipsInfo) ->VerticalTextLayer = {(textInfo : TextTipsInfo) -> VerticalTextLayer in let tmpLayer : VerticalTextLayer = VerticalTextLayer() tmpLayer.frame = CGRect.init(x: textInfo.position.origin.x, y: textInfo.position.origin.y, width: textInfo.position.size.width, height: textInfo.position.size.height) tmpLayer.foregroundColor = textInfo.color.cgColor; tmpLayer.alignmentMode = kCAAlignmentCenter; tmpLayer.isWrapped = true; tmpLayer.font = textInfo.font tmpLayer.fontSize = textInfo.fontSize tmpLayer.string = textInfo.text tmpLayer.contentsScale = UIScreen.main.scale; return tmpLayer } lazy var circleLayer : CAShapeLayer = CAShapeLayer() lazy var successLayer : CAShapeLayer = CAShapeLayer() lazy var failLayer : CAShapeLayer = CAShapeLayer() lazy var centerPos : CGPoint = {[unowned self]() -> CGPoint in return CGPoint.init(x: self.frame.width / 2, y: self.frame.height / 2 - 10) }() lazy var animate : CABasicAnimation = {() -> CABasicAnimation in let anim : CABasicAnimation = CABasicAnimation.init(keyPath: "strokeEnd") anim.duration = 0.7 anim.fromValue = 0.0 anim.toValue = 1.0 anim.fillMode = kCAFillModeForwards anim.isRemovedOnCompletion = false anim.delegate = self as CAAnimationDelegate anim.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionLinear) return anim }() lazy var bIsSuccess : Bool = false //当使用动画模式时,是否播放成功动画 var bIsPureTypeModel : Bool = false //是否为纯文本模式 override init(frame: CGRect) { super.init(frame: frame) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func initAnimLayer(success : Bool) { setCircleAttr(radius: 25, color: UIColor.green, strokWidth: 2.0) guard success else { setFailLayer(color: UIColor.green, strokWidth: 2.0) return } setSuccessLayer(color: UIColor.green, strokWidth: 2.0) } func setCircleAttr(radius : CGFloat,color: UIColor,strokWidth : CGFloat){ let path : UIBezierPath = UIBezierPath.init() path.addArc(withCenter: centerPos, radius: radius, startAngle: 0, endAngle: 3.1415926 * 2, clockwise: true) circleLayer.lineWidth = strokWidth circleLayer.path = path.cgPath circleLayer.fillColor = UIColor.clear.cgColor circleLayer.strokeColor = UIColor.green.cgColor self.layer.addSublayer(circleLayer) } func setSuccessLayer(color : UIColor, strokWidth : CGFloat) { let successPath : UIBezierPath = UIBezierPath.init() successPath.move(to: CGPoint.init(x: centerPos.x - 15, y: centerPos.y - 10)) //线的表达式为: y = k *x + 5 其中 k = -1 successPath.addLine(to: CGPoint.init(x: centerPos.x - 2, y: centerPos.y + 3)) successPath.move(to: CGPoint.init(x: centerPos.x - 2, y: centerPos.y + 3)) //线的表达式为: y = -k * x 其中 k = -1 successPath.addLine(to: CGPoint.init(x: centerPos.x + 15, y: centerPos.y - 15)) successLayer.lineWidth = strokWidth successLayer.path = successPath.cgPath successLayer.fillColor = UIColor.clear.cgColor successLayer.strokeColor = color.cgColor successLayer.strokeEnd = 0.0 self.layer.addSublayer(successLayer) } func setFailLayer(color : UIColor, strokWidth : CGFloat) { let failPath : UIBezierPath = UIBezierPath.init() failPath.move(to: CGPoint.init(x: centerPos.x - 12.5, y: centerPos.y - 12.5)) //线的表达式为: y = k *x + failPath.addLine(to: CGPoint.init(x: centerPos.x + 12.5, y: centerPos.y + 12.5)) //每一条线的斜率 = 1 failPath.move(to: CGPoint.init(x: centerPos.x - 12.5, y: centerPos.y + 12.5)) //线的表达式为: y = -k * x 其中 k = -1 failPath.addLine(to: CGPoint.init(x: centerPos.x + 12.5, y: centerPos.y - 12.5)) failLayer.lineWidth = strokWidth failLayer.path = failPath.cgPath failLayer.fillColor = UIColor.clear.cgColor failLayer.strokeColor = color.cgColor failLayer.strokeEnd = 0.0 self.layer.addSublayer(failLayer) } func setTextLayer(textInfo : TextTipsInfo){ self.layer.addSublayer(textLayer(textInfo)) } func toast(success : Bool,withTitle : String) { self.bIsSuccess = success initAnimLayer(success:success) circleLayer.add(animate, forKey: "circleAnim") setTextLayer(textInfo: TextTipsInfo.init(position: CGRect.init(x: centerPos.x - self.frame.width / 2, y: self.frame.height - 30, width: self.frame.width, height: 30), color: UIColor.white, font: UIFont.systemFont(ofSize: 2), text: withTitle, fontSize: 14)) } /// 只显示文本模式 /// /// - Parameters: /// - text: 文本内容 /// - color: 字体颜色 /// - font: 字体信息 /// - size: 画笔大小 func toastOnly(text : String) { setTextLayer(textInfo: TextTipsInfo.init(position: CGRect.init(x: centerPos.x - self.frame.width / 2, y: centerPos.y - 40 / 2, width: self.frame.width, height: 40), color: UIColor.white, font: UIFont.systemFont(ofSize: 2), text: text, fontSize: 14)) recycRes() } func recycRes() { let time: TimeInterval = 1 DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + time) {[unowned self] in UIView.animate(withDuration: 0.5, animations: { self.alpha = 0 }, completion: {(true) -> Void in self.removeFromSuperview() }) } } } extension ToastView : CAAnimationDelegate{ func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { if(anim == circleLayer.animation(forKey: "circleAnim")){ guard self.bIsSuccess else { failLayer.add(animate, forKey: "failAnim") return } successLayer.add(animate, forKey: "successAnim") }else if(anim == successLayer.animation(forKey: "successAnim") || anim == failLayer.animation(forKey: "failAnim")){ recycRes() } } } /// 本类使用CATextLayer文本垂直居中 class VerticalTextLayer : CATextLayer{ override func draw(in ctx: CGContext) { let fontSize = self.fontSize let height = self.bounds.size.height let deltaY = (height-fontSize)/2 - fontSize/6 ctx.saveGState() ctx.translateBy(x: 0.0, y: deltaY) super.draw(in: ctx) ctx.restoreGState() } }
在vc中测试方式:
override func viewDidLoad() { initView() let btnSuccessToast : UIButton = UIButton.init(frame: CGRect.init(x: 10, y: 400, width: 80, height: 40)) btnSuccessToast.backgroundColor = UIColor.gray btnSuccessToast.tag = 41 btnSuccessToast.setTitle("加载成功", for: UIControlState.normal) btnSuccessToast.addTarget(self, action: #selector(onClick), for: .touchUpInside) self.view.addSubview(btnSuccessToast) let btnFailToast : UIButton = UIButton.init(frame: CGRect.init(x: 100, y: 400, width: 80, height: 40)) btnFailToast.backgroundColor = UIColor.gray btnFailToast.tag = 42 btnFailToast.setTitle("加载失败", for: UIControlState.normal) btnFailToast.addTarget(self, action: #selector(onClick), for: .touchUpInside) self.view.addSubview(btnFailToast) let btnTextToast : UIButton = UIButton.init(frame: CGRect.init(x: 200, y: 400, width: 80, height: 40)) btnTextToast.backgroundColor = UIColor.gray btnTextToast.tag = 43 btnTextToast.setTitle("文本模式", for: UIControlState.normal) btnTextToast.addTarget(self, action: #selector(onClick), for: .touchUpInside) self.view.addSubview(btnTextToast) let btncd : UIButton = UIButton.init(frame: CGRect.init(x: 290, y: 400, width: 80, height: 40)) btncd.backgroundColor = UIColor.gray btncd.tag = 44 btncd.setTitle("倒计时动画", for: UIControlState.normal) btncd.addTarget(self, action: #selector(onClick), for: .touchUpInside) self.view.addSubview(btncd) } func onClick(button: UIButton){ switch button.tag { case 41: //初始toast let toast : ToastView = ToastView.init(frame: CGRect.init(x: getScreenSize().width / 2 - 50, y: getScreenSize().height / 2 - 50, width: 100, height: 100)) toast.backgroundColor = UIColor.gray toast.toast(success: true,withTitle: "加载成功") self.view.addSubview(toast) break case 42: //初始toast let toast : ToastView = ToastView.init(frame: CGRect.init(x: getScreenSize().width / 2 - 50, y: getScreenSize().height / 2 - 50, width: 100, height: 100)) toast.backgroundColor = UIColor.gray toast.toast(success: false,withTitle: "加载失败") self.view.addSubview(toast) break; case 43 : //初始toast let toast : ToastView = ToastView.init(frame: CGRect.init(x: getScreenSize().width / 2 - 150, y: getScreenSize().height / 2 - 150, width: 300, height: 300)) toast.backgroundColor = UIColor.gray toast.toastOnly(text: "加载成功") self.view.addSubview(toast) break; case 44: let countDowm : CountDownView = CountDownView.init(frame: CGRect.init(x: self.getScreenSize().width / 2 - 100, y: self.getScreenSize().height / 2 - 100, width: 100, height: 100)) countDowm.setCircleAttr(radius: 25, color: UIColor.green, strokWidth: 2.0) countDowm.setTextAttr(initText: "10", color: UIColor.black, font: UIFont.systemFont(ofSize: 10), size: CGSize.init(width: 50, height: 50)) countDowm.countDownListener = self countDowm.startCountDown(duration: 10) countDowm.backgroundColor = UIColor.gray self.view.addSubview(countDowm) break; default: break; } }