此软件包是一个微型框架,用于以类似 Storybook 的方式渲染组件的预览。它利用 objc 运行时和 SwiftUI 来使其使用尽可能无缝。即使你的应用程序没有使用 SwiftUI,也可以使用它,UIKit 应用程序也可以利用此框架(UIKit 辅助库)。
2.0.0 版本现已发布。这带来了一些重大的新功能和生活质量的改进。这些包括:
一个针对 iOS 11 的演示项目可以在这里找到。
演示视频
V2
V1
✅ 可配置组件
✅ 能够将 storybook 与 staging 版本一起发布,供设计师在应用程序旁边查看。目前正在 experimental 分支上进行此项工作。
🔲 使用快照进行视觉回归测试,如 storybook js:https://storybook.org.cn/tutorials/intro-to-storybook/react/en/test/
🔲 待定...
2.0 版本已发布,其中包含许多改进,但这样做可能会导致某些现有功能不再以相同的方式工作。
创建一个名为 Storybook
的文件,并向其添加一个预览提供程序以渲染 StorybookCollection
。
请注意,如果你的应用程序的最低版本低于 iOS 13,则需要使用 @available(iOS 13, *)
。预览仍然会渲染,你也不会收到任何编译错误。如果你的目标是 iOS 13+,则无需放置 @available(iOS 13, *)
。
// Storybook.swift
#if DEBUG
import Storybook
import SwiftUI
@available(iOS 13, *)
struct StorybookPreview: PreviewProvider {
static var previews: some View {
Storybook.render()
}
}
#endif
为了向 storybook 添加页面,只需在 Storybook
类上创建扩展,其中包含你要渲染的视图。建议在组件所在的文件中添加这些扩展。这将使你更容易找到 storybook 中组件的位置,并且如果你完全删除组件文件,它将自动从 storybook 中删除。
重要提示:静态属性必须用 @objc 标记,才能被 Storybook 发现和渲染。
// SomeView.swift
#if DEBUG
import Storybook
@available(iOS 13.0, *)
extension Storybook {
@objc static let someView = StorybookPage(
folder: "/Design System/Views/Some View",
views: [
SomeView().storybookTitle("Primary")
SomeView().storybookTitle("Secondary")
]
)
}
#endif
使用 2.0.0+ 版本,你可以在 storybook 中渲染一个控制面板,该面板可以修改当前正在查看的视图。控件由 StorybookControlType
枚举提供支持,该枚举允许你使用预构建的控件或你自己的自定义控件。
为了利用控件,你必须将它们添加到你的 Storybook 上下文中。由于 storybook 利用了 SwiftUI 的 Environment,这意味着你可以将控件应用于单个视图或级联到 Storybook 中的每个视图。默认情况下,Storybook 会将所有视图包装在一个控件上下文中,该上下文从环境中拾取控件,因此你无需一次又一次地指定相同的控件。
func storybookSetGlobalControls(_ controls: StorybookControlType...) -> some View
要将控件应用于 Storybook 中的所有视图,请在根目录设置全局控件。在此示例中,将为所有视图应用 colorScheme、dynamicType、screenSize 和自定义控件的控件。
Storybook.render()
.storybookSetGlobalControls(
.colorScheme,
.dynamicType,
.screenSize,
.custom(StorybookControl(id: "MyCustomControl", view: {
CustomControl()
}))
)
要向视图添加单个控件,你可以使用另一个函数将控件添加到上下文中。以下代码将为此特定视图的控制菜单添加一个 jira 文档链接。
extension Storybook {
@objc static let someView = StorybookPage(
folder: "/Design System/Views/Some View",
view: SomeView()
.storybookAddControls(
.documentationLink(
title: "Jira",
url: "https://jira.com/123",
icon: .jira
)
)
.storybookTitle("Primary")
)
}
可以轻松地将自定义控件添加到 Storybook 控件覆盖层。这是一个更改视图标题的控件示例。
重要提示:如果控件具有需要更新的状态(而不是文本),则必须在更改时更新 id。例如,对于切换,你必须在切换更改时更新 id。希望将来会有更简洁的解决方案。
// The View used in the App
@available(iOS 13.0, *)
struct SomeView: View {
let title: String
var body: some View {
Text(title)
}
}
// A wrapper around SomeView to control it
@available(iOS 13.0, *)
struct ControlledSomeView: View {
@State var title = "Hello, World!"
@State var isToggled = false
var controlId: String {
return "SomeViewControl" + isToggled.description
}
var body: some View {
SomeView(title: title)
.storybookAddControls(
.custom(StorybookControl(
id: controlId,
view: {
VStack {
TextField("Title", text: $title)
Toggle(isOn: $isToggled) {
Text("Some Toggle")
}
}
}
))
)
}
}
@available(iOS 13.0, *)
extension Storybook {
@objc static let someView = StorybookPage(
folder: "/Views",
view: ControlledSomeView().storybookTitle("Some View")
)
}
Storybook
Storybook
类使用 objc 运行时来镜像其类型为 StorybookPage
的静态属性,并为其生成预览。为了向 storybook 添加预览,请在 Storybook
上创建一个扩展,其中包含指向 StorybookPage
的静态属性。
StorybookPage
StorybookPage
类用于渲染你想要在 StorybookCollection
中显示的内容。
public init(
folder directory: String,
view: StoryBookView,
file: String = #file
)
public init(
folder directory: String,
views: [StoryBookView],
file: String = #file
)
有两种初始化器用于创建页面。标题和文件参数用于渲染 Storybook 页面列表。有时,代码中组件的名称不能准确描述它是什么。因此,标题可以是组件更易于理解的描述,而文件告诉你该组件在代码中的位置。该文件有一个默认参数,它将获取初始化器调用自的文件,或者你可以手动提供该文件(不推荐)。
StorybookView
StoryBookView
结构是一个 SwiftUI 包装视图,用于渲染 StorybookPage
中的视图。你提供一个标题和要渲染的视图。
View 有一个扩展,将其自身包装在 StorybookView
中,以实现更简洁的 API。
func storybookTitle(_ title: String, file: String = #file) -> StoryBookView
// Usage
SomeView().storybookTitle("My View")
StorybookCollection
StorybookCollection
结构是一个 SwiftUI 视图,用于渲染所有 Storybook 页面。这应该在 PreviewProvider 中使用,以便你可以轻松浏览而无需运行任何内容。
Storybook 上的一个便捷函数 render 使记住如何渲染内容更容易一些。
Storybook.render() is the same as StorybookCollection()