软件环境:Xcode 13.2
创建时间:2022年 03月7号
适用范围:iOS项目
这篇文章都说了什么
快速度搭建提示信息,不用依赖复杂的三方
效果图
使用实例
// toast
HToast.show("不能为空")
// toast消失前禁止交互
HToast.show("请稍后", userEnable: false)
实现思路
在window上添加一个view,展示toast信息并自动延迟隐藏。
优点:不需要绑定view,方便在任何地方调用。
缺点:不适合多toast同时展示的需求(和elementUI、Android原生toast略有不同)
源码如下:
import UIKit
public class HToast: UIView {
/// 展示toastView
/// 默认允许用交互,展示时间2.0s
@objc class public func show(_ title: String, userEnable: Bool = true) {
UIApplication.shared.keyWindow?.statusView.isHidden = true
let toastView = UIApplication.shared.keyWindow?.toastView
toastView?.isUserInteractionEnabled = !userEnable
// 前置
UIApplication.shared.keyWindow?.bringSubviewToFront(toastView!)
toastView?.show_in(title)
}
/// 展示toastView,允许用交互,展示时间2.50s
@objc class public func show(_ title: String) {
self.show(title, userEnable: true)
}
/// 手动强制隐藏
@objc class public func hide() {
let toastView = UIApplication.shared.keyWindow?.toastView
toastView?.isHidden = true
toastView?.alpha = 1
}
public override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.black.withAlphaComponent(0.05)
self.isUserInteractionEnabled = false
addSubview(backView)
backView.addSubview(titleLabel)
backView.snp.makeConstraints { (make) in
make.center.equalToSuperview()
}
titleLabel.snp.makeConstraints { (make) in
make.top.equalTo(17)
make.bottom.equalTo(-17)
make.left.greaterThanOrEqualTo(20)
make.right.greaterThanOrEqualTo(-20)
make.width.lessThanOrEqualTo(kSCWIDTH - 90)
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: private
// 记录自动隐藏的时间
private var hideTime = ""
@objc private func show_in(_ title: String) {
let showTime = NSDate.init().timeIntervalSince1970
self.isHidden = false
titleLabel.text = title
hideToastView(showTime)
}
/// 延迟隐藏toast,如果中间有其他toast刷新了隐藏时间,则以最后一次时间为准
@objc private func hideToastView(_ interval: TimeInterval) {
hideTime = "\(interval)"
DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
if (self.hideTime == ("\(interval)")) {
UIView.animate(withDuration: 1, animations: {
self.alpha = 0.01
}, completion: { (finish) in
if finish {
self.isHidden = true
self.alpha = 1
}
})
}
}
}
// MARK: views
private lazy var backView: UIView = {
let backView = UIView.init()
backView.backgroundColor = UIColor.black.withAlphaComponent(0.6)
backView.clipsToBounds = true
backView.layer.cornerRadius = 8
return backView
}()
private lazy var titleLabel: UILabel = {
let lable = UILabel.init()
lable.textAlignment = .center
lable.font = mediumFont(16)
lable.textColor = .white
lable.numberOfLines = 3
return lable
}()
}
extension UIWindow {
@objc var toastView :HToast {
get {
if let view = self.viewWithTag(123411980) {
return view as! HToast
} else {
let view = HToast()
view.tag = 123411980
self.addSubview(view);
view.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
return view
}
}
}
}
效果图
使用实例
// 无标题,只转菊花
HStatus.show("")
// 有标题,转菊花,禁止交互
HStatus.show("上传中...", userEnable: false)
// 隐藏
HStatus.hide()
实现思路
在window上添加一个view,展示loading信息,需要的时候可以隐藏。
优点:不需要绑定view,方便在任何地方调用。
重要功能:展示loading、展示进度、允许或者禁止loading期间交互、默认2s防抖
源码如下:
import UIKit
public class HStatus: UIView {
var isUserEnabled = true
/// 展示statusView,title不超过6个字,
/// 可用于刷新文字,会重新布局
/// 前2s禁止交互,默认2s后允许用交互 userEnable = false时一直不允许交互
@objc class public func show (_ title: String, userEnable: Bool = true) {
DispatchQueue.main.async {
// 隐藏 toastView
let window = UIApplication.shared.windows.first
window?.toastView.isHidden = true
// 找到status view
let statusView = window?.statusView
// 前置 防止遮挡
window?.bringSubviewToFront(statusView!)
statusView?.isUserEnabled = userEnable
// 先禁止交互
statusView?.isUserInteractionEnabled = true
// 展示出来
statusView?.show_in(title)
// 2s后,检查是否允许交互
statusView?.updateUserEnabled_in()
var width = title.sizeWith(font: mediumFont(16), inSize: CGSize.init(width: 100, height: 20)).width;
width = width + 25;
var margin = (width - 32 - 18) / 3.0;
// 未输入文字时,只展示菊花
if title.count == 0 {
margin = 10;
statusView?.backView.snp.remakeConstraints { (make) in
make.center.equalToSuperview()
make.size.equalTo(CGSize.init(width: 56, height: 56))
}
}
// 有文字时大小不小于80
else if width < 80 {
margin = (80 - 32 - 18) / 3.0;
statusView?.backView.snp.remakeConstraints { (make) in
make.center.equalToSuperview()
make.size.equalTo(CGSize.init(width: 80, height: 80))
}
}
// 按文字宽度展示
else {
statusView?.backView.snp.remakeConstraints { (make) in
make.center.equalToSuperview()
make.size.equalTo(CGSize.init(width: width, height: width))
}
}
// 平均分割Y轴的空间(margin)
statusView?.loadingImageView.snp.remakeConstraints { (make) in
make.top.equalToSuperview().offset(margin)
make.centerX.equalToSuperview()
}
statusView?.titleLabel.snp.remakeConstraints { (make) in
make.bottom.equalTo(-margin)
make.height.equalTo(18)
make.centerX.equalToSuperview()
make.left.equalTo(10)
make.right.equalTo(-10)
}
}
}
/// 更新状态文字(不会重新布局)
@objc class public func updateStatus (_ title: String) {
DispatchQueue.main.async {
let statusView = UIApplication.shared.windows.first?.statusView
statusView?.titleLabel.text = title
}
}
/// 隐藏状态
@objc class public func hide () {
let statusView = UIApplication.shared.windows.first?.statusView
statusView?.stopAnimation_in();
}
// MARK: private
@objc private func show_in(_ title: String) {
titleLabel.text = title
if self.isHidden {
self.isHidden = false
startAnimation()
}
}
@objc private func stopAnimation_in() {
DispatchQueue.main.async {
self.loadingImageView.layer.removeAllAnimations()
self.isHidden = true;
}
}
@objc private func updateUserEnabled_in() {
DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
self.isUserInteractionEnabled = !self.isUserEnabled
})
}
public override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
self.isUserInteractionEnabled = false
addSubview(backView)
backView.addSubview(titleLabel)
backView.addSubview(iconImageView)
backView.addSubview(loadingImageView)
iconImageView.snp.makeConstraints { (make) in
make.center.equalTo(loadingImageView)
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
@objc private func startAnimation() {
let rotatinAnimation = CABasicAnimation.init(keyPath: "transform.rotation.z")
rotatinAnimation.toValue = NSNumber.init(value: Double.pi * 2.0)
rotatinAnimation.duration = 0.7
rotatinAnimation.isCumulative = true
rotatinAnimation.repeatCount = MAXFLOAT
loadingImageView.layer.add(rotatinAnimation, forKey: "rotationAnimation")
}
// MARK: views
private lazy var backView: UIView = {
let backView = UIView.init()
backView.backgroundColor = UIColor.white
backView.layer.cornerRadius = 5
backView.layer.setLayerShadow(.black.withAlphaComponent(0.16), offset: CGSize.init(width: 0, height: 0), radius: 16)
return backView
}()
private lazy var titleLabel: UILabel = {
let lable = UILabel.init()
lable.textAlignment = .center
lable.font = regularFont(12)
lable.textColor = .black.withAlphaComponent(0.8)
lable.numberOfLines = 3
return lable
}()
private lazy var iconImageView: UIImageView = {
let iconImageView = UIImageView.init(image: UIImage.init(named: "common_loading_icon"))
return iconImageView
}()
private lazy var loadingImageView: UIImageView = {
let imageView = UIImageView.init(image: UIImage.init(named: "common_loading_n"))
return imageView
}()
}
extension UIWindow {
@objc var statusView :HStatus {
get {
if let view = self.viewWithTag(123411981) {
return view as! HStatus
} else {
let view = HStatus()
view.tag = 123411981
view.isHidden = true
self.addSubview(view);
view.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
return view
}
}
}
}
没有什么组件是最好的,根据自己的业务不断的打磨,适合最重要。