This commit is contained in:
xaoxuu 2023-04-27 18:14:09 +08:00
parent abe4738c0f
commit 7e672707cf
19 changed files with 264 additions and 91 deletions

View File

@ -69,20 +69,46 @@ class AlertVC: ListVC {
} }
} }
list.add(title: "文字 + 按钮") { section in list.add(title: "文字 + 按钮") { section in
section.add(title: "只有一小段文字 + 无背景色按钮") {
Alert { alert in
alert.config.boldTextFont = .systemFont(ofSize: 15)
alert.config.buttonFont = .systemFont(ofSize: 15)
alert.config.cardCornerRadius = 12
alert.vm.title = "你正在使用移动网络观看"
} .onViewDidLoad { vc in
guard let alert = vc as? Alert else {
return
}
alert.add(contentSpacing: 30)
let v = UIView()
v.backgroundColor = UIColor("#f2f2f2")
alert.add(subview: v).snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.height.equalTo(1)
}
alert.add(contentSpacing: 16)
alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc")))
}
}
section.add(title: "只有一段文字 + 无背景色按钮") { section.add(title: "只有一段文字 + 无背景色按钮") {
Alert { alert in Alert { alert in
alert.config.boldTextFont = .systemFont(ofSize: 15) alert.config.boldTextFont = .systemFont(ofSize: 15)
alert.config.buttonFont = .systemFont(ofSize: 15) alert.config.buttonFont = .systemFont(ofSize: 15)
alert.config.cardCornerRadius = 12 alert.config.cardCornerRadius = 12
alert.vm.title = "为了维护社区氛围,上麦用户需进行主播认证" alert.vm.title = "为了维护社区氛围,上麦用户需进行主播认证"
} .onViewDidLoad { vc in
guard let alert = vc as? Alert else {
return
}
alert.add(contentSpacing: 30)
let v = UIView() let v = UIView()
v.backgroundColor = UIColor("#F7F7F7") v.backgroundColor = UIColor("#f2f2f2")
alert.add(subview: v).snp.makeConstraints { make in alert.add(subview: v).snp.makeConstraints { make in
make.left.right.equalToSuperview() make.left.right.equalToSuperview()
make.height.equalTo(1) make.height.equalTo(1)
} }
alert.add(spacing: 16) alert.add(contentSpacing: 16)
alert.add(action: "取消", style: .plain(textColor: UIColor("#939999"))) alert.add(action: "取消", style: .plain(textColor: UIColor("#939999")))
alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc"))) alert.add(action: "确定", style: .plain(textColor: UIColor("#14cccc")))
} }
@ -281,7 +307,7 @@ class AlertVC: ListVC {
slider.maximumValue = 100 slider.maximumValue = 100
slider.value = 50 slider.value = 50
alert.add(action: slider) alert.add(action: slider)
alert.add(spacing: 24, for: alert.actionStack) alert.add(actionSpacing: 124)
alert.add(action: "取消", style: .gray) alert.add(action: "取消", style: .gray)
} }
} }
@ -305,7 +331,7 @@ class AlertVC: ListVC {
// Fallback on earlier versions // Fallback on earlier versions
} }
alert.config.actionAxis = .vertical alert.config.actionAxis = .vertical
alert.add(spacing: 24, for: alert.actionStack) alert.add(contentSpacing: 24)
alert.add(action: "OK", style: .gray) alert.add(action: "OK", style: .gray)
} }
} }

View File

@ -46,7 +46,7 @@ public class Alert: ProHUD.Controller {
let lb = UILabel() let lb = UILabel()
lb.textColor = config.primaryLabelColor lb.textColor = config.primaryLabelColor
lb.font = config.titleFontByDefault lb.font = config.titleFontByDefault
lb.textAlignment = .justified lb.textAlignment = .left
lb.numberOfLines = config.titleMaxLines lb.numberOfLines = config.titleMaxLines
return lb return lb
}() }()
@ -56,7 +56,7 @@ public class Alert: ProHUD.Controller {
let lb = UILabel() let lb = UILabel()
lb.textColor = config.primaryLabelColor lb.textColor = config.primaryLabelColor
lb.font = config.bodyFontByDefault lb.font = config.bodyFontByDefault
lb.textAlignment = .justified lb.textAlignment = .left
lb.numberOfLines = config.bodyMaxLines lb.numberOfLines = config.bodyMaxLines
return lb return lb
}() }()
@ -100,7 +100,8 @@ public class Alert: ProHUD.Controller {
public override func viewDidLoad() { public override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
view.tintColor = config.tintColor view.tintColor = config.tintColor
reloadData() reloadData(animated: false)
navEvents[.onViewDidLoad]?(self)
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {

View File

@ -0,0 +1,16 @@
//
// AlertButton.swift
//
//
// Created by xaoxuu on 2023/4/27.
//
import UIKit
public class AlertButton: Button {
override var customEdgeInset: UIEdgeInsets {
.init(top: 12, left: 24, bottom: 12, right: 24)
}
}

View File

@ -14,7 +14,7 @@ extension Alert: InternalConvenienceLayout {
insert(action: action, at: actionStack.arrangedSubviews.count) insert(action: action, at: actionStack.arrangedSubviews.count)
} }
@discardableResult public func insert(action: Action, at index: Int) -> Button { @discardableResult public func insert(action: Action, at index: Int) -> Button {
let btn = Button(config: config, action: action) let btn = AlertButton(config: config, action: action)
if index < actionStack.arrangedSubviews.count { if index < actionStack.arrangedSubviews.count {
actionStack.insertArrangedSubview(btn, at: index) actionStack.insertArrangedSubview(btn, at: index)
} else { } else {
@ -32,7 +32,7 @@ extension Alert: InternalConvenienceLayout {
self?.pop() self?.pop()
} }
} }
if isViewLoaded { if isViewDisplayed {
self.actionStack.layoutIfNeeded() self.actionStack.layoutIfNeeded()
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) { UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
@ -78,7 +78,7 @@ extension Alert: InternalConvenienceLayout {
buttonEvents[view] = nil buttonEvents[view] = nil
} }
} }
if isViewLoaded { if isViewDisplayed {
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) { UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
self.actionStack.layoutIfNeeded() self.actionStack.layoutIfNeeded()
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
@ -105,16 +105,31 @@ extension Alert: InternalConvenienceLayout {
// MARK: // MARK:
public func add(spacing: CGFloat) { public func set(spacing: CGFloat, after: UIView?, in stack: UIStackView) {
add(spacing: spacing, for: contentStack) if #available(iOS 11.0, *) {
if let after = after ?? stack.arrangedSubviews.last {
stack.setCustomSpacing(spacing, after: after)
}
}
} }
public func add(spacing: CGFloat, for stack: UIStackView) { public func set(contentSpacing: CGFloat, after: UIView?) {
if #available(iOS 11.0, *) { set(spacing: contentSpacing, after: after, in: contentStack)
if let last = stack.arrangedSubviews.last {
stack.setCustomSpacing(spacing, after: last)
} }
public func set(textSpacing: CGFloat, after: UIView?) {
set(spacing: textSpacing, after: after, in: textStack)
} }
public func set(actionSpacing: CGFloat, after: UIView?) {
set(spacing: actionSpacing, after: after, in: actionStack)
}
public func add(contentSpacing: CGFloat) {
set(spacing: contentSpacing, after: nil, in: contentStack)
}
public func add(textSpacing: CGFloat) {
set(spacing: textSpacing, after: nil, in: textStack)
}
public func add(actionSpacing: CGFloat) {
set(spacing: actionSpacing, after: nil, in: actionStack)
} }
// MARK: // MARK:

View File

@ -13,10 +13,13 @@ extension Alert: DefaultLayout {
return config return config
} }
func reloadDataByDefault() { func reloadData(animated: Bool) {
if self.cfg.customReloadData?(self) == true {
return
}
let isFirstLayout: Bool let isFirstLayout: Bool
if contentView.superview == nil { if contentView.superview == nil {
isFirstLayout = true isFirstLayout = animated
// //
loadContentViewIfNeeded() loadContentViewIfNeeded()
} else { } else {
@ -42,6 +45,7 @@ extension Alert: DefaultLayout {
contentView.layoutIfNeeded() contentView.layoutIfNeeded()
// //
if animated {
if isFirstLayout { if isFirstLayout {
view.layoutIfNeeded() view.layoutIfNeeded()
imageView.transform = .init(scaleX: 0.7, y: 0.7) imageView.transform = .init(scaleX: 0.7, y: 0.7)
@ -54,7 +58,9 @@ extension Alert: DefaultLayout {
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
} }
} }
} else {
view.layoutIfNeeded()
}
} }
func loadContentViewIfNeeded() { func loadContentViewIfNeeded() {
@ -166,14 +172,14 @@ extension Alert {
contentStack.insertArrangedSubview(textStack, at: 0) contentStack.insertArrangedSubview(textStack, at: 0)
} }
textStack.snp.remakeConstraints { (mk) in textStack.snp.remakeConstraints { (mk) in
mk.top.greaterThanOrEqualTo(contentView).inset(config.padding*1.75) mk.top.greaterThanOrEqualTo(contentView).inset(config.padding*1.875)
mk.bottom.lessThanOrEqualTo(contentView).inset(config.padding*1.75) mk.bottom.lessThanOrEqualTo(contentView).inset(config.padding*1.875)
if UIScreen.main.bounds.width < 414 { if UIScreen.main.bounds.width < 414 {
mk.left.greaterThanOrEqualTo(contentView).inset(config.padding*1.5)
mk.right.lessThanOrEqualTo(contentView).inset(config.padding*1.5)
} else {
mk.left.greaterThanOrEqualTo(contentView).inset(config.padding*2) mk.left.greaterThanOrEqualTo(contentView).inset(config.padding*2)
mk.right.lessThanOrEqualTo(contentView).inset(config.padding*2) mk.right.lessThanOrEqualTo(contentView).inset(config.padding*2)
} else {
mk.left.greaterThanOrEqualTo(contentView).inset(config.padding*3)
mk.right.lessThanOrEqualTo(contentView).inset(config.padding*3)
} }
} }
} }

View File

@ -58,6 +58,11 @@ open class Controller: UIViewController {
var navEvents = [NavEvent: ((Controller) -> Void)]() var navEvents = [NavEvent: ((Controller) -> Void)]()
@discardableResult public func onViewDidLoad(_ callback: ((_ vc: Controller) -> Void)?) -> Controller {
navEvents[.onViewDidLoad] = callback
return self
}
@discardableResult public func onViewWillAppear(_ callback: ((_ vc: Controller) -> Void)?) -> Controller { @discardableResult public func onViewWillAppear(_ callback: ((_ vc: Controller) -> Void)?) -> Controller {
navEvents[.onViewWillAppear] = callback navEvents[.onViewWillAppear] = callback
return self return self

View File

@ -17,10 +17,7 @@ public extension CommonLayout {
guard let self = self as? DefaultLayout else { guard let self = self as? DefaultLayout else {
return return
} }
if self.cfg.customReloadData?(self) == true { self.reloadData(animated: true)
return
}
self.reloadDataByDefault()
} }
} }

View File

@ -54,9 +54,12 @@ public protocol ConvenienceLayout {
// MARK: // MARK:
/// iOS11 /// stack
/// - Parameter spacing: /// - Parameters:
func add(spacing: CGFloat) /// - spacing:
/// - after: view
/// - stack: stack
func set(spacing: CGFloat, after: UIView?, in stack: UIStackView)
// MARK: // MARK:

View File

@ -11,9 +11,7 @@ protocol DefaultLayout: CommonLayout {
var cfg: Configuration { get } var cfg: Configuration { get }
func reloadDataByDefault() func reloadData(animated: Bool)
func loadContentViewIfNeeded()
} }

View File

@ -12,7 +12,7 @@ extension LoadingAnimation {
/// updateProgress(0) /// updateProgress(0)
/// - Parameter progress: 0~1 /// - Parameter progress: 0~1
public func update(progress: CGFloat) { public func update(progress: CGFloat) {
guard isViewLoaded else { return } guard isViewDisplayed else { return }
guard let superview = imageView.superview else { return } guard let superview = imageView.superview else { return }
if progressView == nil { if progressView == nil {
let width = imageView.frame.size.width + ProgressView.lineWidth * 2 let width = imageView.frame.size.width + ProgressView.lineWidth * 2

View File

@ -13,12 +13,13 @@ open class Button: UIButton {
public internal(set) var action: Action? public internal(set) var action: Action?
var edgeInset: CGFloat { 8 * 1.5 } var customEdgeInset: UIEdgeInsets {
.init(top: 12, left: 24, bottom: 12, right: 24)
}
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
let padding = edgeInset contentEdgeInsets = customEdgeInset
contentEdgeInsets = .init(top: padding, left: padding * 2, bottom: padding, right: padding * 2)
addTarget(self, action: #selector(self._onTouchUp(_:)), for: [.touchUpInside, .touchUpOutside]) addTarget(self, action: #selector(self._onTouchUp(_:)), for: [.touchUpInside, .touchUpOutside])
addTarget(self, action: #selector(self._onTouchDown(_:)), for: .touchDown) addTarget(self, action: #selector(self._onTouchDown(_:)), for: .touchDown)
addTarget(self, action: #selector(self._onTouchUpInside(_:)), for: .touchUpInside) addTarget(self, action: #selector(self._onTouchUpInside(_:)), for: .touchUpInside)

View File

@ -64,10 +64,12 @@ public class Sheet: Controller {
let tap = UITapGestureRecognizer(target: self, action: #selector(_onTappedBackground(_:))) let tap = UITapGestureRecognizer(target: self, action: #selector(_onTappedBackground(_:)))
backgroundView.addGestureRecognizer(tap) backgroundView.addGestureRecognizer(tap)
reloadData() reloadData(animated: false)
_translateOut() _translateOut()
navEvents[.onViewDidLoad]?(self)
} }
} }

View File

@ -8,5 +8,7 @@
import UIKit import UIKit
public class SheetButton: Button { public class SheetButton: Button {
override var edgeInset: CGFloat { 8 * 1.75 } override var customEdgeInset: UIEdgeInsets {
.init(top: 14, left: 28, bottom: 14, right: 28)
}
} }

View File

@ -28,7 +28,7 @@ extension Sheet: ConvenienceLayout {
self?.pop() self?.pop()
} }
} }
if isViewLoaded { if isViewDisplayed {
self.contentStack.layoutIfNeeded() self.contentStack.layoutIfNeeded()
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) { UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
@ -74,7 +74,7 @@ extension Sheet: ConvenienceLayout {
buttonEvents[view] = nil buttonEvents[view] = nil
} }
} }
if isViewLoaded { if isViewDisplayed {
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) { UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
self.contentStack.layoutIfNeeded() self.contentStack.layoutIfNeeded()
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
@ -92,14 +92,18 @@ extension Sheet: ConvenienceLayout {
// MARK: // MARK:
public func add(spacing: CGFloat) { public func set(spacing: CGFloat, after: UIView?, in stack: UIStackView) {
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
if let last = contentStack.arrangedSubviews.last { if let after = after ?? stack.arrangedSubviews.last {
contentStack.setCustomSpacing(spacing, after: last) stack.setCustomSpacing(spacing, after: after)
} }
} }
} }
public func add(spacing: CGFloat) {
set(spacing: spacing, after: nil, in: contentStack)
}
// MARK: // MARK:
@discardableResult public func set(customView: UIView) -> UIView { @discardableResult public func set(customView: UIView) -> UIView {

View File

@ -13,7 +13,10 @@ extension Sheet: DefaultLayout {
return config return config
} }
func reloadDataByDefault() { func reloadData(animated: Bool) {
if self.cfg.customReloadData?(self) == true {
return
}
// background // background
if backgroundView.superview == nil { if backgroundView.superview == nil {
view.insertSubview(backgroundView, at: 0) view.insertSubview(backgroundView, at: 0)
@ -30,10 +33,9 @@ extension Sheet: DefaultLayout {
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
} }
} }
} }
func loadContentViewIfNeeded() { private func loadContentViewIfNeeded() {
contentView.layer.cornerRadiusWithContinuous = config.cardCornerRadiusByDefault contentView.layer.cornerRadiusWithContinuous = config.cardCornerRadiusByDefault
if contentView.superview != view { if contentView.superview != view {
view.insertSubview(contentView, aboveSubview: backgroundView) view.insertSubview(contentView, aboveSubview: backgroundView)
@ -92,5 +94,4 @@ extension Sheet: DefaultLayout {
} }
} }

View File

@ -124,7 +124,9 @@ public class Toast: Controller {
let pan = UIPanGestureRecognizer(target: self, action: #selector(_onPanGesture(_:))) let pan = UIPanGestureRecognizer(target: self, action: #selector(_onPanGesture(_:)))
view.addGestureRecognizer(pan) view.addGestureRecognizer(pan)
reloadData() reloadData(animated: false)
navEvents[.onViewDidLoad]?(self)
} }
public func onTapped(action: @escaping (_ toast: Toast) -> Void) { public func onTapped(action: @escaping (_ toast: Toast) -> Void) {

View File

@ -8,6 +8,8 @@
import UIKit import UIKit
public class ToastButton: Button { public class ToastButton: Button {
override var edgeInset: CGFloat { 8 * 1.25 } override var customEdgeInset: UIEdgeInsets {
.init(top: 10, left: 24, bottom: 10, right: 24)
}
} }

View File

@ -7,21 +7,35 @@
import UIKit import UIKit
public extension Toast { extension Toast: ConvenienceLayout {
@discardableResult func add(subview: UIView) -> UIView { // MARK:
if contentStack.superview != nil {
contentStack.removeFromSuperview() ///
/// - Parameters:
/// - title:
/// - style:
/// - identifier:
/// - handler:
/// - Returns:
@discardableResult public func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ toast: Toast) -> Void)? = nil) -> Button {
if let handler = handler {
let action = Action(identifier: identifier, style: style, title: title) { vc in
if let vc = vc as? Toast {
handler(vc)
}
}
return add(action: action)
} else {
return add(action: .init(identifier: identifier, style: style, title: title, handler: nil))
} }
contentView.addSubview(subview)
return subview
} }
@discardableResult func add(action: Action) -> Button { @discardableResult public func add(action: Action) -> Button {
insert(action: action, at: actionStack.arrangedSubviews.count) insert(action: action, at: actionStack.arrangedSubviews.count)
} }
@discardableResult func insert(action: Action, at index: Int) -> Button { @discardableResult public func insert(action: Action, at index: Int) -> Button {
let btn = ToastButton(config: config, action: action) let btn = ToastButton(config: config, action: action)
if index < actionStack.arrangedSubviews.count { if index < actionStack.arrangedSubviews.count {
actionStack.insertArrangedSubview(btn, at: index) actionStack.insertArrangedSubview(btn, at: index)
@ -37,7 +51,7 @@ public extension Toast {
self?.pop() self?.pop()
} }
} }
if isViewLoaded { if isViewDisplayed {
self.actionStack.layoutIfNeeded() self.actionStack.layoutIfNeeded()
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) { UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
@ -46,36 +60,111 @@ public extension Toast {
return btn return btn
} }
// MARK:
public func button(for identifier: String) -> Button? {
if let index = actionIndex(for: identifier) {
return contentStack.arrangedSubviews[index] as? Button
}
return nil
}
// MARK:
public func update(action title: String, style: Action.Style? = nil, for identifier: String) {
if let btn = button(for: identifier), let act = btn.action {
act.title = title
if let style = style {
act.style = style
}
btn.update(config: config, action: act)
}
}
// MARK:
public func remove(actions finder: Action.Filter) {
if finder.ids.count > 0 {
for identifier in finder.ids {
while let index = actionIndex(for: identifier), index < contentStack.arrangedSubviews.count {
let view = contentStack.arrangedSubviews[index]
contentStack.removeArrangedSubview(view)
view.removeFromSuperview()
buttonEvents[view] = nil
}
}
} else {
for view in contentStack.arrangedSubviews {
contentStack.removeArrangedSubview(view)
view.removeFromSuperview()
buttonEvents[view] = nil
}
}
if isViewDisplayed {
UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) {
self.contentStack.layoutIfNeeded()
self.view.layoutIfNeeded()
}
}
}
// MARK:
@discardableResult public func add(subview: UIView) -> UIView {
if contentStack.superview != nil {
contentStack.removeFromSuperview()
}
contentView.addSubview(subview)
return subview
}
// MARK: // MARK:
func add(spacing: CGFloat) { public func set(spacing: CGFloat, after: UIView?, in stack: UIStackView) {
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
if let last = contentStack.arrangedSubviews.last { if let after = after ?? stack.arrangedSubviews.last {
contentStack.setCustomSpacing(spacing, after: last) stack.setCustomSpacing(spacing, after: after)
} }
} }
} }
/// public func set(contentSpacing: CGFloat, after: UIView?) {
/// - Parameters: set(spacing: contentSpacing, after: after, in: contentStack)
/// - title:
/// - style:
/// - identifier:
/// - handler:
/// - Returns:
@discardableResult func add(action title: String, style: Action.Style = .tinted, identifier: String? = nil, handler: ((_ toast: Toast) -> Void)? = nil) -> Button {
if let handler = handler {
let action = Action(identifier: identifier, style: style, title: title) { vc in
if let vc = vc as? Toast {
handler(vc)
} }
public func set(textSpacing: CGFloat, after: UIView?) {
set(spacing: textSpacing, after: after, in: textStack)
} }
return add(action: action) public func set(actionSpacing: CGFloat, after: UIView?) {
} else { set(spacing: actionSpacing, after: after, in: actionStack)
return add(action: .init(identifier: identifier, style: style, title: title, handler: nil))
} }
public func add(contentSpacing: CGFloat) {
set(spacing: contentSpacing, after: nil, in: contentStack)
}
public func add(textSpacing: CGFloat) {
set(spacing: textSpacing, after: nil, in: textStack)
}
public func add(actionSpacing: CGFloat) {
set(spacing: actionSpacing, after: nil, in: actionStack)
} }
// MARK:
public func set(customView: UIView) -> UIView {
self.customView = customView
contentView.addSubview(customView)
return customView
}
// MARK: internal
func actionIndex(for identifier: String) -> Int? {
let arr = contentStack.arrangedSubviews.compactMap({ $0 as? Button })
for i in 0 ..< arr.count {
if arr[i].action?.identifier == identifier {
return i
}
}
return nil
}
} }

View File

@ -13,7 +13,10 @@ extension Toast: DefaultLayout {
return config return config
} }
func reloadDataByDefault() { func reloadData(animated: Bool) {
if self.cfg.customReloadData?(self) == true {
return
}
loadContentViewIfNeeded() loadContentViewIfNeeded()
loadContentMaskViewIfNeeded() loadContentMaskViewIfNeeded()
guard customView == nil else { guard customView == nil else {