SwiftUIIntrospect 1.3.0

SwiftUIIntrospect 1.3.0

David Roman 维护。



  • 作者:
  • David Roman

SwiftUI Introspect

CI Status Badge Platform Compatibility Badge

注意

SwiftUIIntrospect 是一个全新的模块,基于原始的 Introspect 模块,改进了稳定性、可预测性和易用性。

这两个模块目前都存放在这个仓库中,但最终计划是随着 1.0 版本的发布,废弃 Introspect 并使用 SwiftUIIntrospect

虽然 Introspect 支持 Swift 5.5 或更高版本,但 SwiftUIIntrospect 需要 Swift 5.7 或更高版本,因为使用了更现代的语言特性,这有助于实现上述改进。

SwiftUIIntrospect 允许您获取 SwiftUI 视图的底层 UIKit 或 AppKit 元素。

例如,使用 SwiftUIIntrospect,您可以访问 UITableView 来修改分隔符,或访问 UINavigationController 来自定义标签栏。

它是如何工作的

SwiftUIIntrospect 通过在所选视图上方添加一个不可见的 IntrospectionView,以及下方添加一个不可见的“锚点”视图,然后在两者之间通过 UIKit/AppKit 视图层次结构查找相关视图来工作。

例如,当检查 ScrollView...

ScrollView {
    Text("Item 1")
}
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in
    // do something with UIScrollView
}

...

  • 它将
  • IntrospectionView 设置为 TextField 的覆盖层。
  • IntrospectionAnchorView 设置为 TextField 的背景。

遍历两个视图之间的所有子视图,直到找到一个 UIScrollView 实例(如果有的话)。

警告

默认情况下,.introspect 直接在其 接收者 上工作。这意味着从您要分析的观点内部调用 .introspect 不会产生任何效果。这与原始的 Introspect 模块不同,其中一些视图会隐式允许从内部进行反射。这主要是因为大多数时候直接反射视图更为稳定和可预测,但有时这对于库开发者来说是不可能或太不灵活的。您 可以 使用 SwiftUIIntrospect 反射一个 祖先,但您必须显式选择这个行为,通过重写反射 scope

ScrollView {
    Text("Item 1")
        .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { scrollView in
            // do something with UIScrollView
        }
}

在生产环境中使用

SwiftUIIntrospect 意图在生产中使用。它不使用任何私有 API。它仅使用公开方法检查视图层次结构。该库在检查视图层次结构上采取了一种保护性的方法:没有对元素布局的硬性假设,没有对 UIKit/AppKit 类的强制转换,如果找不到 UIKit/AppKit 视图,则简单地忽略 .introspect 修饰符。

安装

Swift 包管理器

let package = Package(
    dependencies: [
        .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.11.0"),
    ],
    targets: [
        .target(name: <#Target Name#>, dependencies: [
            .product(name: "SwiftUIIntrospect", package: "swiftui-introspect"),
        ]),
    ]
)

CocoaPods

pod 'SwiftUIIntrospect'

自省

已实现

缺少一个元素?创建一个issue。作为临时解决方案,您可以实现您自己的可自省视图类型

不能实现

SwiftUI 受影响的框架 为什么
文本 UIKit, AppKit 不是UILabel / NSLabel
图像 UIKit, AppKit 不是UIImageView / NSImageView
按钮 UIKit 不是一个UIButton

示例

列表

List {
    Text("Item")
}
.introspect(.list, on: .iOS(.v13, .v14, .v15)) { tableView in
    tableView.backgroundView = UIView()
    tableView.backgroundColor = .cyan
}
.introspect(.list, on: .iOS(.v16, .v17)) { collectionView in
    collectionView.backgroundView = UIView()
    collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan
}

滚动视图(ScrollView)

ScrollView {
    Text("Item")
}
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in
    scrollView.backgroundColor = .red
}

导航视图(NavigationView)

NavigationView {
    Text("Item")
}
.navigationViewStyle(.stack)
.introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in
    navigationController.navigationBar.backgroundColor = .cyan
}

文本字段(TextField)

TextField("Text Field", text: <#Binding<String>#>)
    .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { textField in
        textField.backgroundColor = .red
    }

实现您自己的选择器

缺少一个元素?创建一个问题

如果SwiftUIIntrospect不支持您正在寻找的SwiftUI元素,您可以实现自己的选择器。例如,要查找TextField

@_spi(Advanced) import SwiftUIIntrospect

public struct TextFieldType: IntrospectableViewType {}

extension IntrospectableViewType where Self == TextFieldType {
    public static var textField: Self { .init() }
}

#if canImport(UIKit)
extension iOSViewVersion<TextFieldType, UITextField> {
    public static let v13 = Self(for: .v13)
    public static let v14 = Self(for: .v14)
    public static let v15 = Self(for: .v15)
    public static let v16 = Self(for: .v16)
    public static let v17 = Self(for: .v17)
}

extension tvOSViewVersion<TextFieldType, UITextField> {
    public static let v13 = Self(for: .v13)
    public static let v14 = Self(for: .v14)
    public static let v15 = Self(for: .v15)
    public static let v16 = Self(for: .v16)
    public static let v17 = Self(for: .v17)
}

extension visionOSViewVersion<TextFieldType, UITextField> {
    public static let v1 = Self(for: .v1)
}
#elseif canImport(AppKit)
extension macOSViewVersion<TextFieldType, NSTextField> {
    public static let v10_15 = Self(for: .v10_15)
    public static let v11 = Self(for: .v11)
    public static let v12 = Self(for: .v12)
    public static let v13 = Self(for: .v13)
    public static let v14 = Self(for: .v14)
}
#endif

发布

  1. 更新变更日志与新版本

  2. 以'提升到X.Y.Z'提交PR并合并它

  3. 标记新版本

    $ git tag X.Y.Z
    $ git push origin --tags