Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Introspect underlying UIKit/AppKit components from SwiftUI

License

NotificationsYou must be signed in to change notification settings

siteline/swiftui-introspect

Repository files navigation

CI Status BadgeSwift Version Compatibility BadgePlatform Compatibility Badge

SwiftUI Introspect allows you to get the underlying UIKit or AppKit element of a SwiftUI view.

For instance, with SwiftUI Introspect you can accessUITableView to modify separators, orUINavigationController to customize the tab bar.

How it works

SwiftUI Introspect works by adding an invisibleIntrospectionView on top of the selected view, and an invisible "anchor" view underneath it, then looking through the UIKit/AppKit view hierarchy between the two to find the relevant view.

For instance, when introspecting aScrollView...

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

... it will:

  1. Add marker views in front and behindScrollView.
  2. Traverse through all subviews between both marker views until aUIScrollView instance (if any) is found.

Important

Although this introspection method is very solid and unlikely to break in itself, future OS releases require explicit opt-in for introspection (.iOS(.vXYZ)), given potential differences in underlying UIKit/AppKit view types between major OS versions.

By default, the.introspect modifier acts directly on itsreceiver. This means calling.introspect from inside the view you're trying to introspect won't have any effect. However, there are times when this is not possible or simply too inflexible, in which case youcan introspect anancestor, but you must opt into this explicitly by overriding the introspectionscope:

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

Usage in production

SwiftUI Introspect is meant to be used in production. It does not use any private API. It only inspects the view hierarchy using publicly available methods. The library takes a defensive approach to inspecting the view hierarchy: there is no hard assumption that elements are laid out a certain way, there is no force-cast to UIKit/AppKit classes, and the.introspect modifier is simply ignored if UIKit/AppKit views cannot be found.

Install

Swift Package Manager

Xcode

Package.swift

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

CocoaPods

pod'SwiftUIIntrospect','~> 1.0'

Introspection

Implemented

Missing an element? Pleasestart a discussion. As a temporary solution, you canimplement your own introspectable view type.

Cannot implement

SwiftUIAffected FrameworksWhy
TextUIKit, AppKitNot a UILabel / NSLabel
ImageUIKit, AppKitNot a UIImageView / NSImageView
ButtonUIKitNot a UIButton

Examples

List

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

ScrollView

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

NavigationView

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

TextField

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

Advanced usage

Implement your own introspectable type

Missing an element? Pleasestart a discussion.

In case SwiftUI Introspect (unlikely) doesn't support the SwiftUI element that you're looking for, you can implement your own introspectable type.

For example, here's how the library implements the introspectableTextField type:

import SwiftUI@_spi(Advanced)import SwiftUIIntrospectpublicstructTextFieldType:IntrospectableViewType{}extensionIntrospectableViewTypewhere Self==TextFieldType{publicstaticvartextField:Self{.init()}}#if canImport(UIKit)extensioniOSViewVersion<TextFieldType,UITextField>{publicstaticletv13=Self(for:.v13)publicstaticletv14=Self(for:.v14)publicstaticletv15=Self(for:.v15)publicstaticletv16=Self(for:.v16)publicstaticletv17=Self(for:.v17)publicstaticletv18=Self(for:.v18)publicstaticletv26=Self(for:.v26)}extensiontvOSViewVersion<TextFieldType,UITextField>{publicstaticletv13=Self(for:.v13)publicstaticletv14=Self(for:.v14)publicstaticletv15=Self(for:.v15)publicstaticletv16=Self(for:.v16)publicstaticletv17=Self(for:.v17)publicstaticletv18=Self(for:.v18)publicstaticletv26=Self(for:.v26)}extensionvisionOSViewVersion<TextFieldType,UITextField>{publicstaticletv1=Self(for:.v1)publicstaticletv2=Self(for:.v2)publicstaticletv26=Self(for:.v26)}#elseif canImport(AppKit)extensionmacOSViewVersion<TextFieldType,NSTextField>{publicstaticletv10_15=Self(for:.v10_15)publicstaticletv11=Self(for:.v11)publicstaticletv12=Self(for:.v12)publicstaticletv13=Self(for:.v13)publicstaticletv14=Self(for:.v14)publicstaticletv15=Self(for:.v15)publicstaticletv26=Self(for:.v26)}#endif

Introspect on future platform versions

By default, introspection applies per specific platform version. This is a sensible default for maximum predictability in regularly maintained codebases, but it's not always a good fit for e.g. library developers who may want to cover as many future platform versions as possible in order to provide the best chance for long-term future functionality of their library without regular maintenance.

For such cases, SwiftUI Introspect offers range-based platform version predicates behind the Advanced SPI:

import SwiftUI@_spi(Advanced)import SwiftUIIntrospectstructContentView:View{varbody:someView{ScrollView{            // ...}.introspect(.scrollView, on:.iOS(.v13...)){ scrollViewin            // ...}}}

Bear in mind this should be used cautiously, and with full knowledge that any future OS version might break the expected introspection types unless explicitly available. For instance, if in the example above hypothetically iOS 19 stops using UIScrollView under the hood, the customization closure will never be called on said platform.

Keep instances outside the customize closure

Sometimes, you might need to keep your introspected instance around for longer than the customization closure lifetime. In such cases,@State is not a good option because it produces retain cycles. Instead, SwiftUI Introspect offers a@Weak property wrapper behind the Advanced SPI:

import SwiftUI@_spi(Advanced)import SwiftUIIntrospectstructContentView:View{@WeakvarscrollView:UIScrollView?varbody:someView{ScrollView{            // ...}.introspect(.scrollView, on:.iOS(.v13,.v14,.v15,.v16,.v17,.v18,.v26)){ scrollViewinself.scrollView= scrollView}}}

Community projects

Here's a list of open source libraries powered by the SwiftUI Introspect library:

If you're working on a library built on SwiftUI Introspect or know of one, feel free to submit a PR adding it to the list.


[8]ページ先頭

©2009-2025 Movatter.jp