LNPopupController
LNPopupController
是一个框架,可以将视图控制器作为其他视图控制器的弹出窗口呈现,类似于 Apple Music 和 Podcasts 应用。
对于 SwiftUI,请查看我的 LNPopupUI 库。
在此处查看现代化弹出窗口的外观和感觉的视频 这里 以及经典弹出窗口的外观和感觉视频 这里。
一旦使用内容视图控制器呈现了弹出条,用户可以在弹出窗口的任何地方滑动或点击以呈现内容控制器。完成后,用户可以通过滑动或点击弹出窗口的关闭按钮来关闭弹出窗口。
该框架旨在非常通用并在大多数情况下工作,因此它作为 UIViewController
的类别实现。每个视图控制器都可以在底部视图中呈现弹出条。对于 UITabBarController
子类,默认停靠视图是标签栏。对于 UINavigationController
子类,默认停靠视图是工具栏。对于其他类,弹出条在屏幕底部呈现。视图控制器子类可以提供自己的停靠视图。
在弹出窗口呈现和关闭时,框架正确维护容器控制器的视图及其子控制器的安全区域插图和指南。
弹出窗口的内容是使用与弹出内容视图控制器关联的弹出项目对象(LNPopupItem
类的实例)动态构建的。因此,要更改弹出窗口的内容,您必须因此配置视图控制器的弹出项目。
通常,建议在最外层容器控制器上呈现弹出窗口。因此,如果您有一个包含在导航控制器中的视图控制器,该导航控制器又包含在标签栏控制器中,建议在标签栏控制器上呈现弹出窗口。
请查看示例项目,以获取在各种场景中框架的许多常见用例。它包含 Swift 和 Objective-C 中的示例。
特性
- 支持 iOS 11 及更高版本,可作为 Xcode 框架或 SPM 包使用
- 是现代 UIKit 世界中的良好市民
- 现代 Objective-C 语法和出色的 Swift 互操作性
- 对于 SwiftUI,请查看我的 LNPopupUI 库。
添加到项目中
Swift 包管理器
LNPopupController 支持版本 5.1.0 及以上 SPM。要使用 SPM,您应该使用 Xcode 11 打开项目。点击 文件
-> Swift 包
-> 添加包依赖
,输入 https://github.com/LeoNatan/LNPopupController
。选择您希望使用的版本。
您也可以手动将包添加到您的 Package.swift 文件中
.package(url: "https://github.com/LeoNatan/LNPopupController.git", from: "2.9.2")
以及目标中的依赖项
.target(name: "BestExampleApp", dependencies: ["LNPopupController"]),
Carthage
将以下内容添加到您的 Cartfile 中
github "LeoNatan/LNPopupController"
请确保您遵循 Carthage 集成说明 在这里。
手动
将 LNPopupController.xcodeproj
项目拖放到您的项目中,并将 LNPopupController.framework
添加到项目目标 通用 选项卡中的 嵌入的二进制文件。Xcode 应该会自行整理其余内容。
CocoaPods
CocoaPods 不受支持。这有几个原因。不要使用 CocoaPods,而是使用 Carthage。您可以为其他依赖项继续使用 CocoaPods,而为 LNPopupController
使用 Carthage。
使用框架
Swift
尽管该框架是用 Objective C 编写的,但它使用现代 Objective C 语法,因此使用 Swift 中的框架应该是非常简单直观的。
项目集成
在您的项目中导入模块
import LNPopupController
弹出项
弹出项应始终反映与相关联的视图控制器中的弹出信息。弹出项应提供要显示在弹出条中的标题和副标题,当视图控制器作为弹出内容控制器呈现时。此外,项目还可能包含额外的按钮,用于在弹出条的leading和/或trailing边缘显示,可以使用 `leadingBarButtonItems` 和 `trailingBarButtonItems` 实现。
管理弹出条
要显示弹出条,创建内容控制器、更新其弹出项并显示弹出条。
let demoVC = DemoPopupContentViewController()
demoVC.view.backgroundColor = .red
demoVC.popupItem.title = "Hello World"
demoVC.popupItem.subtitle = "And a subtitle!"
demoVC.popupItem.progress = 0.34
tabBarController?.presentPopupBar(withContentViewController: demoVC, animated: true, completion: nil)
当弹出条正在显示并且弹出本身打开时,可以呈现新的内容控制器。
要程序化地打开和关闭弹出,分别使用 openPopup(animated:completion:)
和 closePopup(animated:completion:)
。
tabBarController?.openPopup(animated: true, completion: nil)
或者,可以使用 presentPopupBar(withContentViewController:openPopup:animated:completion:)
在一个动画中展示弹出条并打开弹出。
tabBarController?.presentPopupBar(withContentViewController: demoVC, openPopup:true, animated: true, completion: nil)
要消失弹出条,使用 dismissPopupBarAnimated:completion:
。
tabBarController?.dismissPopupBar(animated: true, completion: nil)
如果在消失弹出条时弹出是打开的,弹出内容也会消失。
弹出视图控制器容器
任何 UIViewController
子类都可以是弹出视图控制器容器。弹出条连接到底部锚定视图。默认情况下,UITabBarController
和 UINavigationController
的子类返回它们的底部 条作为锚定视图;而其他控制器将在视图底部返回一个高度为 0pt 的隐藏视图。在你的子类中,覆盖 bottomDockingViewForPopupBar
和 defaultFrameForBottomDockingView
并相应地返回你的视图和框架。 返回的视图必须连接到视图控制器的底部视图,否则结果是不确定的。
override var bottomDockingViewForPopupBar: UIView? {
return myCoolBottomView
}
override var defaultFrameForBottomDockingView: CGRect {
var bottomViewFrame = myCoolBottomView.frame
if isMyCoolBottomViewHidden {
bottomViewFrame.origin = CGPoint(x: bottomViewFrame.x, y: view.bounds.height)
} else {
bottomViewFrame.origin = CGPoint(x: bottomViewFrame.x, y: view.bounds.height - bottomViewFrame.height)
}
return bottomViewFrame
}
外观和行为
现代外观和感觉
LNPopupController
提供两种不同的弹出窗口样式和外观,一种是基于现代音乐应用程序的样式和外观,另一种是之前基于 iOS 9 样式和外观的。弹出栏样式被任意标记为“显眼”以用于现代样式弹出栏,而“紧凑”用于 iOS 9 样式。弹出交互样式被标记为“快速”以用于现代样式的快速弹出,而“拖动”用于 iOS 9 的互动弹出交互。弹出关闭按钮的样式被标记为“箭头”以用于现代样式的箭头关闭按钮,而“圆形”用于 iOS 9 样式关闭按钮。对于每种样式,都有一个“默认”样式,用于选择当前操作系统版本最合适的样式。
默认设置包括
- 显眼的栏样式
- 快速交互样式
- 箭头关闭按钮样式
- 无进度视图样式
栏样式
通过设置弹出栏的 barStyle
属性来实现自定义弹出栏样式。
navigationController?.popupBar.barStyle = .compact
交互样式
通过设置包含控制器的弹出表示的 popupInteractionStyle
属性来实现自定义弹出交互样式。
navigationController?.popupInteractionStyle = .drag
进度视图样式
通过设置弹出栏的 progressViewStyle
属性来实现自定义弹出栏进度视图样式。
navigationController?.popupBar.progressViewStyle = .top
要隐藏进度视图,将 progressViewStyle
属性设置为 LNPopupBarProgressViewStyle.none
。
关闭按钮样式
通过设置弹出内容视图的 popupCloseButtonStyle
属性来实现自定义弹出关闭按钮样式。
navigationController.popupContentView.popupCloseButtonStyle = .round
要隐藏弹出关闭按钮,将 popupCloseButtonStyle
属性设置为 LNPopupCloseButtonStyle.none
。
弹出栏外观
对于导航和标签栏控制器弹出容器,弹出栏的样式根据底部栏的外观确定。对于其他容器控制器,样式是默认样式。对于每种样式,标题和按钮颜色将相应调整。
在更新容器控制器底部栏外观后,要更新弹出栏外观,请调用 updatePopupBarAppearance()
方法。
为标题和/或副标题提供长文本会导致文本滚动。否则,文本将居中。
所有控制器均支持 hidesBottomBarWhenPushed
属性。当设置时,弹出栏将过渡到容器控制器视图的底部。设置 isToolbarHidden = true
并调用 setToolbarHidden(_:animated:)
也是支持的。
尊重并适用时应用弹出内容视图控制器的状态栏管理。
交互手势识别器
LNPopupContentView
以 popupInteractionGestureRecognizer
属性的方式公开了弹出交互手势识别器的访问。这个手势识别器在打开弹出内容、通过向上滑动弹出栏(当弹出栏关闭时)和关闭弹出内容、通过滑动弹出内容视图(当弹出栏打开时)时是共享的。
当打开弹出时,系统会查询弹出内容视图控制器的 viewForPopupInteractionGestureRecognizer
属性以确定要将交互手势识别器添加到哪个视图。默认情况下,该属性返回控制器的根视图。可以覆盖属性的getter以更改此行为。
可以实现交互手势识别器的代理,以影响其行为,例如在用户与弹出内容内部的控件或视图交互时阻止弹出交互。
注意:如果在弹出后禁用手势识别器,必须监控弹出状态,并在用户通过代码关闭时重新启用手势识别器。相反,可以考虑实现手势识别器的代理并提供禁用交互的自定义逻辑。
全面支持从右到左
该框架全面支持从右到左。
默认情况下,弹出条将遵循系统的用户界面布局方向,但会保留条按钮项的顺序。要自定义此行为,请修改弹出条的semanticContentAttribute
和barItemsSemanticContentAttribute
属性。
自定义
可以通过LNPopupBar
、LNPopupContentView
和LNPopupCustomBarViewController
类来实现自定义。
弹出条自定义
LNPopupBar
提供了API来自定义默认弹出条的外观,无论是通过UIAppearance
API还是直接在弹出条对象上。
let appearanceProxy = LNPopupBar.appearance(whenContainedInInstancesOf: [UINavigationController.self])
appearanceProxy.titleTextAttributes = [.font: UIFont(name: "Chalkduster", size: 14)!, .foregroundColor: UIColor.yellow]
appearanceProxy.subtitleTextAttributes = [.font: UIFont(name: "Chalkduster", size: 12)!, .foregroundColor: UIColor.green]
appearanceProxy.backgroundStyle = .systemChromeMaterialDark
appearanceProxy.tintColor = .yellow
自定义弹出条
框架支持实现自定义弹出条。
要实现自定义弹出条,继承LNPopupCustomBarViewController
。
在你的LNPopupCustomBarViewController
子类中,构建弹出条视图层次结构,并使用预期的弹出条高度设置控制器的preferredContentSize
属性。重写wantsDefaultTapGestureRecognizer
和/或wantsDefaultPanGestureRecognizer
属性以禁用添加默认手势识别器。
在你的子类中,实现popupItemDidUpdate
方法以接收弹出内容视图控制器的项目更新通知,或当显示新的弹出内容视图控制器(带有新的弹出条项)时。你必须调用此方法的super
实现。
最后,将弹出条对象的customBarViewController
属性设置为你的LNPopupCustomBarViewController
子类实例。这将更改条样式为LNPopupBarStyle.custom
。
包含的示例项目包含自定义弹出条场景的示例。
无障碍访问
框架支持无障碍访问,并会尊重无障碍标签、提示和值。默认情况下,弹出条的无障碍标签是弹出条项提供的标题和副标题。
要修改弹出条的无障碍标签和提示,请设置弹出内容视图控制器中LNPopupItem
对象的accessibilityLabel
和accessibilityHint
属性。
demoVC.popupItem.accessibilityLabel = NSLocalizedString("Custom popup bar accessibility label", comment: "")
demoVC.popupItem.accessibilityHint = NSLocalizedString("Custom popup bar accessibility hint", comment: "")
为了给按钮添加无障碍标签和提示,请设置 UIBarButtonItem
对象的 accessibilityLabel
和 accessibilityHint
属性。
let upNext = UIBarButtonItem(image: UIImage(named: "next"), style: .plain, target: self, action: #selector(nextItem))
upNext.accessibilityLabel = NSLocalizedString("Up Next", comment: "")
upNext.accessibilityHint = NSLocalizedString("Double tap to show up next list", comment: "")
要修改弹出关闭按钮的无障碍标签和提示,请设置弹出容器视图控制器的 LNPopupCloseButton
对象的 accessibilityLabel
和 accessibilityHint
属性。
tabBarController?.popupContentView.popupCloseButton.accessibilityLabel = NSLocalizedString("Custom popup close button accessibility label", comment: "")
tabBarController?.popupContentView.popupCloseButton.accessibilityHint = NSLocalizedString("Custom popup close button accessibility hint", comment: "")
要修改弹出条进度视图的无障碍标签和值,请设置弹出内容视图控制器的 LNPopupItem
对象的 accessibilityProgressLabel
和 accessibilityProgressValue
属性。
demoVC.popupItem.accessibilityImageLabel = NSLocalizedString("Custom image label", comment: "")
demoVC.popupItem.accessibilityProgressLabel = NSLocalizedString("Custom accessibility progress label", comment: "")
demoVC.popupItem.accessibilityProgressValue = "\(accessibilityDateComponentsFormatter.stringFromTimeInterval(NSTimeInterval(popupItem.progress) * totalTime)!) \(NSLocalizedString("of", comment: "")) \(accessibilityDateComponentsFormatter.stringFromTimeInterval(totalTime)!)"
笔记
- 不支持不透明的条,可能会导致视觉杂乱或布局错误。苹果本身对这种条有很多问题,支持这些不是 LNPopupController 的优先事项。
- 相反,可以使用透明条,为您的条设置背景色而不是将其设置为不透明,或者将
extendedLayoutIncludesOpaqueBars
设置为 true 以包含控制器
- 相反,可以使用透明条,为您的条设置背景色而不是将其设置为不透明,或者将
- 框架或苹果不支持手动隐藏标签栏。请勿使用
tabBar.hidden = YES
来隐藏标签栏。
致谢
框架使用了
- MarqueeLabel 版权 (c) 2011-2020 Charles Powell
此外,演示项目使用了
- LoremIpsum 版权 (c) 2013 Lukas Kubanek
- COSTouchVisualizer 版权 (c) 2014 Joe Blau