SwiftMessages

Twitter: @TimothyMoose Version License Platform Carthage compatible

概述

🔥🔥🔥 新功能 添加了 SwiftUI 支持!

SwiftMessages 是一个非常灵活的视图和视图控制器展示库,适用于 UIKit 和 SwiftUI。

消息视图和视图控制器可以显示在屏幕的顶部、底部或中央,或者在导航栏和标签栏的后面。 有交互式的关闭手势,包括一个有趣的、基于物理的手势。 有多种背景变暗模式。 还有更多功能!

除了大量的配置选项外,SwiftMessages 还提供了几种美观的布局和主题。 但 SwiftMessages 对设计师也很友好,这意味着您可以完全且轻松地自定义视图。

安装

Swift Package Manager

在 Xcode 中,转到 File | Swift Packages | Add Package Dependency... 并搜索 "SwiftMessages"。如果找到多个结果,请选择 SwiftKick Mobile 拥有的那个。

CocoaPods

将以下行添加到您的 Podfile 中

pod 'SwiftMessages'

Carthage

将以下行添加到您的 Cartfile 中

github "SwiftKickMobile/SwiftMessages"

如果 Carthage 构建失败,尝试使用该脚本

手动

  1. 将 SwiftMessages repo 放置在您的项目目录中的某个位置。
  2. 在 Xcode 中,将 SwiftMessages.xcodeproj 添加到您的项目中。
  3. 在应用程序的目标上,添加 SwiftMessages 框架
    1. 作为 General 选项卡上的嵌入式二进制文件。
    2. 作为 Build Phases 选项卡上的目标依赖项。

用法

基础

SwiftMessages.show(view: myView)

虽然您可以显示 UIView 的任何实例,但 SwiftMessages 提供了一个 MessageView 类和基于 nib 的布局集合,应该可以处理大多数情况。

// Instantiate a message view from the provided card view layout. SwiftMessages searches for nib
// files in the main bundle first, so you can easily copy them into your project and make changes.
let view = MessageView.viewFromNib(layout: .cardView)

// Theme message elements with the warning style.
view.configureTheme(.warning)

// Add a drop shadow.
view.configureDropShadow()

// Set message title, body, and icon. Here, we're overriding the default warning
// image with an emoji character.
let iconText = ["🤔", "😳", "🙄", "😶"].randomElement()!
view.configureContent(title: "Warning", body: "Consider yourself warned.", iconText: iconText)

// Increase the external margin around the card. In general, the effect of this setting
// depends on how the given layout is constrained to the layout margins.
view.layoutMarginAdditions = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)

// Reduce the corner radius (applicable to layouts featuring rounded corners).
(view.backgroundView as? CornerRoundingView)?.cornerRadius = 10

// Show the message.
SwiftMessages.show(view: view)

您可能希望使用视图提供程序变体 show(viewProvider:) 以确保您的 UIKit 代码在主队列上执行。

SwiftMessages.show {
    let view = MessageView.viewFromNib(layout: .cardView)
    // ... configure the view
    return view
}

SwiftMessages.Config 结构提供了许多可以传递给 show() 的配置选项。

var config = SwiftMessages.Config()

// Slide up from the bottom.
config.presentationStyle = .bottom

// Display in a window at the specified window level.
config.presentationContext = .window(windowLevel: .statusBar)

Note that, as of iOS 13, it is no longer possible to cover the status bar
regardless of the window level. A workaround is to hide the status bar instead.
config.prefersStatusBarHidden = true

// Disable the default auto-hiding behavior.
config.duration = .forever

// Dim the background like a popover view. Hide when the background is tapped.
config.dimMode = .gray(interactive: true)

// Disable the interactive pan-to-hide gesture.
config.interactiveHide = false

// Specify haptic feedback (see also MessageView/configureTheme)
config.haptic = .success

// Specify a status bar style to if the message is displayed directly under the status bar.
config.preferredStatusBarStyle = .lightContent

// Specify one or more event listeners to respond to show and hide events.
config.eventListeners.append() { event in
    if case .didHide = event {
        print("yep id=\(String(describing: event.id)")
    }
}

SwiftMessages.show(config: config, view: view)

指定默认配置选项

SwiftMessages.defaultConfig.presentationStyle = .bottom

// Show message with default config.
SwiftMessages.show(view: view)

// Customize config using the default as a base.
var config = SwiftMessages.defaultConfig
config.duration = .forever
SwiftMessages.show(config: config, view: view)

视图控制器

SwiftMessages 可以使用 SwiftMessagesSegue 自定义模态 segue 来呈现视图控制器!

SwiftMessagesSegueUIStoryboardSegue 的一个子类,它作为自定义模态 segue 直接集成到 Interface Builder 中,使视图控制器能够利用 SwiftMessages 布局、动画等。 SwiftMessagesSegue 适用于任何 UIKIt 项目——不需要故事板。 有关更多信息,请参阅下面的视图控制器自述文件。

视图控制器自述文件

并查看我们的博客文章 优雅的自定义 UIViewController 转场,以学习一种很棒的技术,您可以使用它来构建自己的自定义 segues,这些 segues 利用 UIViewControllerTransitioningDelegateUIViewControllerAnimatedTransitioning

SwiftUI

可以通过从可观察对象、按钮操作闭包等中调用 SwiftMessages API 来显示任何内置的 SwiftMessages 视图。 但是,SwiftMessages 也可以显示您的自定义 SwiftUI 视图。

采用以下消息视图和配套数据模型

struct DemoMessage: Identifiable {
    let title: String
    let body: String

    var id: String { title + body }
}

struct DemoMessageView: View {

    let message: DemoMessage

    var body: some View {
        VStack(alignment: .leading) {
            Text(message.title).font(.system(size: 20, weight: .bold))
            Text(message.body)
        }
        .multilineTextAlignment(.leading)
        .padding(30)
        // This makes the message width greedy
        .frame(maxWidth: .infinity)
        .background(.gray)
        // This makes a tab-style view where the bottom corners are rounded and
        // the view's background extends to the top edge.
        .mask(
            UnevenRoundedRectangle(bottomLeadingRadius: 15, bottomTrailingRadius: 15)
            // This causes the background to extend into the safe area to the screen edge.
            .edgesIgnoringSafeArea(.top)
        )
    }
}

您可以从按钮操作、视图模型或其他类似上下文中显示它,如下所示

struct DemoView: View {
    var body: some View {
        Button("Show message") {
            let message = DemoMessage(title: "Demo", body: "SwiftUI forever!")
            let messageView = MessageHostingView(id: message.id, content: DemoMessageView(message: message)
            SwiftMessages.show(view: messageView)
        }
    }
}

但是您也可以使用基于状态的方法,使用 swiftMessage() 视图修饰符

struct DemoView: View {

    @State var message: DemoMessage?

    var body: some View {
        Button("Show message") {
            message = DemoMessage(title: "Demo", body: "SwiftUI forever!")
        }
        .swiftMessage(message: $message) { message in
            DemoMessageView(message: message)
        }
    }
}

这与 .sheet() 修饰符非常相似。 但是,它没有公开 SwiftMessages 的所有功能,例如通过 ID 显式隐藏消息。 使用两种方法的组合是完全合理的。

如果您的消息视图纯粹是数据驱动的,不需要委托、回调等,则 swiftMessage() 有一个稍微简化的变体,不需要视图构建器。 相反,您的数据模型应该符合 MessageViewConvertible

extension DemoMessage: MessageViewConvertible {
    func asMessageView() -> DemoMessageView {
        DemoMessageView(message: self)
    }
}

然后,您可以在调用 swiftMessage() 时删除视图构建器

struct DemoView: View {

    @State var message: DemoMessage?

    var body: some View {
        Button("Show message") {
            message = DemoMessage(title: "Demo", body: "SwiftUI forever!")
        }
        .swiftMessage(message: $message)
    }
}

在 SwiftUI 演示应用程序中尝试一下!

辅助功能

SwiftMessages 提供了出色的开箱即用的 VoiceOver 支持。

有关在自定义视图中实现适当的辅助功能支持,请参阅 AccessibleMessage 协议。

键盘规避

KeyboardTrackingView 类可用于使消息视图通过在键盘过于靠近时向上滑动来避开键盘。

var config = SwiftMessages.defaultConfig
config.keyboardTrackingView = KeyboardTrackingView()

即使您未使用 SwiftMessages,也可以将 KeyboardTrackingView 合并到您的应用程序中。 通过将 KeyboardTrackingView 固定到屏幕的底部、前导和尾随边缘,将其安装到您的视图层次结构中。 然后将应避开键盘的内容的底部固定到 KeyboardTrackingView 的顶部。 使用等式约束严格跟踪键盘,或使用不等式约束仅在键盘过于靠近时才移动。 KeyboardTrackingView 通过观察键盘通知并调整其高度以使其顶部边缘保持在键盘上方来工作,从而向上推动您的内容。 有关配置选项,请参阅 KeyboardTrackingView 中的注释。

消息队列

您可以根据需要多次调用 SwiftMessages.show()。 SwiftMessages 维护一个队列并一次显示一条消息。 如果您的视图实现了 Identifiable 协议(例如 MessageView),则会自动删除重复的消息。 可以调整消息之间的暂停

SwiftMessages.pauseBetweenMessages = 1.0

有几种以编程方式隐藏消息的方法

// Hide the current message.
SwiftMessages.hide()

// Or hide the current message and clear the queue.
SwiftMessages.hideAll()

// Or for a view that implements `Identifiable`:
SwiftMessages.hide(id: someId)

// Or hide when the number of calls to show() and hideCounted(id:) for a 
// given message ID are equal. This can be useful for messages that may be
// shown from  multiple code paths to ensure that all paths are ready to hide.
SwiftMessages.hideCounted(id: someId)

可以使用 SwiftMessages 的多个实例来一次显示多条消息。 请注意,静态 SwiftMessages.show()SwiftMessage 上的其他静态 API 只是共享实例 SwiftMessages.sharedInstance 的便捷包装器)。 实例必须保留,因此它应该是某些东西(例如您的视图控制器)的属性

class SomeViewController: UIViewController {
    let otherMessages = SwiftMessages()	
	
    func someMethod() {
        SwiftMessages.show(...)
        otherMessages.show(...)
    }
}

检索消息

有几个 API 可用于检索当前正在显示、隐藏或排队等待显示的消息。 这些 API 对于在发生某些事件时更新消息非常有用,而无需保留临时引用。 另请参阅 eventListeners

// Get a message view with the given ID if it is currently 
// being shown or hidden.
if let view = SwiftMessages.current(id: "some id") { ... }

// Get a message view with the given ID if is it currently 
// queued to be shown. 
if let view = SwiftMessages.queued(id: "some id") { ... }

// Get a message view with the given ID if it is currently being
// shown, hidden or in the queue to be shown.
if let view = SwiftMessages.currentOrQueued(id: "some id") { ... }

自定义

SwiftMessages 可以显示任何 UIView。 但是,可以对捆绑的视图进行不同程度的自定义。

Nib 文件

与 SwiftMessages 捆绑在一起的所有消息设计都有关联的 nib 文件。 鼓励您将这些 nib 文件中的任何一个复制到您的项目中并根据您的需要进行修改。 SwiftMessages 将加载您的文件副本,而不是原始文件。 可以使用拖放操作在 Xcode 中复制 Nib 文件。

为了方便使用基于 nib 的布局,MessageView 提供了一些类型安全的便捷方法来加载捆绑的 nib

let view = MessageView.viewFromNib(layout: .cardView)

此外,SwiftMessages 类提供了一些通用加载方法

// Instantiate MessageView from a named nib.
let view: MessageView = try! SwiftMessages.viewFromNib(named: "MyCustomNib")

// Instantiate MyCustomView from a nib named MyCustomView.nib.
let view: MyCustomView = try! SwiftMessages.viewFromNib()

MessageView 类

MessageView 是所有捆绑设计使用的轻量级视图。 它主要由以下可选的 @IBOutlet 属性组成

元素 声明 描述
标题 titleLabel: UILabel? 消息标题。
消息正文 bodyLabel: UILabel? 消息的正文。
图像图标 iconImageView: UIImageView? 基于图像的图标。
文本图标 iconLabel: UILabel? 图像图标的基于文本(表情符号)的替代方案。
按钮 button: UIButton? 操作按钮。

SwiftMessages nib 文件使用 MessageView 作为顶级视图,其内容连接到这些出口。 布局是使用堆栈视图完成的,这意味着您可以通过简单地隐藏元素来删除元素

view.titleLabel.isHidden = true

一个常见的错误是尝试通过将相应的出口设置为 nil 来删除元素。 这不起作用,因为它不会从视图层次结构中删除元素。

配置

MessageView 提供了许多遵循 configure* 命名约定的方法

view.configureTheme(.warning, includeHaptic: true)
view.configureContent(title: "Warning", body: "Consider yourself warned.", iconText: "🤔")

所有这些方法都是快速配置底层视图属性的快捷方式。 SwiftMessages 努力避免在这些方法中进行任何内部魔法,因此您无需调用它们。 您可以直接配置视图属性或结合使用这两种方法。

互动

MessageView 为按钮提供了一个可选的基于块的点击处理程序,另一个用于视图本身

// Hide when button tapped
messageView.buttonTapHandler = { _ in SwiftMessages.hide() }

// Hide when message view tapped
messageView.tapHandler = { _ in SwiftMessages.hide() }

扩展

MessageView 作为基础并添加新元素(例如其他按钮)的建议方法如下

  1. 将捆绑的 nib 文件之一复制到您的项目中,或从头创建一个新的 nib 文件。
  2. 将新元素添加到 nib 文件。
  3. 子类化 MessageView 并为新元素创建出口。
  4. 将 nib 文件中的顶级视图分配给子类。
  5. 在 nib 文件和子类之间连接出口。
  6. (推荐)根据需要重写 Identifiable 的实现,以将新元素合并到消息的标识中。
  7. (推荐)根据需要重写 AccessibleMessage 的实现,以将新元素合并到 Voice Over 中。
  8. 使用上述 nib 加载方法之一来加载视图。

BaseView 类

BaseViewMessageView 的超类,并提供了许多不特定于 MessageView 的“标题 + 正文 + 图标 + 按钮”设计的选项。 与 MessageView 显着不同的自定义视图(例如进度指示器)应子类化 BaseView

CornerRoundingView 类

CornerRoundingView 是一个自定义视图,消息可以使用它来对全部或部分角进行圆角处理,使用 squircles (在应用图标上看到的更平滑的圆角方法)。具有圆角的 nib 文件将 backgroundView 赋值给 CornerRoundingView。它提供了一个 roundsLeadingCorners 选项,用于在从顶部或底部呈现时动态地仅对视图的前导角进行圆角处理(用于标签样式布局的功能)。

动画器协议

Animator 是 SwiftMessages 用于呈现和消失动画的协议。自定义动画可以通过 SwiftMessages.PresentationStyle.custom(animator:) 完成。一些相关组件:

欢迎提供高质量的 PR,来实现酷炫的 Animator

边距可调协议

MarginAdjustableBaseView 采用的协议。如果被呈现的视图采用 MarginAdjustable,SwiftMessages 将拥有该视图的布局边距,以确保在整个呈现上下文中具有理想的间距。

背景视图协议

BackgroundViewableBaseView 采用的协议,它要求视图提供一个 backgroundView 属性。 BaseView 初始化 backgroundView = self,您可以自由地将其重新分配给任何子视图。

如果被呈现的视图采用 BackgroundViewable,SwiftMessages 将忽略 backgroundView 外部的触摸。这很重要,因为消息视图始终跨越设备的整个宽度。卡片和标签样式布局看起来是从设备的边缘插入的,因为消息视图的背景是透明的,并且 backgroundView 被分配给约束到布局边距的子视图。在这些布局中,应忽略透明边距中的触摸。

可识别协议

IdentifiableMessageView 采用的协议,它要求视图提供一个 id 属性,SwiftMessages 使用该属性进行消息去重。

MessageView 基于消息内容计算 id,但也可以根据需要显式设置 id

辅助消息协议

AccessibleMessageMessageView 采用的协议。如果被呈现的视图采用 AccessibleMessage,SwiftMessages 将提供改进的 Voice Over。

关于 SwiftKick Mobile

我们构建高质量的应用程序!如果您在项目中需要帮助,请联系我们

许可

SwiftMessages 在 MIT 许可下分发。有关详细信息,请参阅 LICENSE