ModalKit

Platform Swift 5.0 iOS 13.0+ SwiftPM MIT

ModalKit 是一个简单且灵活的框架,用于管理 iOS 中的模态视图呈现。它支持自定义动画、可配置的呈现尺寸和交互手势。ModalKit 简化了创建动态和用户友好的模态界面的过程。

📖 文档

⚠️重要提示
当前版本仍在开发中。在 1.0 版本之前,版本更新可能会有破坏性变更。

特性

用法

基本用法

使用 ModalKit 呈现一个视图控制器

使用 presentModal 扩展
import ModalKit

let viewControllerToPresent = MyViewController()
presentModal(viewControllerToPresent)
直接设置 ModalKit
import ModalKit

let viewControllerToPresent = MyViewController()
viewControllerToPresent.modalPresentationStyle = .custom
viewControllerToPresent.transitioningDelegate = MKPresentationManager()
present(viewControllerToPresent, animated: true)
关闭模态视图

以编程方式关闭模态视图

self.dismiss(animated: true, completion: nil)

呈现尺寸

ModalKit 支持多种呈现尺寸,可以使用 MKPresentationSize 枚举来定义

您可以在 preferredPresentationSize 中提供多个选项。模态可以吸附到任何合适的尺寸。

示例

class MyViewController: MKPresentable {
    var preferredPresentationSize: [MKPresentationSize] {
        return [.small, .intrinsicHeight, .large]
}

转换到新的呈现尺寸

您也可以通过编程方式将模态视图转换到不同的尺寸

self.transition(to: .medium)

此方法使模态视图以动画形式过渡到新的尺寸,同时遵守 MKPresentationSize 定义的约束。这对于需要根据用户交互或内容更改动态调整的模态视图特别有用。

MKPresentable

视图控制器可以遵循 MKPresentable 协议来自定义它们的呈现方式。此协议使您可以精细控制

下面是一个基本遵循的示例,它还演示了您可以重写的可选方法,以完全控制模态视图的行为,包括过渡和滚动行为

extension MyViewController: MKPresentable {

    // Defines the possible sizes for the presented view controller (e.g., medium & large).
    var preferredPresentationSize: [MKPresentationSize] {
        return [.medium, .large]
    }

    // If your view contains a scroll view (like a UITableView), provide it here.
    // ModalKit will observe its offset to seamlessly hand off between scrolling and dragging the modal.
    var scrollView: UIScrollView? {
        return myTableView
    }

    // Called once before the modal is presented. Use this to configure drag indicators,
    // corner radius, and other presentation options.
    func configure(_ configuration: inout MKPresentableConfiguration) {
        configuration.showDragIndicator = true
        configuration.hasRoundedCorners = true
    }

    // Called when the dimming view is tapped.
    // By default, this dismisses the modal. Override for custom behavior.
    func onDimmingViewTap() {
        dismiss(animated: true)
    }
    
    // Return false if you want to disallow certain drag gestures (e.g., under some conditions).
    // By default, this returns true, letting the modal's pan gesture proceed.
    func shouldContinue(with gestureRecognizer: UIPanGestureRecognizer) -> Bool {
        return true
    }

    // Called right before the modal tries to switch to a new size (e.g., from .small to .medium).
    // Return false to block that transition. By default, this returns true.
    func shouldTransition(to size: MKPresentationSize) -> Bool {
        return true
    }

    // Called immediately before the modal begins resizing to the new size.
    // Use this to update your UI (e.g., hiding certain subviews).
    func willTransition(to size: MKPresentationSize) {
        // e.g., hide a toolbar
    }

    // Called after the modal has finished transitioning to the new size.
    // Use this to finalize any layout changes or re-display elements.
    func didTransition(to size: MKPresentationSize) {
        // e.g., show the toolbar again
    }
}

动态布局更新

ModalKit 提供了在运行时处理布局更改的方法,确保模态视图在内容或约束更改时正确调整。这对于动态内容(例如更新的文本、添加的视图或大小要求更改)特别有用。

强制布局更新

如果模态视图的布局受到内容更改或外部更新的影响,您可以手动触发重新计算,以确保模态视图调整到新的尺寸。通过调用 presentationLayoutIfNeeded() 方法,模态视图会更新其布局,并根据 preferredPresentationSize 或其他定义的尺寸配置以动画形式移动到正确的位置

self.updateUI()
self.presentationLayoutIfNeeded()

MKPresentableConfiguration

MKPresentableConfiguration 提供了多种选项

选项 描述
showDragIndicator 指示是否应在模态视图的顶部显示拖动指示器。默认值:false
dragIndicatorColor 拖动指示器的颜色(如果已启用)。默认值:.label
dragIndicatorSize 拖动指示器的大小(如果已显示)。默认值:CGSize(width: 40, height: 5)
dismissibleScale 自动关闭视图控制器的缩放比例。默认值:0.6
isDismissable 确定是否可以通过拖动关闭模态视图。默认值:true
dragResistance 指定模态视图对拖动手势的阻力,范围从 0.0(无阻力)到 1.0(完全阻力)。默认值:0
hasRoundedCorners 指示模态视图是否应具有圆角。默认值:false

滚动视图集成

ModalKit 提供对 UIScrollView 及其子类(例如 UITableViewUICollectionView)的内置支持,允许滚动和模态手势之间进行无缝交互。ModalKit 确保在滚动内容与用于调整大小或关闭模态视图的拖动手势重叠时平滑过渡。

示例

要启用滚动视图集成,请实现 MKPresentable 协议并在 scrollView 属性中返回您的滚动视图

class MyViewController: MKPresentable {
    var scrollView: UIScrollView? {
        return myTableView
    }
}

滚动和拖动之间的切换:当滚动视图位于其内容的顶部或底部时,拖动手势会无缝过渡到调整大小或关闭模态视图。ModalKit 观察滚动视图的内容偏移量,以确定何时允许拖动或保持滚动行为。

如果需要自定义滚动视图和模态视图之间的交互,可以在 MKPresentable 协议中重写 shouldContinue(with:) 方法

class MyViewController: MKPresentable {
    var scrollView: UIScrollView? {
        return myTableView
    }

    func shouldContinue(with gestureRecognizer: UIPanGestureRecognizer) -> Bool {
        // Custom logic to determine if dragging should continue
        return true
    }
}

TabBar 集成

ModalKit 也能很好地与 UITabBarController 配合使用,确保模态视图呈现在标签栏之上,而不会干扰其功能。ModalKit` 自动处理安全区域插图,并调整模态视图的布局以避免与标签栏重叠。

示例

class MyTabBarViewController: UITabBarController, MKPresentable {
    /// The preferred presentation size for the currently selected view controller.
    var preferredPresentationSize: [MKPresentationSize] {
        if let vc = selectedViewController as? MKPresentable {
            return vc.preferredPresentationSize
        }
        return [.large]
    }
}

导航控制器集成

ModalKit 与 UINavigationController 完全兼容,允许在导航堆栈中呈现模态视图。它还支持推送的视图控制器和模态呈现的视图之间的平滑过渡。

示例

class MyNavigationViewController: UINavigationController, UINavigationControllerDelegate, MKPresentable {
    /// Returns the preferred presentation size of the top view controller if it conforms to `MKPresentable`,
    /// otherwise defaults to `.large`.
    var preferredPresentationSize: [MKPresentationSize] {
        if let vc = topViewController as? MKPresentable {
            return vc.preferredPresentationSize
        }
        return [.large]
    }

    override var preferredStatusBarStyle: UIStatusBarStyle { .lightContent }

    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        guard !navigationController.isBeingPresented else { return }
        /// Ensures the presentation layout is updated when transitioning between view controllers.
        presentationLayoutIfNeeded()
    }
}

示例

ModalKitExample 项目提供了各种用法示例。这些示例展示了如何为不同的用例实现和自定义 ModalKit。请浏览 ModalKitExample 项目以获取更多详细信息。

要求

安装

Swift Package Manager

要使用 Swift Package Manager 将 ModalKit 集成到您的项目中,请将以下内容添加到您的 Package.swift 文件中

dependencies: [
    .package(url: "https://github.com/emrearmagan/ModalKit.git")
]

手动安装

  1. 从最新版本下载 ModalKit.zip 并将其内容解压缩到您的项目文件夹中。
  2. 从 Xcode 项目中,从“文件”菜单中选择“添加文件到 ...”,然后添加提取的文件。

贡献

欢迎贡献!如果您想贡献,请在 GitHub 上打开一个 pull request 或 issue。

许可证

ModalKit 在 MIT 许可证下可用。有关更多信息,请参见 LICENSE 文件。