This commit is contained in:
xaoxuu 2019-08-01 17:02:17 +08:00
parent 40bf7d38a8
commit d6e8ae791f
8 changed files with 238 additions and 195 deletions

View File

@ -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",
);

View File

@ -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) {

View File

@ -6,6 +6,7 @@ target 'Example' do
pod 'ProHUD', :path => '..'
pod 'SnapKit', '4.2.0'
pod 'Inspire', :path => '../../Inspire'
end

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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?
/// iOS13window
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.height0
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
}
}
}
}