XLActionController
XLActionController 是一个可扩展的库,可以快速创建任何自定义操作表控制器。
示例
上面显示的操作表控制器完全使用 XLActionController 创建,并包含在示例中。要运行示例项目:克隆 XLActionController 仓库,打开 XLActionController 工作空间并运行示例项目。
下面的代码片段显示了如何呈现 Tweetbot 操作表控制器
let actionController = TweetbotActionController()
actionController.addAction(Action("View Details", style: .default, handler: { action in
// do something useful
}))
actionController.addAction(Action("View Retweets", style: .default, handler: { action in
// do something useful
}))
actionController.addAction(Action("View in Favstar", style: .default, handler: { action in
// do something useful
}))
actionController.addAction(Action("Translate", style: .default, executeImmediatelyOnTouch: true, handler: { action in
// do something useful
}))
actionController.addSection(Section())
actionController.addAction(Action("Cancel", style: .cancel, handler:nil))
present(actionController, animated: true, completion: nil)
如您所见,库的使用方式与 UIAlertController 非常相似。
在从屏幕上关闭警告控制器后执行动作句柄。如果您想改变这个,可以将 true
传递到动作的构造函数中的 executeImmediatelyOnTouch
参数。
背后,XLActionController 使用 UICollectionView 显示操作表。
用法
首先创建一个自定义操作表视图控制器,通过扩展 ActionController
泛型类。有关如何创建自定义操作表控制器的信息,请参阅可扩展性部分。
例如,假设我们已创建了一个 TwitterActionController
。
// Instantiate custom action sheet controller
let actionSheet = TwitterActionController()
// set up a header title
actionSheet.headerData = "Accounts"
// Add some actions, note that the first parameter of `Action` initializer is `ActionData`.
actionSheet.addAction(Action(ActionData(title: "Xmartlabs", subtitle: "@xmartlabs", image: UIImage(named: "tw-xmartlabs")!), style: .default, handler: { action in
// do something useful
}))
actionSheet.addAction(Action(ActionData(title: "Miguel", subtitle: "@remer88", image: UIImage(named: "tw-remer")!), style: .default, handler: { action in
// do something useful
}))
// present actionSheet like any other view controller
present(actionSheet, animated: true, completion: nil)
如以上代码所示,与 UIAlertController API 相比,没有相关差异。
主要区别在于,XLActionController 可以与任何头数据类型一起工作,而不仅仅是标准的 UIAlertController title
和 message
属性。同样地,XLActionController 的 Action 可以与任何数据类型一起工作,而不仅仅是 title
字符串。
// XLActionController:
xlActionController.headerData = SpotifyHeaderData(title: "The Fast And The Furious Soundtrack Collection", subtitle: "Various Artists", image: UIImage(named: "sp-header-icon")!)
// vs UIAlertController:
uiActionController.title = "The Fast And The Furious Soundtrack Collection" // no way to pass an image
uiActionController.message = "Various Artists"
// XLActionController:
let xlAction = Action(ActionData(title: "Save Full Album", image: UIImage(named: "sp-add-icon")!), style: .default, handler: { action in })
// notice that we are able to pass an image in addition to the title
xlActionController.addAction(xlAction)
// vs UIAlertController:
let uiAction = UIAlertAction(title: "Xmartlabs", style: .default, handler: { action in }))
uiActionController.addAction(uiAction)
这可以做到是因为 XLActionController 是一种通用类型。
另一个重要区别是,XLActionController 提供了一种方法,如以下代码所示,来添加动作部分
actionController.addSection(Section())
并且每个部分都有一个 data
属性。这个属性是通用的,因此它可以保存任何类型。这些数据将用于创建本部分的头部视图。
let section = actionController.addSection(Section())
section.data = "String" // assuming section data Type is String
每个部分包含一组操作。我们通常使用部分来显示在操作集合上方的头部视图。
可扩展性
ActionController 使用 UICollectionView 在屏幕上显示操作和头部。操作将作为 UICollectionViewCell 的实例进行渲染。您可以通过在动作控制器声明中指定它来使用您自己的 UICollectionViewCell 子类。此外,ActionController 允许您指定全局头部和部分头部。头部作为集合视图的补充视图显示。
ActionController
类是一种通用类型,适用于任何单元格、头部、部分头部类型及其关联的数据类型。
创建您的自定义动作表控制器
XLActionController 提供扩展点,以便指定自定义表控制器的新外观和感觉,并调整呈现和消失动画。让我们看一个例子
// As first step we should extend the ActionController generic type
public class PeriscopeActionController: ActionController<PeriscopeCell, String, PeriscopeHeader, String, UICollectionReusableView, Void> {
// override init in order to customize behavior and animations
public override init(nibName nibNameOrNil: String? = nil, bundle nibBundleOrNil: Bundle? = nil) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
// customizing behavior and present/dismiss animations
settings.behavior.hideOnScrollDown = false
settings.animation.scale = nil
settings.animation.present.duration = 0.6
settings.animation.dismiss.duration = 0.5
settings.animation.dismiss.options = .curveEaseIn
settings.animation.dismiss.offset = 30
// providing a specific collection view cell which will be used to display each action, height parameter expects a block that returns the cell height for a particular action.
cellSpec = .nibFile(nibName: "PeriscopeCell", bundle: Bundle(for: PeriscopeCell.self), height: { _ in 60})
// providing a specific view that will render each section header.
sectionHeaderSpec = .cellClass(height: { _ in 5 })
// providing a specific view that will render the action sheet header. We calculate its height according the text that should be displayed.
headerSpec = .cellClass(height: { [weak self] (headerData: String) in
guard let me = self else { return 0 }
let label = UILabel(frame: CGRect(x: 0, y: 0, width: me.view.frame.width - 40, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.font = .systemFontOfSize(17.0)
label.text = headerData
label.sizeToFit()
return label.frame.size.height + 20
})
// once we specify the views, we have to provide three blocks that will be used to set up these views.
// block used to setup the header. Header view and the header are passed as block parameters
onConfigureHeader = { [weak self] header, headerData in
guard let me = self else { return }
header.label.frame = CGRect(x: 0, y: 0, width: me.view.frame.size.width - 40, height: CGFloat.greatestFiniteMagnitude)
header.label.text = headerData
header.label.sizeToFit()
header.label.center = CGPoint(x: header.frame.size.width / 2, y: header.frame.size.height / 2)
}
// block used to setup the section header
onConfigureSectionHeader = { sectionHeader, sectionHeaderData in
sectionHeader.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
}
// block used to setup the collection view cell
onConfigureCellForAction = { [weak self] cell, action, indexPath in
cell.setup(action.data, detail: nil, image: nil)
cell.separatorView?.isHidden = indexPath.item == self!.collectionView.numberOfItems(inSection: indexPath.section) - 1
cell.alpha = action.enabled ? 1.0 : 0.5
cell.actionTitleLabel?.textColor = action.style == .destructive ? UIColor(red: 210/255.0, green: 77/255.0, blue: 56/255.0, alpha: 1.0) : UIColor(red: 0.28, green: 0.64, blue: 0.76, alpha: 1.0)
}
}
}
ActionController 类型声明
public class ActionController<ActionViewType: UICollectionViewCell, ActionDataType, HeaderViewType: UICollectionReusableView, HeaderDataType, SectionHeaderViewType: UICollectionReusableView, SectionHeaderDataType>
当扩展 ActionController 时,我们必须指定以下视图类型 ActionViewType、HeaderViewType、SectionHeaderViewType。这些类型是用于渲染操作的单元格类型、用于渲染操作表头部的视图和用于渲染部分头部的视图。
每个视图类型都有其关联的数据:ActionDataType、HeaderDataType、SectionHeaderDataType 分别。
如果您的自定义动作表没有头部视图,我们可以使用
UICollectionReusableView
作为HeaderViewType
,并使用Void
作为HeaderDataType
。如果没有部分头部视图,您可以使用UICollectionReusableView
作为SectionHeaderViewType
,并使用Void
作为SectionHeaderDataType
。
以下代码显示了如何为项目中的示例动作控制器指定这些类型
class PeriscopeActionController: ActionController<PeriscopeCell, String, PeriscopeHeader, String, UICollectionReusableView, Void> { ... } // doesn't need to show a section header
class SpotifyActionController: ActionController<SpotifyCell, ActionData, SpotifyHeaderView, SpotifyHeaderData, UICollectionReusableView, Void> { ... } // doesn't need to show a section header
class TwitterActionController: ActionController<TwitterCell, ActionData, TwitterActionControllerHeader, String, UICollectionReusableView, Void> { ... } // doesn't need to show a section header
class YoutubeActionController: ActionController<YoutubeCell, ActionData, UICollectionReusableView, Void, UICollectionReusableView, Void>
调整默认样式和动画参数
按照上一节所述步骤,您应该能够操作自定义动作控制器。经常遇到的情况是需要进行一些其他定制,例如缩放显示视图、更改状态栏颜色或自定义默认的显示和消失动画。
ActionController
类定义了类型为 ActionSheetControllerSettings
的 settings
属性,以调整所有这些。
UICollectionView的行为
// Indicates if the action controller must be dismissed when the user taps the background view. `true` by default.
settings.behavior.hideOnTap: Bool
// Indicates if the action controller must be dismissed when the user scrolls down the collection view. `true` by default.
settings.behavior.hideOnScrollDown: Bool
// Indicates if the collectionView's scroll is enabled. `false` by default.
settings.behavior.scrollEnabled: Bool
// Controls whether the collection view scroll bounces past the edge of content and back again. `false` by default.
settings.behavior.bounces: Bool
// Indicates if the collection view layout will use UIDynamics to animate its items. `false` by default.
settings.behavior.useDynamics: Bool
// Determines whether the navigation bar is hidden when action controller is being presented. `true` by default
settings.hideCollectionViewBehindCancelView: Bool
UICollectionView样式
// Margins between the collection view and the container view's margins. `0` by default
settings.collectionView.lateralMargin: CGFloat
// Cells height when UIDynamics is used to animate items. `50` by default.
settings.collectionView.cellHeightWhenDynamicsIsUsed: CGFloat
动画设置
包含所有与显示和消失动画相关的属性的格式
// Used to scale the presenting view controller when the action controller is being presented. If `nil` is set, then the presenting view controller won't be scaled. `(0.9, 0.9)` by default.
settings.animation.scale: CGSize? = CGSize(width: 0.9, height: 0.9)
显示动画设置
// damping value for the animation block. `1.0` by default.
settings.animation.present.damping: CGFloat
// delay for the animation block. `0.0` by default.
settings.animation.present.delay: TimeInterval
// Indicates the animation duration. `0.7` by default.
settings.animation.present.duration: TimeInterval
// Used as `springVelocity` for the animation block. `0.0` by default.
settings.animation.present.springVelocity: CGFloat
// Present animation options. `UIViewAnimationOptions.curveEaseOut` by default.
settings.animation.present.options: UIViewAnimationOptions
消失动画设置
// damping value for the animation block. `1.0` by default.
settings.animation.dismiss.damping: CGFloat
// Used as delay for the animation block. `0.0` by default.
settings.animation.dismiss.delay: TimeInterval
// animation duration. `0.7` by default.
settings.animation.dismiss.duration: TimeInterval
// springVelocity for the animation block. `0.0` by default
settings.animation.dismiss.springVelocity: CGFloat
// dismiss animation options. `UIViewAnimationOptions.curveEaseIn` by default
settings.animation.dismiss.options: UIViewAnimationOptions
状态栏样式
// Indicates if the status bar should be visible or hidden when the action controller is visible. Its default value is `true`
settings.statusBar.showStatusBar: Bool
// Determines the style of the device’s status bar when the action controller is visible. `UIStatusBarStyle.LightContent` by default.
settings.statusBar.style: UIStatusBarStyle
// Determines whether the action controller takes over control of status bar appearance from the presenting view controller. `true` by default.
settings.statusBar.modalPresentationCapturesStatusBarAppearance: Bool
取消视图样式
有时我们需要在集合视图下方显示一个取消视图。这是 SpotifyActionController
的一个例子。这些属性与添加到动作控制器中的动作无关,也与具有 .Cancel 风格值的动作无关。
// Indicates if the cancel view is shown. `false` by default.
settings.cancelView.showCancel: Bool
// Cancel view's title. "Cancel" by default.
settings.cancelView.title: String?
// Cancel view's height. `60` by default.
settings.cancelView.height: CGFloat
// Cancel view's background color. `UIColor.black.withAlphaComponent(0.8)` by default.
settings.cancelView.backgroundColor: UIColor
// Indicates if the collection view is partially hidden by the cancelView when it is pulled down.
settings.cancelView.hideCollectionViewBehindCancelView: Bool
高级动画
如果调整以前设置不足以使动画按预期工作,XLActionController 允许您通过重写一些函数来更改显示/消失动画。
显示
open func presentView(_ presentedView: UIView, presentingView: UIView, animationDuration: Double, completion: ((_ completed: Bool) -> Void)?)
上述函数负责生成显示动画。它封装了显示的过程,并调用 onWillPresentView
、performCustomPresentationAnimation
和 onDidPresentView
来允许您更改动画的特定点。
通常我们不需要重写
presentView
函数,因为重写onWillPresentView
、performCustomPresentationAnimation
或onDidPresentView
就足够了。
open func onWillPresentView()
onWillPresentView
在动画块开始之前被调用。这里所做的任何更改都不会被动画化。它旨在设置初始动画属性值。
open func performCustomPresentationAnimation(_ presentedView: UIView, presentingView: UIView)
performCustomPresentationAnimation
在主动画块内部被调用。
open func onDidPresentView()
显示动画完成后,presentView
在完成回调内部从 onDidPresentView
调用。
如果您重写了
presentView
实现则不会调用onWillPresentView
、performCustomPresentationAnimation
或onDidPresentView
。
消失
消失动画可以像显示动画一样进行自定义。
open func dismissView(_ presentedView: UIView, presentingView: UIView, animationDuration: Double, completion: ((_ completed: Bool) -> Void)?)
上面的函数负责执行消失动画。它封装了消失动画的执行方法,并通过调用onWillDismissView
、performCustomDismissingAnimation
和onDidDismissView
来允许您更改动画的特定点。
通常,我们不需要重写
dismissView
方法,因为重写OnWillDismissView、
performCustomDismissingAnimation或
onDidDismissView`就足够了。
open func onWillDismissView()
覆盖onWillDismissView
以在动画开始前执行任何设置。
open func performCustomDismissingAnimation(_ presentedView: UIView, presentingView: UIView)
performCustomDismissingAnimation
函数在主动画块内被调用。
open func onDidDismissView()
消失动画完成后,dismissView
在完成回调内调用onDidDismissView
。
如果覆盖了
dismissView
实现,则不会调用onWillDismissView
、performCustomDismissingAnimation
或onDidDismissView
。
为了展示XLActionController如何简单强大,并给出扩展ActionController的几个示例,我们模拟了Skype、Tweetbot、Twitter、Youtube、Periscope和Spotify动作控制器。
要求
- iOS 9.3+
- Xcode 10.2+
- Swift 5.0+
参与
- 如果您希望做出贡献,请随时提交拉取请求。
- 如果您有功能请求,请打开一个问题。
- 如果您发现错误或需要帮助,请在提交问题前检查旧问题。
如果您在您的应用中使用XLActionController,我们想听听您的看法!在twitter上给我们留言。
安装
CocoaPods安装
CocoaPods 是一个用于 Cocoa 项目的依赖管理器。
在项目的 Podfile 中指定 XLActionController
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
target '<Your App Target>' do
# This will install just the library's core, won't include any examples
pod 'XLActionController'
# Uncomment depending on the examples that you want to install
#pod 'XLActionController/Periscope'
#pod 'XLActionController/Skype'
#pod 'XLActionController/Spotify'
#pod 'XLActionController/Tweetbot'
#pod 'XLActionController/Twitter'
#pod 'XLActionController/Youtube'
end
然后运行以下命令
$ pod install
Carthage
Carthage 是一个简单、去中心化的 Cocoa 依赖管理器。
在项目 Carthage 中指定 XLActionController
github "xmartlabs/XLActionController" ~> 5.1.0
手动作为嵌入式框架
从您的项目根 git 文件夹运行以下命令,通过 git 子模块克隆 XLActionController。
$ git submodule add https://github.com/xmartlabs/XLActionController.git
打开由前面的 git 子模块命令创建的 XLActionController 文件夹,并将 XLActionController.xcodeproj 拖到您应用程序的 Xcode 项目的 Project Navigator 中。
在 Project Navigator 中选择 XLActionController.xcodeproj 并验证部署目标是否与您的应用程序部署目标匹配。
在 Xcode 导航中选择您的项目,然后在侧栏中选择您应用程序的目标。接下来选择“General”选项卡,然后点击“Embedded Binaries”部分下的+按钮。
选择 XLActionController.framework,我们就可以完成了!
作者
许可证
XLActionController 在 MIT 许可证 下发布并由 Xmartlabs SRL 版权所有。