XLActionController 5.1.0

XLActionController 5.1.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2021年2月
SPM支持 SPM

Martin BarretoRemeRMathias Claassen维护。



  • Miguel Revetria 和 Martin Barreto

XLActionController

Build status Platform iOS Swift 5 compatible Carthage compatible CocoaPods compatible License: MIT

XMARTLABS

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 titlemessage 属性。同样地,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 类定义了类型为 ActionSheetControllerSettingssettings 属性,以调整所有这些。

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)?)

上述函数负责生成显示动画。它封装了显示的过程,并调用 onWillPresentViewperformCustomPresentationAnimationonDidPresentView 来允许您更改动画的特定点。

通常我们不需要重写 presentView 函数,因为重写 onWillPresentViewperformCustomPresentationAnimationonDidPresentView 就足够了。

open func onWillPresentView()

onWillPresentView 在动画块开始之前被调用。这里所做的任何更改都不会被动画化。它旨在设置初始动画属性值。

open func performCustomPresentationAnimation(_ presentedView: UIView, presentingView: UIView)

performCustomPresentationAnimation 在主动画块内部被调用。

open func onDidPresentView()

显示动画完成后,presentView 在完成回调内部从 onDidPresentView 调用。

如果您重写了 presentView 实现则不会调用 onWillPresentViewperformCustomPresentationAnimationonDidPresentView

消失

消失动画可以像显示动画一样进行自定义。

open func dismissView(_ presentedView: UIView, presentingView: UIView, animationDuration: Double, completion: ((_ completed: Bool) -> Void)?)

上面的函数负责执行消失动画。它封装了消失动画的执行方法,并通过调用onWillDismissViewperformCustomDismissingAnimationonDidDismissView来允许您更改动画的特定点。

通常,我们不需要重写dismissView方法,因为重写OnWillDismissViewperformCustomDismissingAnimationonDidDismissView`就足够了。

open func onWillDismissView()

覆盖onWillDismissView以在动画开始前执行任何设置。

open func performCustomDismissingAnimation(_ presentedView: UIView, presentingView: UIView)

performCustomDismissingAnimation函数在主动画块内被调用。

open func onDidDismissView()

消失动画完成后,dismissView在完成回调内调用onDidDismissView

如果覆盖了dismissView实现,则不会调用onWillDismissViewperformCustomDismissingAnimationonDidDismissView

为了展示XLActionController如何简单强大,并给出扩展ActionController的几个示例,我们模拟了SkypeTweetbotTwitterYoutubePeriscopeSpotify动作控制器。

要求

  • 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 版权所有。