Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
df9782d
feat(core): make alpha/cornerRadius optional and add text row
daisuke0131 Aug 6, 2026
dfdd77c
feat(core): add MeasurementTarget and accessibility element scanner
daisuke0131 Aug 6, 2026
4ea059a
feat(core): inspect accessibility elements in window coordinates
daisuke0131 Aug 6, 2026
0f9822f
feat(core): merge accessibility elements into hierarchy scan
daisuke0131 Aug 6, 2026
2a48e2a
feat(ui): measure SwiftUI accessibility elements in the overlay
daisuke0131 Aug 6, 2026
3ef96d6
fix(ui): keep infoView above SwiftUI element monitor buttons
daisuke0131 Aug 6, 2026
2a60390
feat(api): add View.viewMonitor() for SwiftUI lifecycle apps
daisuke0131 Aug 6, 2026
b59e431
refactor(ui): render InfoView rows with SwiftUI
daisuke0131 Aug 6, 2026
446142e
feat(example): add SwiftUI lifecycle example app
daisuke0131 Aug 6, 2026
23a105b
docs: document SwiftUI support and limitations
daisuke0131 Aug 6, 2026
4e19afe
docs(changelog): write unreleased entry in Japanese
daisuke0131 Aug 6, 2026
7ba0db5
fix(ui): keep launcher above SwiftUI element monitor buttons
daisuke0131 Aug 6, 2026
61d514b
chore(lint): include SwiftUI example in swiftlint scope
daisuke0131 Aug 6, 2026
a097657
test(ui): rename stale targetView reference
daisuke0131 Aug 6, 2026
0a8e20e
fix(ui): break launcher button retain cycle in onToggle
daisuke0131 Aug 6, 2026
2e7cc09
fix(ui): stop treating own InfoView appearance as a screen transition
daisuke0131 Aug 9, 2026
1ee0ee5
feat: enable SwiftUI element detection without an accessibility client
daisuke0131 Aug 9, 2026
ca66694
feat(ui): surface a notice when SwiftUI elements cannot be detected
daisuke0131 Aug 9, 2026
df41174
docs: replace broken simulator workaround with the supported setup
daisuke0131 Aug 9, 2026
7fcf8cd
fix: detect SwiftUI List rows by scanning the whole hosting subtree
daisuke0131 Aug 9, 2026
9533e13
feat(ui): block touches on the app while measuring is on
daisuke0131 Aug 9, 2026
7c93429
fix(ui): attach all monitor buttons above the shield instead of insid…
daisuke0131 Aug 9, 2026
fd616b9
docs: describe pinned buttons and transition behavior accurately
daisuke0131 Aug 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ jobs:
-scheme ViewMonitorExample \
-destination "platform=iOS Simulator,name=${{ steps.sim.outputs.name }}"

- name: Build SwiftUI example
run: |
xcodebuild build \
-project Example/ViewMonitorSwiftUIExample/ViewMonitorSwiftUIExample.xcodeproj \
-scheme ViewMonitorSwiftUIExample \
-destination "platform=iOS Simulator,name=${{ steps.sim.outputs.name }}"

pod-lint:
name: pod lib lint
runs-on: macos-latest
Expand Down
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ included:
- Sources
- Tests
- Example/ViewMonitorExample/ViewMonitorExample
- Example/ViewMonitorSwiftUIExample/ViewMonitorSwiftUIExample

excluded:
- .build
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- SwiftUI 対応。SwiftUI 画面の `Text` / `Image` / `Button` を、SwiftUI が VoiceOver 向けに公開するアクセシビリティ要素経由で検出し、位置・サイズ・テキスト内容の計測と UIKit ビューとの距離計測に対応。検出にはアクセシビリティツリーの構築が必要(下記 `enableSwiftUIElementDetection()` か、VoiceOver / Accessibility Inspector などのクライアント接続)。詳細は README を参照
- `ViewMonitor.enableSwiftUIElementDetection()`。外部のアクセシビリティクライアント無しで SwiftUI 要素の検出を有効化する。DEBUG ビルド限定(内部でプライベート API を使うため、リリースビルドでは実装ごとコンパイルから除外され常に false を返す)
- SwiftUI ライフサイクルのアプリから起動するための `View.viewMonitor()` モディファイア
- SwiftUI サンプルアプリ(`Example/ViewMonitorSwiftUIExample`)
- ホスティングビューがあるのにアクセシビリティ要素を検出できない場合、InfoView に有効化手順の案内を表示(無言で0件のままにしない)
- `List` の行の検出。List はホスティングビューの下に UICollectionView を挟み、行のアクセシビリティ要素は各セル内のビューが公開するため、ホスティングビュー配下のすべてのビューを走査対象にした(行は List の仕様どおり1行=1要素として計測される)
- 計測中はアプリ本体へのタッチ(タップ・スクロール・エッジスワイプ)を遮断。誤操作でアクションが発火したり、画面遷移で計測状態が破棄されたりしない。計測ボタン・InfoView・実行ボタンなど ViewMonitor 自身の UI だけが操作できる

### Changed

- `UIHostingController` を埋め込んだ画面でも SwiftUI 要素に計測ボタンが表示されるように
- `InfoView` の内部描画を SwiftUI 化(挙動変更なし)
- 計測ボタンは UIKit 対象・SwiftUI 要素とも rootView 直下の固定配置に統一(対象ビューの subview には追加しない)。SwiftUI のホスティングビューのヒットテストが非決定的で、対象ビュー内に置いたボタンへのタッチが実機で届かないことがあるため。あわせて、隠れている対象(大タイトル表示中のインラインナビタイトルなど)にはボタンを付けない

## [2.2.0] - 2026-08-06

### Added
Expand Down
14 changes: 14 additions & 0 deletions Example/ViewMonitorSwiftUIExample/Signing.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Code signing settings for ViewMonitorSwiftUIExample.
//
// Building for a device needs a DEVELOPMENT_TEAM, but the value differs per
// developer and this is a public repository, so it lives neither in the
// pbxproj nor in this file. Create a Local.xcconfig as below and Cmd+R in
// Xcode will deploy to a device (you can find your Team ID under Signing &
// Capabilities in Xcode, or on your Apple Developer membership page):
//
// echo 'DEVELOPMENT_TEAM = XXXXXXXXXX' > Example/ViewMonitorSwiftUIExample/Local.xcconfig
//
// Local.xcconfig is git-ignored. The include below is optional (#include?) so
// builds don't break where the file is absent, such as on CI. Simulator builds
// don't need signing at all, so they need no setting here.
#include? "Local.xcconfig"
Original file line number Diff line number Diff line change
@@ -0,0 +1,337 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {

/* Begin PBXBuildFile section */
9D3E06DCEEC09745F88A635C /* ViewMonitorSwiftUIExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBA02CAC4239B0C02C6F642 /* ViewMonitorSwiftUIExampleApp.swift */; };
C0CDF57005D23B4DCB014E29 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23C4B9CF1CC0C3CDB4A351F3 /* ContentView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
0EBA02CAC4239B0C02C6F642 /* ViewMonitorSwiftUIExampleApp.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ViewMonitorSwiftUIExampleApp.swift; sourceTree = "<group>"; };
23C4B9CF1CC0C3CDB4A351F3 /* ContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
509AB8F272B9805C09EF2A08 /* ViewMonitorSwiftUIExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ViewMonitorSwiftUIExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
98BB70B66795A38A430A5454 /* Signing.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Signing.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
DA9FF015503021E80DBD010A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
5EFCA5F5108BD64E6944B7E8 /* Products */ = {
isa = PBXGroup;
children = (
509AB8F272B9805C09EF2A08 /* ViewMonitorSwiftUIExample.app */,
);
name = Products;
sourceTree = "<group>";
};
63C2FE9CC903968BEB57A378 = {
isa = PBXGroup;
children = (
5EFCA5F5108BD64E6944B7E8 /* Products */,
76ACFF22299DDF00C7F1862B /* Frameworks */,
69FE07EAE7EB9541D3177C23 /* ViewMonitorSwiftUIExample */,
98BB70B66795A38A430A5454 /* Signing.xcconfig */,
);
sourceTree = "<group>";
};
69FE07EAE7EB9541D3177C23 /* ViewMonitorSwiftUIExample */ = {
isa = PBXGroup;
children = (
0EBA02CAC4239B0C02C6F642 /* ViewMonitorSwiftUIExampleApp.swift */,
23C4B9CF1CC0C3CDB4A351F3 /* ContentView.swift */,
);
name = ViewMonitorSwiftUIExample;
path = ViewMonitorSwiftUIExample;
sourceTree = "<group>";
};
76ACFF22299DDF00C7F1862B /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
A3156E7133E7E80387CAD402 /* ViewMonitorSwiftUIExample */ = {
isa = PBXNativeTarget;
buildConfigurationList = BF1C0A3270B9F85CEA4CD32E /* Build configuration list for PBXNativeTarget "ViewMonitorSwiftUIExample" */;
buildPhases = (
63EB3E442AF8EA21899BB242 /* Sources */,
DA9FF015503021E80DBD010A /* Frameworks */,
5C16A2E3408B47ABAF1390CD /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = ViewMonitorSwiftUIExample;
packageProductDependencies = (
B3230F6343F7EFB6B6DF459F /* ViewMonitor */,
);
productName = ViewMonitorSwiftUIExample;
productReference = 509AB8F272B9805C09EF2A08 /* ViewMonitorSwiftUIExample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
98D0D799360F71CE242702AD /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1600;
LastUpgradeCheck = 1600;
};
buildConfigurationList = F3F42AAB82426067B2C33587 /* Build configuration list for PBXProject "ViewMonitorSwiftUIExample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 63C2FE9CC903968BEB57A378;
minimizedProjectReferenceProxies = 0;
packageReferences = (
6F1EE87498990AF7286F9126 /* XCLocalSwiftPackageReference ".." */,
);
preferredProjectObjectVersion = 100;
productRefGroup = 5EFCA5F5108BD64E6944B7E8 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
A3156E7133E7E80387CAD402 /* ViewMonitorSwiftUIExample */,
);
};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
5C16A2E3408B47ABAF1390CD /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
63EB3E442AF8EA21899BB242 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9D3E06DCEEC09745F88A635C /* ViewMonitorSwiftUIExampleApp.swift in Sources */,
C0CDF57005D23B4DCB014E29 /* ContentView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */

/* Begin XCBuildConfiguration section */
36575AD06C3E0C3A6D16849F /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 98BB70B66795A38A430A5454 /* Signing.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = NO;
"CODE_SIGNING_ALLOWED[sdk=iphonesimulator*]" = NO;
CODE_SIGN_STYLE = Automatic;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = daisuke.ViewMonitorSwiftUIExample;
SDKROOT = iphoneos;
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
4B835E7CDFC26D5D8E022AC8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 6.0;
};
name = Debug;
};
5FACFFF8B63151A51BDDD355 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 98BB70B66795A38A430A5454 /* Signing.xcconfig */;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = NO;
"CODE_SIGNING_ALLOWED[sdk=iphonesimulator*]" = NO;
CODE_SIGN_STYLE = Automatic;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = daisuke.ViewMonitorSwiftUIExample;
SDKROOT = iphoneos;
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
6607C4F36ADC408EF50C0438 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 6.0;
};
name = Release;
};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
BF1C0A3270B9F85CEA4CD32E /* Build configuration list for PBXNativeTarget "ViewMonitorSwiftUIExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5FACFFF8B63151A51BDDD355 /* Release */,
36575AD06C3E0C3A6D16849F /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F3F42AAB82426067B2C33587 /* Build configuration list for PBXProject "ViewMonitorSwiftUIExample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4B835E7CDFC26D5D8E022AC8 /* Debug */,
6607C4F36ADC408EF50C0438 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */

/* Begin XCLocalSwiftPackageReference section */
6F1EE87498990AF7286F9126 /* XCLocalSwiftPackageReference ".." */ = {
isa = XCLocalSwiftPackageReference;
relativePath = ../..;
};
/* End XCLocalSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
B3230F6343F7EFB6B6DF459F /* ViewMonitor */ = {
isa = XCSwiftPackageProductDependency;
productName = ViewMonitor;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 98D0D799360F71CE242702AD /* Project object */;
}
Loading
Loading