diff --git a/PHDemo/PHDemo/DemoCapsuleVC.swift b/PHDemo/PHDemo/DemoCapsuleVC.swift index a84c79d..c8ab9ff 100644 --- a/PHDemo/PHDemo/DemoCapsuleVC.swift +++ b/PHDemo/PHDemo/DemoCapsuleVC.swift @@ -189,8 +189,8 @@ class DemoCapsuleVC: ListVC { section.add(title: "指定id=a, haha") { Capsule(.identifier("a").message("id=a, haha")) } - section.add(title: "指定id=a, hahaha") { - Capsule(.identifier("a").message("id=a, hahaha")) + section.add(title: "指定id=a, hahahahahaha") { + Capsule(.identifier("a").message("id=a, hahahahahaha")) } } diff --git a/PHDemo/PHDemo/DemoToastVC.swift b/PHDemo/PHDemo/DemoToastVC.swift index 0c7157a..fc5eeea 100644 --- a/PHDemo/PHDemo/DemoToastVC.swift +++ b/PHDemo/PHDemo/DemoToastVC.swift @@ -233,7 +233,7 @@ class DemoToastVC: ListVC { } section.add(title: "不存在就创建,存在就更新") { i += 1 - Toast(.identifier("loading").title("正在加载\(i)").message("这条消息不会重复显示多条")) + Toast(.identifier("loading").title("正在加载\(i)").message("这条消息\n不会重复显示多条")) } section.add(title: "如果存在就更新,如果不存在就忽略") { i += 1 diff --git a/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift b/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift index ac77829..75f15d4 100644 --- a/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift +++ b/Sources/ProHUD/Capsule/CapsuleDefaultLayout.swift @@ -25,7 +25,9 @@ extension CapsuleTarget: DefaultLayout { loadContentMaskViewIfNeeded() // text - contentStack.addArrangedSubview(textLabel) + if contentStack.arrangedSubviews.contains(textLabel) == false { + contentStack.addArrangedSubview(textLabel) + } var text = [vm?.title ?? "", vm?.message ?? ""].filter({ $0.count > 0 }).joined(separator: " ") if text.count > 0 { textLabel.snp.remakeConstraints { make in @@ -47,6 +49,7 @@ extension CapsuleTarget: DefaultLayout { UIView.animateEaseOut(duration: config.animateDurationForReloadByDefault) { self.view.layoutIfNeeded() } + updateWindowSize() } } @@ -88,7 +91,9 @@ extension CapsuleTarget: DefaultLayout { if vm?.icon == nil && vm?.iconURL == nil { contentStack.removeArrangedSubview(imageView) } else { - contentStack.insertArrangedSubview(imageView, at: 0) + if contentStack.arrangedSubviews.contains(imageView) == false { + contentStack.insertArrangedSubview(imageView, at: 0) + } if config.iconSizeByDefault != .zero { imageView.snp.remakeConstraints { make in make.height.equalTo(config.iconSizeByDefault) @@ -106,4 +111,47 @@ extension CapsuleTarget: DefaultLayout { } + func getWindowSize(window: CapsuleWindow) -> CGSize { + let cardEdgeInsetsByDefault = config.cardEdgeInsetsByDefault + view.layoutIfNeeded() + var size = contentStack.frame.size + let width = min(config.cardMaxWidthByDefault, size.width + cardEdgeInsetsByDefault.left + cardEdgeInsetsByDefault.right) + let height = min(config.cardMaxHeightByDefault, size.height + cardEdgeInsetsByDefault.top + cardEdgeInsetsByDefault.bottom) + return .init(width: width, height: max(height, config.cardMinHeight)) + } + + func getWindowFrame(size: CGSize) -> CGRect { + // 应用到frame + let newFrame: CGRect + switch vm?.position { + case .top, .none: + let topLayoutMargins = AppContext.appWindow?.safeAreaInsets.top ?? 8 + let y = max(topLayoutMargins, 8) + newFrame = .init(x: (AppContext.appBounds.width - size.width) / 2, y: y, width: size.width, height: size.height) + case .middle: + newFrame = .init(x: (AppContext.appBounds.width - size.width) / 2, y: (AppContext.appBounds.height - size.height) / 2 - 20, width: size.width, height: size.height) + case .bottom: + let bottomLayoutMargins = AppContext.appWindow?.layoutMargins.bottom ?? 8 + let y = AppContext.appBounds.height - bottomLayoutMargins - size.height - 60 + newFrame = .init(x: (AppContext.appBounds.width - size.width) / 2, y: y, width: size.width, height: size.height) + } + return newFrame + } + + func updateWindowSize() { + guard let window = view.window as? CapsuleWindow else { return } + window.isUserInteractionEnabled = tapActionCallback != nil + let size = getWindowSize(window: window) + let newFrame = getWindowFrame(size: size) + window.transform = .identity + view.layoutIfNeeded() + UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut) { + window.frame = newFrame + window.layoutIfNeeded() + } + if vm?.position == .top { + ToastWindow.updateToastWindowsLayout() + } + } + } diff --git a/Sources/ProHUD/Capsule/CapsuleManager.swift b/Sources/ProHUD/Capsule/CapsuleManager.swift index 37fabce..a33e01b 100644 --- a/Sources/ProHUD/Capsule/CapsuleManager.swift +++ b/Sources/ProHUD/Capsule/CapsuleManager.swift @@ -43,30 +43,11 @@ extension CapsuleTarget { window = CapsuleWindow(capsule: self) windows[position] = nil } + window.isUserInteractionEnabled = tapActionCallback != nil - // frame - let cardEdgeInsetsByDefault = config.cardEdgeInsetsByDefault - view.layoutIfNeeded() - var size = contentStack.frame.size - let width = min(config.cardMaxWidthByDefault, size.width + cardEdgeInsetsByDefault.left + cardEdgeInsetsByDefault.right) - let height = min(config.cardMaxHeightByDefault, size.height + cardEdgeInsetsByDefault.top + cardEdgeInsetsByDefault.bottom) - size = CGSize(width: width, height: max(height, config.cardMinHeight)) - - // 应用到frame - let newFrame: CGRect - switch vm?.position { - case .top, .none: - let topLayoutMargins = AppContext.appWindow?.safeAreaInsets.top ?? 8 - let y = max(topLayoutMargins, 8) - newFrame = .init(x: (AppContext.appBounds.width - size.width) / 2, y: y, width: size.width, height: size.height) - case .middle: - newFrame = .init(x: (AppContext.appBounds.width - size.width) / 2, y: (AppContext.appBounds.height - size.height) / 2 - 20, width: size.width, height: size.height) - case .bottom: - let bottomLayoutMargins = AppContext.appWindow?.layoutMargins.bottom ?? 8 - let y = AppContext.appBounds.height - bottomLayoutMargins - size.height - 60 - newFrame = .init(x: (AppContext.appBounds.width - size.width) / 2, y: y, width: size.width, height: size.height) - } + let size = getWindowSize(window: window) + let newFrame = getWindowFrame(size: size) window.transform = .identity if isNew { window.frame = newFrame diff --git a/Sources/ProHUD/Toast/ToastDefaultLayout.swift b/Sources/ProHUD/Toast/ToastDefaultLayout.swift index b721493..5b331cf 100644 --- a/Sources/ProHUD/Toast/ToastDefaultLayout.swift +++ b/Sources/ProHUD/Toast/ToastDefaultLayout.swift @@ -87,13 +87,14 @@ extension ToastTarget: DefaultLayout { bodyLabel.text = vm?.message view.layoutIfNeeded() + setupImageView() + if isViewAppeared { // 更新时间 updateTimeoutDuration() + updateWindowSize() } - setupImageView() - } func loadContentViewIfNeeded() { @@ -155,4 +156,55 @@ extension ToastTarget { } + func getWindowSize(window: ToastWindow) -> CGSize { + let cardEdgeInsets = config.cardEdgeInsetsByDefault + let width = CGFloat.minimum(AppContext.appBounds.width - config.marginX - config.marginX, config.cardMaxWidthByDefault) + view.frame.size = CGSize(width: width, height: config.cardMaxHeightByDefault) // 以最大高度开始布局,然后计算实际需要高度 + titleLabel.sizeToFit() + bodyLabel.sizeToFit() + view.layoutIfNeeded() + // 更新子视图之后获取正确的高度 + let height = calcHeight() + let size = CGSize(width: width, height: height) + view.frame.size = size + view.layoutIfNeeded() + return size + } + + func updateWindowSize() { + guard let window = view.window as? ToastWindow else { return } + let lastSize = view.frame.size + let newSize = getWindowSize(window: window) + view.frame.size = lastSize + view.layoutIfNeeded() + window.frame.size = newSize + ToastWindow.updateToastWindowsLayout() + } + + private func calcHeight() -> CGFloat { + var height = CGFloat(0) + for v in infoStack.arrangedSubviews { + // 图片或者文本最大高度 + height = CGFloat.maximum(v.frame.maxY, height) + } + if actionStack.arrangedSubviews.count > 0 { + height += actionStack.frame.height + contentStack.spacing + } + contentView.subviews.filter { v in + if v == contentMaskView { + return false + } + if v == contentStack { + return false + } + return true + } .forEach { v in + height = CGFloat.maximum(v.frame.maxY, height) + } + // 上下边间距 + let cardEdgeInsets = config.cardEdgeInsetsByDefault + height += cardEdgeInsets.top + cardEdgeInsets.bottom + return height + } + } diff --git a/Sources/ProHUD/Toast/ToastManager.swift b/Sources/ProHUD/Toast/ToastManager.swift index 1bf4e7b..4cf9e65 100644 --- a/Sources/ProHUD/Toast/ToastManager.swift +++ b/Sources/ProHUD/Toast/ToastManager.swift @@ -9,32 +9,6 @@ import UIKit extension ToastTarget { - private func calcHeight() -> CGFloat { - var height = CGFloat(0) - for v in infoStack.arrangedSubviews { - // 图片或者文本最大高度 - height = CGFloat.maximum(v.frame.maxY, height) - } - if actionStack.arrangedSubviews.count > 0 { - height += actionStack.frame.height + contentStack.spacing - } - contentView.subviews.filter { v in - if v == contentMaskView { - return false - } - if v == contentStack { - return false - } - return true - } .forEach { v in - height = CGFloat.maximum(v.frame.maxY, height) - } - // 上下边间距 - let cardEdgeInsets = config.cardEdgeInsetsByDefault - height += cardEdgeInsets.top + cardEdgeInsets.bottom - return height - } - @objc open func push() { guard ToastConfiguration.isEnabled else { return } let isNew: Bool @@ -49,23 +23,14 @@ extension ToastTarget { isNew = true } - // frame - let cardEdgeInsets = config.cardEdgeInsetsByDefault - let width = CGFloat.minimum(AppContext.appBounds.width - config.marginX - config.marginX, config.cardMaxWidthByDefault) - view.frame.size = CGSize(width: width, height: config.cardMaxHeightByDefault) - titleLabel.sizeToFit() - bodyLabel.sizeToFit() - view.layoutIfNeeded() - // 更新子视图之后获取正确的高度 - let height = calcHeight() - view.frame.size = CGSize(width: width, height: height) - // 应用到frame - window.frame = CGRect(x: (AppContext.appBounds.width - width) / 2, y: 0, width: width, height: height) - window.rootViewController = self // 此时toast.view.frame.size会自动更新为window.frame.size if windows.contains(window) == false { windows.append(window) setContextWindows(windows) } + let size = getWindowSize(window: window) + window.frame = CGRect(x: (AppContext.appBounds.width - size.width) / 2, y: 0, width: size.width, height: size.height) + window.rootViewController = self // 此时toast.view.frame.size会自动更新为window.frame.size + ToastWindow.updateToastWindowsLayout(windows: windows) // 为了更连贯,从进入动画开始时就开始计时 updateTimeoutDuration()