SwiftEntryKit 2.0.0

SwiftEntryKit 2.0.0

Daniel huri 维护。



  • Daniel Huri

SwiftEntryKit

Platform Language Version Carthage compatible Accio: Supported License

🤗捐赠可以通过这里进行。

目录

概述

SwiftEntryKit 是一个简单且功能丰富的 Swift 编写的内容呈现器。

功能

横幅或弹出窗口称为入口

  • 入口在单独的UIWindow(类型为EKWindow)内显示,因此用户可以在入口以非侵入的方式显示时自由地导航应用程序。
  • 该套件提供了美丽的预置,可以使用您自己的颜色和字体进行主题设置。
  • 定制:入口可高度自定义
    • 可以 Positioned 在屏幕的顶部、中心或底部。
    • 可以在屏幕安全区域内或之外显示。
    • 可以风格化:有 边框阴影圆角
    • 其内容及周围背景可以被模糊、变暗、上色或具有样式的渐变。
    • 过渡 动画 可自定义 - 进入、退出和弹出(由另一个入口)。
    • 可以拦截入口或屏幕的用户交互
    • 可以使用 优先级 属性将入口排队或覆盖之前的入口。
    • 每个入口都有一个 显示优先级 属性。这意味着只有具有相等或更高优先级的其他入口才能将其忽略。
    • 预置支持无障碍访问。
    • 在平移时,入口可以有可选的弹性效果。
    • 可以使用简单的滑动手势选择性地忽略入口。
    • 可以内嵌生命周期事件:将和消失的 willdid
    • 可以为入口的显示持续时间设置状态栏样式
    • 支持导航控制器自定义视图

示例项目

示例项目包含各种预置和示例供您使用和修改。

示例项目安装

您可以使用终端或源树等git客户端。

终端用户

$ git clone https://github.com/huri000/SwiftEntryKit.git

Git客户端(源码树)

克隆 https://github.com/huri000/SwiftEntryKit.git

预设

Toast消息 备注 浮动 弹出窗口
toasts_example notes_example floats_example popup_example
警告框 表单 评分 更多...
alert_example form_example rating_example custom_example

游乐场

名词:人们可以玩耍的地方🏈

示例应用程序包含一个游乐场界面,允许您自定义首选条目。游乐场界面有一些限制(允许选择常量值),但是您可以轻松修改代码以满足您的需求。看看吧!

游乐场界面 顶部Toast示例
playground_example playground-sample-1

需求

  • iOS 9 或更高版本。
  • Xcode 9 或更高版本。
  • Swift 4.0 或更高版本。
  • 该库尚未在iOS 8.x.y 或更低版本上进行测试。

安装

  • SwiftEntryKit自1.0.0版本起与Swift 5兼容。
  • SwiftEntryKit自0.8.1版本起与Swift 4.2兼容。
  • 使用较低Swift版本的开发者应安装发布版0.7.2。

CocoaPods

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

Carthage 是一个去中心化的依赖管理器,可用于构建您的依赖项并提供二进制框架。

可以使用 Homebrew 通过以下命令安装 Carthage:

$ brew update
$ brew install carthage

要使用 Carthage 将 SwiftEntryKit 集成到您的 Xcode 项目中,请在您的 Cartfile 中指定以下内容:

github "huri000/SwiftEntryKit" == 2.0.0

Accio

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 结构

var attributes = EKAttributes()

以下是可以修改的 入口属性 属性

入口名称

入口可以有名称。当实例化 EKAttributes 结构时,它是无名的,意味着 name 属性是 nil。建议为入口设置一个有意义的名称。

attributes.name = "Top Note"

可以特别引用具有名称的入口,例如,可以查询是否 特定 的入口正在显示

if SwiftEntryKit.isCurrentlyDisplaying(entryNamed: "Top Note") {
    /* Do your things */
}

窗口级别

入口可以在应用程序主窗口上方、状态栏上方、警报窗口上方甚至有自定义级别(UIWindowLevel)显示。

例如,将窗口级别设置为 正常,类似地

attributes.windowLevel = .normal

这将导致入口在应用程序主窗口上方、状态栏下方显示。

windowLevel 的默认值是 .statusBar

显示位置

入口可以在屏幕的 顶部居中底部 显示。

例如,将显示位置设置为 底部,类似地

attributes.position = .bottom

position 的默认值是 .top

优先级

条目的优先级属性描述了条目插入的方式。它提供了两种管理多个同时条目显示优先级的途径。

覆盖

如果显示优先级等于或高于当前显示的条目,则覆盖它。

以下示例设置了具有.max显示优先级的.override优先级,同时忽略已入队的条目(在新的条目消失后显示它们)。

attributes.precedence = .override(priority: .max, dropEnqueuedEntries: false)

您可以取消队列中条目的队列。

如果dropEnqueuedEntriesfalse,则入队条目将保留在队列中。第一个入队条目将显示在新条目弹出后。如果dropEnqueuedEntriestrue,则在新的条目显示时,条目队列将被清空。

入队

如果队列为空,则立即显示条目,否则,将条目插入队列,直到其显示的时刻到来。

以下示例设置了具有.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

位置约束

将条目与屏幕上下文紧密关联的限制,例如:高度、宽度、最大宽度、最大高度、额外的垂直偏移和安全区相关信息。

  • 支持自动布局的条目 - 它们的高度由应用于它们的所有约束推断。
  • 不包括自动布局的条目 - 必须显式使用 positionConstraintssize 属性来设置它们的精确大小。

例如

比率边 - 表示宽度边的比率是屏幕宽度的0.9。

let widthConstraint = EKAttributes.PositionConstraints.Edge.ratio(value: 0.9)

内在边 - 表示期望的高度值是内容高度 - 由条目的垂直约束决定。

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偏移。由于条目的框架可能超出屏幕边界,用户可能看不到整个条目——我们不想这样。因此,添加了一个额外的关联值——值为20pt的screenEdgeResistance。也就是说,为了确保条目保持在屏幕边界内,并且始终对用户可见。在设备方向为横向并且键盘显示的情况下,可能出现极端情况(更多信息请参阅示例项目预设)。

用户交互

用户可以交互条目和屏幕。用户交互可以通过各种方式拦截

与条目的任何交互(任何触摸)都会延迟其退出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

滚动行为

描述条目在滚动时的行为,即,通过滑动手势和类似于UIScrollView的橡皮筋效果进行关闭。

禁用条目上的拖拽和滑动手势

attributes.scroll = .disabled

启用带有震动效果的滑动、拉伸和回弹

attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)

启用带有平稳出效果的滑动、拉伸和回弹

attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .easeOut)

启用滑动但禁用拉伸

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)

entryBackgroundscreenBackground的默认值为.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

动画

描述入口如何进入和退出屏幕。

  • 每个动画描述符可以同时具有最多3种类型的动画。它们可以组合成单个复杂动画!
  • 平移动画锚点可以显式设置,但它的默认值根据入口位置确定。

例如,从顶部使用弹跳,缩放进入,甚至淡入作为单个进入动画

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

entranceAnimationexitAnimation的默认值是.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

ECAttributes的接口如下

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个简单步骤:

  1. 创建您的 EKAttributes 结构体并设置您希望使用的属性。
  2. 创建 EKNotificationMessage 结构体(内容)并设置内容。
  3. 创建 EKNotificationMessageView (视图)并将 EKNotificationMessage 结构体注入其中。
  4. 使用 SwiftEntryKit 类方法来显示条目。

EkNotificationMessageView 预设示例

// 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完成显示后,应用程序代理持有的窗口再次变为关键窗口。这可以通过使用rollbackWindow参数来更改此行为。

SwiftEntryKit.display(entry: view, using: attributes, rollbackWindow: .custom(window: alternativeWindow))

在条目被关闭后,所提供的alternativeWindow将变为关键窗口,而不是应用程序代理持有的窗口。

关闭条目

您可以通过在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 属性开启)。因此,使用类似于滑动的手势删除条目也变得很自然。

启用滑动手势。当滑动手势失败(未超过速度阈值)时,使其缓慢返回。

attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .easeOut)

启用滑动手势。当滑动手势失败时,将它猛地弹回。

attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)

还可以自定义 PullbackAnimation 的值(持续时间、阻尼和初始弹簧速度)。

滑动 猛冲
swipe_example band_example

处理安全区域

EKAttributes.PositionConstraints.SafeArea 可以用来覆盖项目内容的安全区域,或者用背景颜色(如 Toast 一样)填充安全区域,或者甚至使安全区域保持为空(如 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)
方向变化演示
orientation_change

示例项目中的暗黑模式

您可以使用预设屏幕上的分段控制器来调整显示模式,强制启用浅色和暗黑模式。所有预设均已针对暗黑模式做好准备,但仅在示例项目中展示了一些暗黑模式的功能。

light_dark

Swift和Objective-C的互操作性

SwiftEntryKit的API使用Swift语言的专用语法(枚举、关联值等)。因此,不能直接从Objective-C文件(.m.h.mm)中引用SwiftEntryKit

然而,使用一个简单的.swift类(它充当SwiftEntryKit和您的Objective-C代码之间的适配器)相当容易地将SwiftEntryKit集成到Objective-C项目中。

此项目展示了如何使用Carthage和CocoaPods。

作者

Daniel Huri,[email protected]

捐赠

可通过将Bitcoin或Ether发送到以下地址进行捐赠。

BTC ETH
134TiBiUvVNt7Na5KXEFBSChLdgVDw1Hnr 0xAe6616181FCdde4793AE749Ce21Cd5Af9333A3E2
btc_address eth_address

感谢

感谢Lily Azar,[email protected]提供了那些极好的预设图标。

致谢

图标致谢

许可

SwiftEntryKit 遵循 MIT 许可协议。更多详情请参阅 LICENSE 文件。

例外

请注意,项目内使用的图标需要向创作者致谢。创作者名单请参阅 致谢 文件。