🤗 您可以在此进行捐赠。
SwiftEntryKit 是一个用 Swift 编写的简单而通用的内容呈现器。
横幅或弹出窗口被称为 Entries(条目)。
示例项目包含各种预设和示例,您可以根据自己的喜好使用和修改。
您可以使用终端或像 Source Tree 这样的 Git 客户端。
$ git clone https://github.com/huri000/SwiftEntryKit.git
克隆 https://github.com/huri000/SwiftEntryKit.git
Toasts(吐司) | Notes(通知) | Floats(浮动窗口) | Popups(弹出窗口) |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Alerts(警报) | Forms(表单) | Rating(评分) | 更多... |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
名词:人们可以玩橄榄球的地方 🏈
该示例应用程序包含一个 playground 屏幕,它是一个允许您自定义您喜欢的条目的界面。 playground 屏幕有一些限制(允许选择常量值),但您可以轻松地修改代码以满足您的需求。 快来查看一下!
Playground 屏幕 | 顶部 Toast 示例 |
---|---|
![]() |
![]() |
CocoaPods 是 Cocoa 项目的依赖管理器。 您可以使用以下命令安装它
$ gem install cocoapods
要使用 CocoaPods 将 SwiftEntryKit 集成到您的 Xcode 项目中,请在您的 Podfile
中指定它
source 'https://github.com/cocoapods/specs.git'
platform :ios, '9.0'
use_frameworks!
pod 'SwiftEntryKit', '2.0.0'
然后,运行以下命令
$ pod install
Carthage 是一个去中心化的依赖管理器,它构建您的依赖项并为您提供二进制框架。
您可以使用 Homebrew 使用以下命令安装 Carthage
$ brew update
$ brew install carthage
要使用 Carthage 将 SwiftEntryKit 集成到您的 Xcode 项目中,请在您的 Cartfile
中指定以下内容
github "huri000/SwiftEntryKit" == 2.0.0
Accio 是一个由 SwiftPM 驱动的去中心化依赖管理器,适用于 iOS/tvOS/watchOS/macOS 项目。
您可以使用 Homebrew 使用以下命令安装 Accio
$ brew tap JamitLabs/Accio https://github.com/JamitLabs/Accio.git
$ brew install accio
要使用 Accio 将 SwiftEntryKit 集成到您的 Xcode 项目中,请在您的 Package.swift
清单中指定以下内容
.package(url: "https://github.com/huri000/SwiftEntryKit", .exact("2.0.0"))
在您想要使用的目标中指定 "SwiftEntryKit"
作为依赖项后,运行 accio install
。
无需设置! 每次您希望显示一个条目时,只需创建您的视图并初始化一个 EKAttributes 结构体。 另请参阅预设使用示例和示例项目。 同样如此
// Customized view
let customView = SomeCustomView()
/*
Do some customization on customView
*/
// Attributes struct that describes the display, style, user interaction and animations of customView.
var attributes = EKAttributes()
/*
Adjust preferable attributes
*/
然后,只需调用
SwiftEntryKit.display(entry: customView, using: attributes)
该工具包会将应用程序主窗口替换为 EKWindow 实例并显示该条目。
EKAttributes 是条目的描述符。 每次显示一个条目时,都需要一个 EKAttributes 结构体来描述条目的呈现方式、屏幕内的位置、显示时长、其框架约束(如果需要)、其样式(圆角、边框和阴影)、用户交互事件、动画(进入/退出)等等。
同样创建可变的 EKAttributes 结构
var attributes = EKAttributes()
以下是可以在 EKAttributes 中修改的属性
条目可以有名称。 当实例化 EKAttributes 结构体时,它是无名的,这意味着 name
属性为 nil
。 建议为条目设置一个有意义的名称。
attributes.name = "Top Note"
稍后可以专门引用具有名称的条目,例如,您可以询问当前是否显示了特定条目
if SwiftEntryKit.isCurrentlyDisplaying(entryNamed: "Top Note") {
/* Do your things */
}
Entries 可以显示在应用程序主窗口之上、状态栏之上、警报窗口之上,甚至具有自定义级别 (UIWindowLevel)。
例如,将窗口级别设置为正常,同样如此
attributes.windowLevel = .normal
这会导致条目出现在应用程序主窗口之上和状态栏之下。
windowLevel
的默认值为 .statusBar
。
该条目可以显示在屏幕的顶部、中心或底部。
例如,将显示位置设置为底部,同样如此
attributes.position = .bottom
position
的默认值为 .top
。
条目的优先级属性描述了条目推入的方式。 它提供了两种管理多个同时条目的呈现优先级的方法。
如果显示优先级等于或高于当前显示的条目,则覆盖它。
使用 .max
显示优先级设置 .override
优先级的示例,同时忽略已排队的条目(使其在新条目关闭后显示)。
attributes.precedence = .override(priority: .max, dropEnqueuedEntries: false)
您可以选择性地刷新队列中的条目。
如果 dropEnqueuedEntries
为 false
,则排队的条目将保留在队列中。 第一个排队的条目将在新条目弹出后立即显示。 如果 dropEnqueuedEntries
为 true
,则当显示新条目时,条目队列将被刷新。
如果队列为空,则立即显示该条目,否则,将该条目插入队列,直到轮到它显示。
使用 .normal
显示优先级设置 .enqueue
优先级的示例
attributes.precedence = .enqueue(priority: .normal)
队列中条目优先级排序有两种可能的启发式方法
在使用 SwiftEntryKit
显示条目之前,仅执行一次以下操作,以选择最适合您的启发式方法。
EKAttributes.Precedence.QueueingHeuristic.value = .priority
或者
EKAttributes.Precedence.QueueingHeuristic.value = .chronological
EKAttributes.Precedence.QueueingHeuristic.value
的默认值为 .priority
。
优先级的默认值为 .override(priority: .normal, dropEnqueuedEntries: false)
。
条目的显示优先级确定它是否关闭其他条目或被它们关闭。 一个条目只能被具有相等或更高显示优先级的条目关闭。
let highPriorityAttributes = EKAttributes()
highPriorityAttributes.precedence.priority = .high
let normalPriorityAttributes = EKAttributes()
normalPriorityAttributes.precedence.priority = .normal
// Display high priority entry
SwiftEntryKit.display(entry: view1, using: highPriorityAttributes)
// Display normal priority entry (ignored!)
SwiftEntryKit.display(entry: view2, using: normalPriorityAttributes)
view2 不会被显示!
条目的显示时长(从条目完成其进入动画的那一刻算起,直到退出动画开始)。
显示 4 秒
attributes.displayDuration = 4
无限时长显示
attributes.displayDuration = .infinity
displayDuration
的默认值为 2
。
将条目紧密绑定到屏幕上下文的约束,例如:高度、宽度、最大宽度、最大高度、附加垂直偏移和安全区域相关信息。
positionConstraints
的 size
属性显式设置它们的精确大小。例如
Ratio edge - 表示宽度边的比率是屏幕宽度的 0.9。
let widthConstraint = EKAttributes.PositionConstraints.Edge.ratio(value: 0.9)
Intrinsic edge - 表示所需的高度值是内容高度 - 由条目的垂直约束决定
let heightConstraint = EKAttributes.PositionConstraints.Edge.intrinsic
同样创建条目大小约束
attributes.positionConstraints.size = .init(width: widthConstraint, height: heightConstraint)
您还可以设置 attributes.positionConstraints.maxSize,以确保条目不超过预定义的限制。 这在设备方向改变时很有用。
安全区域 - 可用于覆盖安全区域或为其着色(更多示例在示例项目中)。该代码段意味着应保留安全区域插图,而不应成为条目的一部分。
attributes.positionConstraints.safeArea = .empty(fillSafeArea: false)
垂直偏移 - 可以应用于条目的附加偏移(安全区域之外)。
attributes.positionConstraints.verticalOffset = 10
自动旋转 - 条目是否随设备的方向自动旋转。 默认为 true
。
attributes.positionConstraints.rotation.isEnabled = false
键盘关系 - 用于在显示键盘后将条目绑定到键盘。
let offset = EKAttributes.PositionConstraints.KeyboardRelation.Offset(bottom: 10, screenEdgeResistance: 20)
let keyboardRelation = EKAttributes.PositionConstraints.KeyboardRelation.bind(offset: offset)
attributes.positionConstraints.keyboardRelation = keyboardRelation
在上面的例子中,条目的底部被调整为距离键盘顶部(在显示时)有 10pt 的偏移量。由于条目的 frame 可能会超出屏幕边界,用户可能无法看到条目的全部内容 - 我们不希望出现这种情况。因此,添加了一个额外的关联值 screenEdgeResistance
,其值为 20pt。 也就是说,为了确保条目保持在屏幕范围内,并且始终对用户可见。 在设备方向为横向并且键盘弹出时,可能会出现极端情况(有关更多信息,请参见示例项目表单预设)。
用户可以与条目和屏幕进行交互。 用户交互可以通过多种方式进行拦截
与条目的交互(任何触摸)都会将其退出延迟 3 秒
attributes.entryInteraction = .delayExit(by: 3)
点击条目/屏幕会立即将其关闭
attributes.entryInteraction = .dismiss
attributes.screenInteraction = .dismiss
点击条目会被吞噬(忽略)
attributes.entryInteraction = .absorbTouches
点击屏幕会被转发到较低级别的窗口,在大多数情况下,接收者将是应用程序窗口。 当您想要显示不引人注意的内容(如横幅和推送通知条目)时,这非常有用。
attributes.screenInteraction = .forward
传递用户点击条目时调用的其他操作
let action = {
// Do something useful
}
attributes.entryInteraction.customTapActions.append(action)
screenInteraction
的默认值为 .forward
。
entryInteraction
的默认值为 .dismiss
。
描述条目在滚动时的行为,即通过滑动(swipe)手势进行关闭以及类似于 UIScrollView 的橡皮筋效果。
禁用条目上的平移(pan)和滑动(swipe)手势
attributes.scroll = .disabled
启用滑动(swipe)和拉伸(stretch),以及具有急动(jolt)效果的回弹(pullback)
attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)
启用滑动(swipe)和拉伸(stretch),以及具有缓出(ease-out)效果的回弹(pullback)
attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .easeOut)
启用滑动(swipe)但禁用拉伸(stretch)
attributes.scroll = .edgeCrossingDisabled(swipeable: true)
scroll
的默认值为 .enabled(swipeable: true, pullbackAnimation: .jolt)
。
设备可以产生触觉反馈,从而为每个条目添加额外的感官深度。
hapticFeedbackType
的默认值为 .none
。
可以将事件注入到条目中,以便在条目的生命周期中调用它们。
attributes.lifecycleEvents.willAppear = {
// Executed before the entry animates inside
}
attributes.lifecycleEvents.didAppear = {
// Executed after the entry animates inside
}
attributes.lifecycleEvents.willDisappear = {
// Executed before the entry animates outside
}
attributes.lifecycleEvents.didDisappear = {
// Executed after the entry animates outside
}
为了让您完全支持任何用户界面样式,SwiftEntryKit
引入了两种专门的类型
EKColor
描述浅色和深色模式下的颜色。EKAttributes.BackgroundStyle.BlurStyle
描述浅色和深色模式下的模糊效果。以下代码强制 SwiftEntryKit
在深色模式下显示条目。
attributes.displayMode = .dark
可能的值为:.light
、.dark
、.inferred
。 默认值为 .inferred
,这意味着条目将以当前用户界面样式显示。
条目和屏幕可以具有各种背景样式,例如模糊、颜色、渐变甚至图像。
以下示例表示条目和屏幕都具有清晰的背景
attributes.entryBackground = .clear
attributes.screenBackground = .clear
彩色条目背景和变暗的屏幕背景
attributes.entryBackground = .color(color: .standardContent)
attributes.screenBackground = .color(color: EKColor(UIColor(white: 0.5, alpha: 0.5)))
渐变条目背景(对角线向量)
let colors: [EKColor] = ...
attributes.entryBackground = .gradient(gradient: .init(colors: colors, startPoint: .zero, endPoint: CGPoint(x: 1, y: 1)))
视觉效果条目背景
attributes.entryBackground = .visualEffect(style: .dark)
entryBackground
和 screenBackground
的默认值为 .clear
。
环绕条目的阴影。
在条目周围启用阴影
attributes.shadow = .active(with: .init(color: .black, opacity: 0.3, radius: 10, offset: .zero))
在条目周围禁用阴影
attributes.shadow = .none
shadow
的默认值为 .none
。
条目周围的圆角。
仅左上角和右上角,半径为 10
attributes.roundCorners = .top(radius: 10)
仅左下角和右下角,半径为 10
attributes.roundCorners = .bottom(radius: 10)
所有角,半径为 10
attributes.roundCorners = .all(radius: 10)
没有圆角
attributes.roundCorners = .none
roundCorners
的默认值为 .none
。
条目周围的边框。
添加厚度为 0.5pt 的黑色边框
attributes.border = .value(color: .black, width: 0.5)
没有边框
attributes.border = .none
border
的默认值为 .none
。
描述条目如何动画式地进入和退出屏幕。
例如,从顶部进行平移(translation),带有弹簧(spring)效果、缩放(scale)进入甚至淡入(fade in)作为单个进入动画
attributes.entranceAnimation = .init(
translate: .init(duration: 0.7, anchorPosition: .top, spring: .init(damping: 1, initialVelocity: 0)),
scale: .init(from: 0.6, to: 1, duration: 0.7),
fade: .init(from: 0.8, to: 1, duration: 0.3))
entranceAnimation
和 exitAnimation
的默认值为 .translation
- 条目分别以 0.3 秒的持续时间平移进出。
描述当条目被弹出(被具有相等/更高显示优先级的条目关闭)时的行为。
条目正在以动画方式弹出
attributes.popBehavior = .animated(animation: .init(translate: .init(duration: 0.2)))
条目被覆盖(迅速消失)
attributes.popBehavior = .overridden
popBehavior
的默认值为 .animated(animation: .translation)
- 它以 0.3 秒的持续时间平移出去。
在显示条目期间,可以修改状态栏外观。 SwiftEntryKit 同时支持基于视图控制器的状态栏外观和手动设置。
设置状态栏样式非常简单 -
状态栏变为可见并获得浅色样式
attributes.statusBar = .light
状态栏变为隐藏
attributes.statusBar = .hidden
状态栏外观是从先前的上下文推断出来的(不会更改)
attributes.statusBar = .inferred
如果已经存在一个显示优先级较低/相等的条目,则状态栏将更改其样式。 删除条目后,状态栏将恢复其初始样式。
statusBar
的默认值为 .inferred
。
public struct EKAttributes
// Identification
public var name: String?
// Display
public var windowLevel: WindowLevel
public var position: Position
public var precedence: Precedence
public var displayDuration: DisplayDuration
public var positionConstraints: PositionConstraints
// User Interaction
public var screenInteraction: UserInteraction
public var entryInteraction: UserInteraction
public var scroll: Scroll
public var hapticFeedbackType: NotificationHapticFeedback
public var lifecycleEvents: LifecycleEvents
// Theme & Style
public var displayMode = DisplayMode.inferred
public var entryBackground: BackgroundStyle
public var screenBackground: BackgroundStyle
public var shadow: Shadow
public var roundCorners: RoundCorners
public var border: Border
public var statusBar: StatusBar
// Animations
public var entranceAnimation: Animation
public var exitAnimation: Animation
public var popBehavior: PopBehavior
}
您可以使用 SwiftEntryKit 附带的预设之一,只需执行以下 4 个简单步骤
// Generate top floating entry and set some properties
var attributes = EKAttributes.topFloat
attributes.entryBackground = .gradient(gradient: .init(colors: [EKColor(.red), EKColor(.green)], startPoint: .zero, endPoint: CGPoint(x: 1, y: 1)))
attributes.popBehavior = .animated(animation: .init(translate: .init(duration: 0.3), scale: .init(from: 1, to: 0.7, duration: 0.7)))
attributes.shadow = .active(with: .init(color: .black, opacity: 0.5, radius: 10, offset: .zero))
attributes.statusBar = .dark
attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)
attributes.positionConstraints.maxSize = .init(width: .constant(value: UIScreen.main.minEdge), height: .intrinsic)
let title = EKProperty.LabelContent(text: titleText, style: .init(font: titleFont, color: textColor))
let description = EKProperty.LabelContent(text: descText, style: .init(font: descFont, color: textColor))
let image = EKProperty.ImageContent(image: UIImage(named: imageName)!, size: CGSize(width: 35, height: 35))
let simpleMessage = EKSimpleMessage(image: image, title: title, description: description)
let notificationMessage = EKNotificationMessage(simpleMessage: simpleMessage)
let contentView = EKNotificationMessageView(with: notificationMessage)
SwiftEntryKit.display(entry: contentView, using: attributes)
// Create a basic toast that appears at the top
var attributes = EKAttributes.topToast
// Set its background to white
attributes.entryBackground = .color(color: .white)
// Animate in and out using default translation
attributes.entranceAnimation = .translation
attributes.exitAnimation = .translation
let customView = UIView()
/*
... Customize the view as you like ...
*/
// Display the view with the configuration
SwiftEntryKit.display(entry: customView, using: attributes)
从 0.4.0 版本开始,也支持视图控制器。
SwiftEntryKit.display(entry: customViewController, using: attributes)
默认情况下,在 SwiftEntryKit 完成条目的显示后,应用程序委托持有的窗口会再次成为 key。 可以使用 rollbackWindow
参数更改此行为。
SwiftEntryKit.display(entry: view, using: attributes, rollbackWindow: .custom(window: alternativeWindow))
条目被关闭后,给定的窗口 alternativeWindow
将成为 key,而不是应用程序委托持有的窗口。
您可以通过简单地在 SwiftEntryKit 类中调用 dismiss 来关闭当前显示的条目,如下所示
SwiftEntryKit.dismiss()
或者
SwiftEntryKit.dismiss(.displayed)
这将使用其 exitAnimation 属性以动画方式关闭条目,并在完成后,窗口也将被删除。
您也可以关闭当前显示的条目并刷新队列,如下所示
SwiftEntryKit.dismiss(.all)
仅刷新队列,将任何当前显示的条目留给其自然生命周期
SwiftEntryKit.dismiss(.queue)
按名称关闭特定条目 - 无论是当前显示的还是排队的。 所有具有给定名称的条目都会被关闭。
SwiftEntryKit.dismiss(.specific(entryName: "Entry Name"))
关闭任何显示优先级低于或等于 .normal
的条目。
SwiftEntryKit.dismiss(.prioritizedLowerOrEqualTo(priority: .normal))
注入一个尾随闭包,以便在条目关闭后执行。
SwiftEntryKit.dismiss {
// Executed right after the entry has been dismissed
}
查询当前是否正在显示条目
if SwiftEntryKit.isCurrentlyDisplaying {
/* Do your things */
}
使用 EKAttributes
内的 name
属性查询是否正在显示特定条目。
if SwiftEntryKit.isCurrentlyDisplaying(entryNamed: "Top Note") {
/* Do your things */
}
查询条目队列是否不为空
if SwiftEntryKit.isQueueEmpty {
/* Do your things */
}
查询条目队列是否包含具有名称的条目
if SwiftEntryKit.queueContains(entryNamed: "Custom-Name") {
/* Do your things */
}
条目可以垂直平移(可以使用 scroll 属性启用此功能)。 因此,使用类似滑动的动作关闭条目是很自然的。
启用滑动(swipe)手势。 当滑动(swipe)手势失败(未通过速度阈值)时,将其缓回。
attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .easeOut)
启用滑动(swipe)手势。 当滑动(swipe)手势失败时,将其以急动(jolt)效果扔回去。
attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)
PullbackAnimation 值(duration、damping 和 initialSpringVelocity)也可以自定义。
滑动(Swipe) | 急动(Jolt) |
---|---|
![]() |
![]() |
EKAttributes.PositionConstraints.SafeArea 可用于覆盖条目内容的的安全区域,或者用背景颜色填充安全区域(如 Toasts 所做的那样),甚至将安全区域留空(如 Floats 所做的那样)。
SwiftEntryKit 支持 iOS 11.x.y,并向后兼容 iOS 9.x.y,因此在早期 iOS 版本中,状态栏区域的处理方式与安全区域相同。
SwiftEntryKit 会识别方向改变,并将条目的布局调整为这些改变。 因此,如果您希望限制条目的宽度,可以通过为其指定最大值来实现,如下所示
var attributes = EKAttributes.topFloat
// Give the entry the width of the screen minus 20pts from each side, the height is decided by the content's contraint's
attributes.positionConstraints.size = .init(width: .offset(value: 20), height: .intrinsic)
// Give the entry maximum width of the screen minimum edge - thus the entry won't grow much when the device orientation changes from portrait to landscape mode.
let edgeWidth = min(UIScreen.main.bounds.width, UIScreen.main.bounds.height)
attributes.positionConstraints.maxSize = .init(width: .constant(value: edgeWidth), height: .intrinsic)
let customView = UIView()
/*
... Customize the view as you like ...
*/
// Use class method of SwiftEntryKit to display the view using the desired attributes
SwiftEntryKit.display(entry: customView, using: attributes)
方向改变演示 |
---|
![]() |
您可以使用预设屏幕上的分段控件来调整显示模式,强制浅色和深色模式。 所有预设都已准备好黑暗模式,但示例项目中的只有一些演示了黑暗模式功能。
SwiftEntryKit 的 API 使用 Swift 语言独有的语法(枚举、关联值等)。 因此,无法从 Objective-C 文件(.m、.h 或 .mm)直接引用 SwiftEntryKit
。
但是,使用一个简单的 .swift 类(它是 SwiftEntryKit
和您的 Objective-C 代码之间的适配器)将 SwiftEntryKit 集成到 Objective-C 项目中非常容易。
这个项目 使用 Carthage 和 CocoaPods 演示了这一点。
Daniel Huri, huri000@gmail.com
可以通过向以下地址发送比特币或以太币进行捐赠。
BTC | ETH |
---|---|
134TiBiUvVNt7Na5KXEFBSChLdgVDw1Hnr | 0xAe6616181FCdde4793AE749Ce21Cd5Af9333A3E2 |
![]() |
![]() |
感谢 Lily Azar, lilushkaa@gmail.com 制作了这些很棒的预设图标。
SwiftEntryKit 在 MIT 许可下可用。 有关更多信息,请参见 LICENSE 文件。
请注意,使用项目中的任何图标都需要注明创作者。 有关创作者列表,请参见 credits。