From d6e8ae791f3e7404685eaf5a68407ea593aaa046 Mon Sep 17 00:00:00 2001 From: xaoxuu Date: Thu, 1 Aug 2019 17:02:17 +0800 Subject: [PATCH] update --- Example/Example.xcodeproj/project.pbxproj | 2 + Example/Example/ViewController.swift | 22 +-- Example/Podfile | 1 + Example/Podfile.lock | 10 +- ProHUD.podspec | 2 +- ProHUD/Alert/AlertConfig.swift | 8 +- ProHUD/Toast/ToastConfig.swift | 161 +++++---------- ProHUD/Toast/ToastController.swift | 227 +++++++++++++++------- 8 files changed, 238 insertions(+), 195 deletions(-) diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 5f1c466..4af7c2a 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -208,11 +208,13 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-Example/Pods-Example-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Inspire/Inspire.framework", "${BUILT_PRODUCTS_DIR}/ProHUD/ProHUD.framework", "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Inspire.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProHUD.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework", ); diff --git a/Example/Example/ViewController.swift b/Example/Example/ViewController.swift index 54dcf0f..f9a9b07 100644 --- a/Example/Example/ViewController.swift +++ b/Example/Example/ViewController.swift @@ -43,15 +43,15 @@ class ViewController: UIViewController { // debugPrint("didDisappear") // } - let t = ProHUD.Toast(scene: .loading, title: "正在加载", message: "哈哈") - let a = ProHUD.show(alert: .loading, title: "正在加载", message: "请坐和放宽") - a.didMinimize { - hud.show(t) - } - t.didTapped { [weak t] in - hud.show(a) - t?.remove() - } +// let t = ProHUD.Toast(scene: .loading, title: "正在加载", message: "哈哈") +// let a = ProHUD.show(alert: .loading, title: "正在加载", message: "请坐和放宽") +// a.didMinimize { +// hud.show(t) +// } +// t.didTapped { [weak t] in +// hud.show(a) +// t?.remove() +// } // a.didMinimize { // ProHUD.show(toast: .loading, title: "正在加载", message: "哈哈").didTapped { @@ -60,8 +60,8 @@ class ViewController: UIViewController { // } - - + ProHUD.show(toast: .loading, title: "正在加载", message: "拉升的反馈老实交代分开就撒开了击快乐圣反馈老实交代分开就撒开了击快乐圣") +// ProHUD.show(toast: .loading, title: "正在加载", message: "哈克里斯蒂娜疯狂拉升的反馈老实交代分开就撒开了击快乐圣诞哈克里斯蒂娜疯狂拉升的反馈老实交代分开就撒开了击快乐圣诞") DispatchQueue.main.asyncAfter(deadline: .now()+1) { diff --git a/Example/Podfile b/Example/Podfile index 0d39940..6ba1a5f 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -6,6 +6,7 @@ target 'Example' do pod 'ProHUD', :path => '..' pod 'SnapKit', '4.2.0' + pod 'Inspire', :path => '../../Inspire' end diff --git a/Example/Podfile.lock b/Example/Podfile.lock index e09b10a..955b418 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,20 +1,26 @@ PODS: + - Inspire (1.0.5) - ProHUD (1.0): + - Inspire - SnapKit (= 4.2.0) - SnapKit (4.2.0) DEPENDENCIES: + - Inspire (from `../../Inspire`) - ProHUD (from `..`) - SnapKit (= 4.2.0) EXTERNAL SOURCES: + Inspire: + :path: ../../Inspire ProHUD: :path: .. SPEC CHECKSUMS: - ProHUD: df9d30311cda37d3cd6db6606415733d26b94144 + Inspire: a213962fa02d0c4b8e27e389318ff4b47e4565ed + ProHUD: 0b027f2e79a53869219703ed3f6ac3a48970fabd SnapKit: fe8a619752f3f27075cc9a90244d75c6c3f27e2a -PODFILE CHECKSUM: 346966e04c944658b93fb1a6052bfb61ddb271be +PODFILE CHECKSUM: 69a97e50e038f3f68362d4cfd30617688f7824cc COCOAPODS: 1.3.1 diff --git a/ProHUD.podspec b/ProHUD.podspec index 2d41cb9..fddc36d 100755 --- a/ProHUD.podspec +++ b/ProHUD.podspec @@ -14,6 +14,6 @@ Pod::Spec.new do |s| s.requires_arc = true s.dependency 'SnapKit', '4.2.0' - # s.dependency 'Inspire' + s.dependency 'Inspire' end diff --git a/ProHUD/Alert/AlertConfig.swift b/ProHUD/Alert/AlertConfig.swift index a2ad097..6c6ff42 100644 --- a/ProHUD/Alert/AlertConfig.swift +++ b/ProHUD/Alert/AlertConfig.swift @@ -101,8 +101,8 @@ public extension ProHUD.Configuration { let icon = UIImageView(image: img) vc.contentStack.addArrangedSubview(icon) icon.snp.makeConstraints { (mk) in - mk.top.greaterThanOrEqualTo(vc.contentView).offset(alertConfig.padding*2.5) - mk.bottom.lessThanOrEqualTo(vc.contentView).offset(-alertConfig.padding*2.5) + mk.top.greaterThanOrEqualTo(vc.contentView).offset(alertConfig.padding*2.25) + mk.bottom.lessThanOrEqualTo(vc.contentView).offset(-alertConfig.padding*2.25) mk.leading.greaterThanOrEqualTo(vc.contentView).offset(alertConfig.padding*4) mk.trailing.lessThanOrEqualTo(vc.contentView).offset(-alertConfig.padding*4) } @@ -113,8 +113,8 @@ public extension ProHUD.Configuration { if vc.vm.title?.count ?? 0 > 0 || vc.vm.message?.count ?? 0 > 0 { vc.contentStack.addArrangedSubview(vc.textStack) vc.textStack.snp.makeConstraints { (mk) in - mk.top.greaterThanOrEqualTo(vc.contentView).offset(alertConfig.padding*1.5) - mk.bottom.lessThanOrEqualTo(vc.contentView).offset(-alertConfig.padding*1.5) + mk.top.greaterThanOrEqualTo(vc.contentView).offset(alertConfig.padding*1.75) + mk.bottom.lessThanOrEqualTo(vc.contentView).offset(-alertConfig.padding*1.75) mk.leading.greaterThanOrEqualTo(vc.contentView).offset(alertConfig.padding*2) mk.trailing.lessThanOrEqualTo(vc.contentView).offset(-alertConfig.padding*2) } diff --git a/ProHUD/Toast/ToastConfig.swift b/ProHUD/Toast/ToastConfig.swift index bd81696..af3e5d4 100644 --- a/ProHUD/Toast/ToastConfig.swift +++ b/ProHUD/Toast/ToastConfig.swift @@ -16,12 +16,12 @@ public extension ProHUD.Configuration { public var titleFont = UIFont.boldSystemFont(ofSize: 18) /// 正文字体 public var bodyFont = UIFont.systemFont(ofSize: 16) - /// 标题最多行数(0代表不限制) - public var titleMaxLines = Int(0) - /// 正文最多行数(0代表不限制) - public var bodyMaxLines = Int(0) + /// 标题最多行数 + public var titleMaxLines = Int(1) + /// 正文最多行数 + public var bodyMaxLines = Int(10) /// 圆角半径 - public var cornerRadius = CGFloat(16) + public var cornerRadius = CGFloat(12) public var margin = CGFloat(8) @@ -33,39 +33,18 @@ public extension ProHUD.Configuration { lazy var loadSubviews: (ProHUD.Toast) -> Void = { return { (vc) in debugPrint(vc, "loadSubviews") - let config = toastConfig - vc.view.addSubview(vc.contentView) - vc.contentView.contentView.addSubview(vc.contentStack) - vc.contentStack.spacing = config.margin - - vc.contentView.layer.masksToBounds = true - vc.contentView.layer.cornerRadius = config.cornerRadius - - vc.contentView.snp.makeConstraints { (mk) in - mk.leading.trailing.top.equalToSuperview() - } - vc.contentStack.snp.makeConstraints { (mk) in - mk.top.equalToSuperview().offset(config.padding) - mk.bottom.equalToSuperview().offset(-config.padding) - mk.leading.equalToSuperview().offset(config.padding) - mk.trailing.equalToSuperview().offset(-config.padding) - } + vc.view.addSubview(vc.titleLabel) + vc.view.addSubview(vc.bodyLabel) + vc.view.addSubview(vc.imageView) } }() /// 更新视图 - lazy var updateFrame: (ProHUD.Toast) -> Void = { + lazy var reloadData: (ProHUD.Toast) -> Void = { return { (vc) in - debugPrint(vc, "updateFrame") - let config = toastConfig - let isFirstLayout: Bool - // 图标和文字至少有一个,如果都没有添加到视图中,说明是第一次layout - if vc.textStack.superview == nil && vc.imageView?.superview == nil { - isFirstLayout = true - } else { - isFirstLayout = false - } + debugPrint(vc, "reloadData") + // 设置数据 let imgStr: String switch vc.vm.scene { case .success: @@ -84,84 +63,52 @@ public extension ProHUD.Configuration { imgStr = "ProHUDMessage" } let img = vc.vm.icon ?? ProHUD.image(named: imgStr) - if let imgv = vc.imageView { - imgv.image = img - } else { - let icon = UIImageView(image: img) - vc.contentStack.addArrangedSubview(icon) -// icon.snp.makeConstraints { (mk) in -// mk.top.greaterThanOrEqualTo(vc.contentView).offset(config.padding+config.margin) -// mk.bottom.lessThanOrEqualTo(vc.contentView).offset(-config.padding-config.margin) -// mk.leading.greaterThanOrEqualTo(vc.contentView).offset(config.padding+config.margin) -// mk.trailing.lessThanOrEqualTo(vc.contentView).offset(-config.padding-config.margin) -// } - vc.imageView = icon - } + vc.imageView.image = img + vc.titleLabel.text = vc.vm.title + vc.bodyLabel.text = vc.vm.message - // text - if vc.vm.title?.count ?? 0 > 0 || vc.vm.message?.count ?? 0 > 0 { - vc.contentStack.addArrangedSubview(vc.textStack) -// vc.textStack.snp.makeConstraints { (mk) in -// mk.top.greaterThanOrEqualTo(vc.contentView).offset(config.padding*1.5) -// mk.bottom.lessThanOrEqualTo(vc.contentView).offset(-config.padding*1.5) -// mk.leading.greaterThanOrEqualTo(vc.contentView).offset(config.padding*2) -// mk.trailing.lessThanOrEqualTo(vc.contentView).offset(-config.padding*2) -// } - if vc.vm.title?.count ?? 0 > 0 { - if let lb = vc.titleLabel { - lb.text = vc.vm.title - } else { - let title = UILabel() - title.textAlignment = .justified - title.numberOfLines = config.titleMaxLines - title.textColor = UIColor.init(white: 0.2, alpha: 1) - title.font = config.titleFont - title.text = vc.vm.title - vc.textStack.addArrangedSubview(title) - vc.titleLabel = title - } - } else { - vc.titleLabel?.removeFromSuperview() - } - if vc.vm.message?.count ?? 0 > 0 { - if let lb = vc.messageLabel { - lb.text = vc.vm.message - } else { - let body = UILabel() - body.textAlignment = .justified - body.font = config.bodyFont - body.numberOfLines = config.bodyMaxLines - body.textColor = UIColor.darkGray - body.font = config.bodyFont - body.text = vc.vm.message - vc.textStack.addArrangedSubview(body) - vc.messageLabel = body - } - } else { - vc.messageLabel?.removeFromSuperview() - } - } else { - vc.textStack.removeFromSuperview() - } + vc.tintColor = vc.vm.scene.tintColor - if isFirstLayout { - vc.view.layoutIfNeeded() - vc.updateFrame() - vc.imageView?.transform = .init(scaleX: 0.75, y: 0.75) - } else { - - } - - UIView.animateFastEaseOut(delay: 0, animations: { - vc.imageView?.transform = .identity - vc.view.layoutIfNeeded() - vc.updateFrame() - - }) { (done) in - } } }() + /// 更新视图 + lazy var layoutSubviews: (ProHUD.Toast) -> Void = { + return { (vc) in + debugPrint(vc, "layoutSubviews") + let config = toastConfig + + let scene = vc.vm.scene + + vc.imageView.snp.makeConstraints { (mk) in + mk.top.equalToSuperview().offset(config.padding) + mk.leading.equalToSuperview().offset(config.padding) + mk.bottom.lessThanOrEqualToSuperview().offset(-config.padding) + } + vc.titleLabel.snp.makeConstraints { (mk) in + mk.top.equalToSuperview().offset(config.padding) + mk.leading.equalTo(vc.imageView.snp.trailing).offset(config.margin) + mk.leading.greaterThanOrEqualToSuperview().offset(config.padding) + mk.trailing.equalToSuperview().offset(-config.padding) + } + vc.bodyLabel.snp.makeConstraints { (mk) in + mk.top.equalTo(vc.titleLabel.snp.bottom).offset(config.margin) + mk.leading.trailing.equalTo(vc.titleLabel) + mk.bottom.lessThanOrEqualToSuperview().offset(-config.padding) + } + if [.default, .loading].contains(vc.vm.scene) { + vc.blurMask(.extraLight) + } else { + vc.blurMask(nil) + } + if let bv = vc.blurView { + vc.backgroundView = bv + } else { + vc.backgroundView.backgroundColor = vc.vm.scene.backgroundColor + } + vc.view.layoutIfNeeded() + } + }() /// 加载视图 /// - Parameter callback: 回调代码 public mutating func loadSubviews(_ callback: @escaping (ProHUD.Toast) -> Void) { @@ -170,8 +117,8 @@ public extension ProHUD.Configuration { /// 更新视图 /// - Parameter callback: 回调代码 - public mutating func updateFrame(_ callback: @escaping (ProHUD.Toast) -> Void) { - updateFrame = callback + public mutating func reloadData(_ callback: @escaping (ProHUD.Toast) -> Void) { + reloadData = callback } } diff --git a/ProHUD/Toast/ToastController.swift b/ProHUD/Toast/ToastController.swift index ff3c4cb..6d4df6a 100644 --- a/ProHUD/Toast/ToastController.swift +++ b/ProHUD/Toast/ToastController.swift @@ -7,33 +7,54 @@ // import UIKit -import SnapKit +import Inspire public extension ProHUD { class Toast: HUDController { public var window: UIWindow? - /// 内容容器 - public var contentView = BlurView() - internal var contentStack: StackContainer = { - let stack = StackContainer() - stack.axis = .horizontal - stack.alignment = .top - stack.spacing = toastConfig.margin - return stack - }() /// 图标 - internal var imageView: UIImageView? - /// 文本区域 - internal var textStack: StackContainer = { - let stack = StackContainer() - stack.spacing = toastConfig.margin - stack.alignment = .leading - return stack + internal lazy var imageView: UIImageView = { + let imgv = UIImageView() + imgv.contentMode = .scaleAspectFit + return imgv }() - internal var titleLabel: UILabel? - internal var messageLabel: UILabel? + + /// 标题 + internal lazy var titleLabel: UILabel = { + let lb = UILabel() + lb.textColor = UIColor.init(white: 0.2, alpha: 1) + lb.font = toastConfig.titleFont + lb.textAlignment = .justified + lb.numberOfLines = toastConfig.titleMaxLines + return lb + }() + + /// 正文 + internal lazy var bodyLabel: UILabel = { + let lb = UILabel() + lb.textColor = .darkGray + lb.font = toastConfig.bodyFont + lb.textAlignment = .justified + lb.numberOfLines = toastConfig.bodyMaxLines + return lb + }() + + /// 毛玻璃层 + var blurView: UIVisualEffectView? + + /// 背景层(在iOS13之后window) + var backgroundView = UIView() + + /// 设置颜色 + open var tintColor: UIColor!{ + didSet { + imageView.tintColor = tintColor + titleLabel.textColor = tintColor + bodyLabel.textColor = tintColor + } + } /// 视图模型 public var vm = ViewModel() @@ -51,6 +72,7 @@ public extension ProHUD { /// - Parameter icon: 图标 public convenience init(scene: Scene = .default, title: String? = nil, message: String? = nil, icon: UIImage? = nil) { self.init() + vm.scene = scene vm.title = title vm.message = message @@ -62,7 +84,10 @@ public extension ProHUD { timeout = 2 } - willLayout() + // 布局 + toastConfig.loadSubviews(self) + toastConfig.reloadData(self) + toastConfig.layoutSubviews(self) // 点击 let tap = UITapGestureRecognizer(target: self, action: #selector(privDidTapped(_:))) @@ -92,20 +117,83 @@ public extension ProHUD { } } - internal func updateFrame() { - let newSize = contentView.frame.size - view.frame.size = newSize - window?.frame.size = newSize - hud.updateToastsLayout() + @discardableResult + func blurMask(_ blurEffectStyle: UIBlurEffect.Style?) -> Toast { + if let s = blurEffectStyle { + if let bv = blurView { + bv.effect = UIBlurEffect.init(style: s) + } else { + blurView = UIVisualEffectView(effect: UIBlurEffect.init(style: s)) + blurView?.layer.masksToBounds = true + blurView?.layer.cornerRadius = toastConfig.cornerRadius + } + } else { + blurView?.removeFromSuperview() + blurView = nil + } + return self } + @discardableResult + func update(title: String?) -> Toast { + vm.title = title + titleLabel.text = title + return self + } + + @discardableResult + func update(message: String?) -> Toast { + vm.message = message + bodyLabel.text = message + return self + } + + @discardableResult + func update(icon: UIImage?) -> Toast { + vm.icon = icon + imageView.image = icon + return self + } + +// internal func updateFrame() { +// let config = toastConfig +// var f = UIScreen.main.bounds +// contentStack.frame.size.width = CGFloat.minimum(f.width - 4 * config.margin, config.maxWidth) +// titleLabel?.sizeToFit() +// messageLabel?.sizeToFit() +// contentStack.layoutIfNeeded() +// f.size.width = contentStack.frame.size.width +// f.size.height = (textStack.arrangedSubviews.last?.frame.maxY ?? 0) + config.margin +// debugPrint(f) +// func updateFrame(_ frame: CGRect) -> CGRect { +// return CGRect(origin: CGPoint(x: config.margin, y: config.margin), size: frame.size) +// } +// func superBounds(_ frame: CGRect) -> CGRect { +// return CGRect(x: 0, y: 0, width: frame.width + 2 * config.margin, height: frame.height + 2 * config.margin) +// } +// contentStack.frame = updateFrame(f) +// contentView.frame = superBounds(f) +// view.frame = superBounds(f) +// window?.frame = superBounds(f) +// hud.updateToastsLayout() +// } + // MARK: 设置函数 /// 设置超时时间 /// - Parameter timeout: 超时时间 @discardableResult public func timeout(_ timeout: TimeInterval?) -> Toast { self.timeout = timeout - willLayout() + // 超时 + timeoutBlock?.cancel() + if let t = timeout, t > 0 { + timeoutBlock = DispatchWorkItem(block: { [weak self] in + self?.remove() + }) + DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: timeoutBlock!) + } else { + timeoutBlock = nil + } return self } @@ -130,7 +218,8 @@ public extension ProHUD { vm.title = title vm.message = message vm.icon = icon - toastConfig.updateFrame(self) + toastConfig.reloadData(self) + toastConfig.layoutSubviews(self) return self } @@ -141,32 +230,6 @@ public extension ProHUD { } fileprivate extension ProHUD.Toast { - func willLayout() { - willLayout?.cancel() - willLayout = DispatchWorkItem(block: { [weak self] in - if let a = self { - // 布局 - toastConfig.loadSubviews(a) - toastConfig.updateFrame(a) - // 超时 - a.timeoutBlock?.cancel() - if let t = a.timeout, t > 0 { - a.timeoutBlock = DispatchWorkItem(block: { [weak self] in - self?.remove() - }) - DispatchQueue.main.asyncAfter(deadline: .now()+t, execute: a.timeoutBlock!) - } else { - a.timeoutBlock = nil - } - } - }) - DispatchQueue.main.asyncAfter(deadline: .now()+0.001, execute: willLayout!) - } - - func willUpdateToastsLayout() { - - } - /// 点击事件 /// - Parameter sender: 手势 @@ -189,7 +252,7 @@ fileprivate extension ProHUD.Toast { UIView.animateForToast(animations: { self.window?.transform = .identity }) { (done) in - // 重置计时器 + // FIXME: 重置计时器 } } @@ -206,22 +269,39 @@ public extension ProHUD { func show(_ toast: Toast) -> Toast { let config = toastConfig if toast.window == nil { - let w = UIWindow(frame: .init(x: config.margin, y: config.margin, width: UIScreen.main.bounds.width - 2*config.margin, height: 500)) + let width = CGFloat.minimum(UIScreen.main.bounds.width - 2*config.margin, config.maxWidth) + let w = UIWindow(frame: .init(x: (UIScreen.main.bounds.width - width) / 2, y: config.margin, width: width, height: 400)) toast.window = w - w.rootViewController = toast w.windowLevel = UIWindow.Level(5000) w.backgroundColor = .clear w.layer.shadowRadius = 8 w.layer.shadowOffset = .init(width: 0, height: 5) w.layer.shadowOpacity = 0.2 + w.rootViewController = toast + } let window = toast.window! - window.makeKeyAndVisible() - window.transform = .init(translationX: 0, y: -window.frame.maxY) - + window.isHidden = false toasts.append(toast) + // background & frame + toast.view.layoutIfNeeded() + toast.titleLabel.sizeToFit() + toast.bodyLabel.sizeToFit() + let width = toast.view.frame.width + var height = CGFloat(0) + for v in toast.view.subviews { + height = CGFloat.maximum(v.frame.maxY, height) + } + height += config.padding + toast.backgroundView.frame.size = CGSize(width: width, height: height) + window.insertSubview(toast.backgroundView, at: 0) + window.frame.size.height = height // 这里之后toast.view.frame.height会变成0 + toast.view.frame.size.height = height + + updateToastsLayout() + window.transform = .init(translationX: 0, y: -window.frame.maxY) UIView.animateForToast { window.transform = .identity } @@ -296,18 +376,25 @@ fileprivate extension ProHUD { func updateToastsLayout() { for (i, e) in toasts.enumerated() { let config = toastConfig - var frame = e.window?.frame ?? .zero - if i == 0 { - frame.origin.y = 44 - } else { - let lastY = toasts[i-1].window?.frame.maxY ?? .zero - frame.origin.y = lastY + config.margin - } - UIView.animateForToast(animations: { - e.window?.frame = frame - }) { (done) in - + if let window = e.window { + var frame = window.frame + if i == 0 { + if isPortrait { + frame.origin.y = Inspire.shared.screen.updatedSafeAreaInsets.top + } else { + frame.origin.y = config.margin + } + } else { + let lastY = toasts[i-1].window?.frame.maxY ?? .zero + frame.origin.y = lastY + config.margin + } + UIView.animateForToast(animations: { + e.window?.frame = frame + }) { (done) in + + } } + } }