Fix TabBar icon assets and simplify Podfile post_install.
Rename tab icons to snake_case with @2x/@3x scales, register CYL plus button at app launch, and remove redundant Podfile hooks already covered by the Xcode project. Co-authored-by: Cursor <cursoragent@cursor.com>
56
Podfile
@ -7,7 +7,7 @@ target 'suixinkan_ios' do
|
||||
pod 'Kingfisher', '~> 8.0'
|
||||
pod 'CYLTabBarController', '~> 1.99.38'
|
||||
|
||||
# 高德地图 SDK:仅真机构建时链接;模拟器通过 post_install 剥离
|
||||
# 高德地图 SDK 仅支持真机;模拟器通过下方 post_install 剥离链接
|
||||
pod 'AMap3DMap-NO-IDFA', '~> 11.1'
|
||||
pod 'AMapSearch-NO-IDFA', '~> 9.7'
|
||||
pod 'AMapLocation-NO-IDFA', '~> 2.11'
|
||||
@ -17,6 +17,7 @@ target 'suixinkan_ios' do
|
||||
end
|
||||
end
|
||||
|
||||
# 模拟器构建时不链接高德(真机专用 framework),仅保留通用 Pod 依赖
|
||||
SIMULATOR_OTHER_LDFLAGS = <<~FLAGS.squish
|
||||
-ObjC -l"c++" -l"swiftCoreGraphics" -l"z"
|
||||
-framework "Accelerate" -framework "CFNetwork" -framework "CYLTabBarController"
|
||||
@ -32,6 +33,12 @@ post_install do |installer|
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '16.0'
|
||||
end
|
||||
|
||||
next unless target.name.start_with?('AMap')
|
||||
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['SUPPORTED_PLATFORMS'] = 'iphoneos'
|
||||
end
|
||||
end
|
||||
|
||||
%w[Pods-suixinkan_ios Pods-suixinkan_iosTests].each do |target_name|
|
||||
@ -48,51 +55,4 @@ post_install do |installer|
|
||||
File.write(path, content)
|
||||
end
|
||||
end
|
||||
|
||||
installer.pods_project.targets.each do |target|
|
||||
next unless target.name.start_with?('AMap')
|
||||
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['SUPPORTED_PLATFORMS'] = 'iphoneos'
|
||||
end
|
||||
end
|
||||
|
||||
resources_script = File.join(
|
||||
installer.sandbox.root,
|
||||
'Target Support Files/Pods-suixinkan_ios/Pods-suixinkan_ios-resources.sh'
|
||||
)
|
||||
if File.exist?(resources_script)
|
||||
content = File.read(resources_script)
|
||||
unless content.include?('SKIP_AMAP_RESOURCES_FOR_SIMULATOR')
|
||||
content.sub!(
|
||||
"RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt",
|
||||
<<~SHELL
|
||||
# SKIP_AMAP_RESOURCES_FOR_SIMULATOR
|
||||
if [ "${EFFECTIVE_PLATFORM_NAME}" = "-iphonesimulator" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
|
||||
SHELL
|
||||
)
|
||||
File.write(resources_script, content)
|
||||
end
|
||||
end
|
||||
|
||||
installer.aggregate_targets.each do |aggregate_target|
|
||||
aggregate_target.user_project.native_targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = ''
|
||||
config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO'
|
||||
|
||||
next unless target.name == 'suixinkan_ios'
|
||||
|
||||
existing = config.build_settings['SWIFT_ACTIVE_COMPILATION_CONDITIONS[sdk=iphoneos*]'] || '$(inherited)'
|
||||
unless existing.to_s.include?('AMAP_ENABLED')
|
||||
config.build_settings['SWIFT_ACTIVE_COMPILATION_CONDITIONS[sdk=iphoneos*]'] = "#{existing} AMAP_ENABLED"
|
||||
end
|
||||
end
|
||||
end
|
||||
aggregate_target.user_project.save
|
||||
end
|
||||
end
|
||||
|
||||
@ -39,6 +39,6 @@ SPEC CHECKSUMS:
|
||||
Kingfisher: db468f911dd666c9134dcbeec4db8bed52e4132d
|
||||
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
|
||||
|
||||
PODFILE CHECKSUM: 4068d11e2fdbc0a6514a9ec709302300b8e54da8
|
||||
PODFILE CHECKSUM: 9659fc63d0173f0286a7da18725e548d86c24ca6
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@ -55,6 +55,8 @@
|
||||
};
|
||||
A1TEST012FEE49BE00240726 /* suixinkan_iosTests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
path = suixinkan_iosTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@ -243,14 +245,10 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-suixinkan_ios/Pods-suixinkan_ios-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-suixinkan_ios/Pods-suixinkan_ios-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-suixinkan_ios/Pods-suixinkan_ios-resources.sh\"\n";
|
||||
@ -308,14 +306,10 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-suixinkan_ios/Pods-suixinkan_ios-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-suixinkan_ios/Pods-suixinkan_ios-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-suixinkan_ios/Pods-suixinkan_ios-frameworks.sh\"\n";
|
||||
|
||||
@ -33,13 +33,13 @@ enum AppTab: String, CaseIterable, Identifiable, Hashable {
|
||||
var selectedImageName: String {
|
||||
switch self {
|
||||
case .home:
|
||||
"TabHomeSelected"
|
||||
"tab_home_selected"
|
||||
case .orders:
|
||||
"TabOrderSelected"
|
||||
"tab_order_selected"
|
||||
case .statistics:
|
||||
"TabDataSelected"
|
||||
"tab_data_selected"
|
||||
case .profile:
|
||||
"TabProfileSelected"
|
||||
"tab_profile_selected"
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,13 +47,13 @@ enum AppTab: String, CaseIterable, Identifiable, Hashable {
|
||||
var unselectedImageName: String {
|
||||
switch self {
|
||||
case .home:
|
||||
"TabHomeUnselected"
|
||||
"tab_home_unselected"
|
||||
case .orders:
|
||||
"TabOrderUnselected"
|
||||
"tab_order_unselected"
|
||||
case .statistics:
|
||||
"TabDataUnselected"
|
||||
"tab_data_unselected"
|
||||
case .profile:
|
||||
"TabProfileUnselected"
|
||||
"tab_profile_unselected"
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,6 @@ enum AppTab: String, CaseIterable, Identifiable, Hashable {
|
||||
image: unselectedImage,
|
||||
selectedImage: selectedImage
|
||||
)
|
||||
item.accessibilityIdentifier = "main.tab.\(rawValue)"
|
||||
return item
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
23
suixinkan_ios/Assets.xcassets/icon_scan.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_scan.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_scan@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_scan@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
suixinkan_ios/Assets.xcassets/icon_scan.imageset/icon_scan.png
vendored
Normal file
|
After Width: | Height: | Size: 381 B |
BIN
suixinkan_ios/Assets.xcassets/icon_scan.imageset/icon_scan@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 675 B |
BIN
suixinkan_ios/Assets.xcassets/icon_scan.imageset/icon_scan@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1022 B |
@ -6,10 +6,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_data_selected@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_data_selected@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
BIN
suixinkan_ios/Assets.xcassets/tab_data_selected.imageset/tab_data_selected.png
vendored
Normal file
|
After Width: | Height: | Size: 908 B |
BIN
suixinkan_ios/Assets.xcassets/tab_data_selected.imageset/tab_data_selected@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
suixinkan_ios/Assets.xcassets/tab_data_selected.imageset/tab_data_selected@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
@ -6,10 +6,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_data_unselected@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_data_unselected@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
BIN
suixinkan_ios/Assets.xcassets/tab_data_unselected.imageset/tab_data_unselected.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
suixinkan_ios/Assets.xcassets/tab_data_unselected.imageset/tab_data_unselected@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
suixinkan_ios/Assets.xcassets/tab_data_unselected.imageset/tab_data_unselected@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
@ -6,10 +6,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_home_selected@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_home_selected@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
BIN
suixinkan_ios/Assets.xcassets/tab_home_selected.imageset/tab_home_selected.png
vendored
Normal file
|
After Width: | Height: | Size: 867 B |
BIN
suixinkan_ios/Assets.xcassets/tab_home_selected.imageset/tab_home_selected@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
suixinkan_ios/Assets.xcassets/tab_home_selected.imageset/tab_home_selected@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
@ -6,10 +6,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_home_unselected@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_home_unselected@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
BIN
suixinkan_ios/Assets.xcassets/tab_home_unselected.imageset/tab_home_unselected.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
suixinkan_ios/Assets.xcassets/tab_home_unselected.imageset/tab_home_unselected@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
suixinkan_ios/Assets.xcassets/tab_home_unselected.imageset/tab_home_unselected@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
@ -6,10 +6,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_order_selected@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_order_selected@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
BIN
suixinkan_ios/Assets.xcassets/tab_order_selected.imageset/tab_order_selected.png
vendored
Normal file
|
After Width: | Height: | Size: 733 B |
BIN
suixinkan_ios/Assets.xcassets/tab_order_selected.imageset/tab_order_selected@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
suixinkan_ios/Assets.xcassets/tab_order_selected.imageset/tab_order_selected@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
@ -6,10 +6,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_order_unselected@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_order_unselected@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
BIN
suixinkan_ios/Assets.xcassets/tab_order_unselected.imageset/tab_order_unselected.png
vendored
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
suixinkan_ios/Assets.xcassets/tab_order_unselected.imageset/tab_order_unselected@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
suixinkan_ios/Assets.xcassets/tab_order_unselected.imageset/tab_order_unselected@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
@ -6,10 +6,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_profile_selected@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_profile_selected@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
BIN
suixinkan_ios/Assets.xcassets/tab_profile_selected.imageset/tab_profile_selected.png
vendored
Normal file
|
After Width: | Height: | Size: 710 B |
BIN
suixinkan_ios/Assets.xcassets/tab_profile_selected.imageset/tab_profile_selected@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
suixinkan_ios/Assets.xcassets/tab_profile_selected.imageset/tab_profile_selected@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
@ -6,10 +6,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_profile_unselected@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "tab_profile_unselected@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
BIN
suixinkan_ios/Assets.xcassets/tab_profile_unselected.imageset/tab_profile_unselected.png
vendored
Normal file
|
After Width: | Height: | Size: 944 B |
BIN
suixinkan_ios/Assets.xcassets/tab_profile_unselected.imageset/tab_profile_unselected@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
suixinkan_ios/Assets.xcassets/tab_profile_unselected.imageset/tab_profile_unselected@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
@ -18,12 +18,11 @@ final class MainTabBarController: CYLTabBarController {
|
||||
/// 初始化实例。
|
||||
init(services: AppServices) {
|
||||
self.services = services
|
||||
MainScanPlusButton.register()
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
MainScanPlusButton.onScanTap = { [weak self] in
|
||||
self?.presentGlobalScanner()
|
||||
}
|
||||
delegate = self
|
||||
// delegate = self
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
@ -86,11 +85,10 @@ final class MainTabBarController: CYLTabBarController {
|
||||
tabNavigationControllers[tab] = navigationController
|
||||
return navigationController
|
||||
}
|
||||
applyTabAccessibilityIdentifiers()
|
||||
}
|
||||
|
||||
/// 构建 CYLTabBarController 识别的 TabBarItem 属性字典。
|
||||
private func makeTabBarItemAttributes(for tab: AppTab) -> [String: Any] {
|
||||
private func makeTabBarItemAttributes(for tab: AppTab) -> [String: String] {
|
||||
[
|
||||
CYLTabBarItemTitle: tab.title,
|
||||
CYLTabBarItemImage: tab.unselectedImageName,
|
||||
@ -98,13 +96,6 @@ final class MainTabBarController: CYLTabBarController {
|
||||
]
|
||||
}
|
||||
|
||||
/// CYL 会根据属性字典重建 TabBarItem,因此在设置控制器后补充自动化标识。
|
||||
private func applyTabAccessibilityIdentifiers() {
|
||||
for tab in AppTab.allCases {
|
||||
tabNavigationControllers[tab]?.tabBarItem.accessibilityIdentifier = "main.tab.\(tab.rawValue)"
|
||||
}
|
||||
}
|
||||
|
||||
/// 绑定角标 ViewModel 回调。
|
||||
private func bindBadgeViewModel() {
|
||||
badgeViewModel.onChange = { [weak self] in
|
||||
|
||||
@ -16,10 +16,20 @@ final class MainScanPlusButton: CYLPlusButton, CYLPlusButtonSubclassing {
|
||||
|
||||
/// 构建中间扫码按钮实例,供 CYLTabBarController 注册使用。
|
||||
static func plusButton() -> Any {
|
||||
let button = MainScanPlusButton(type: .custom)
|
||||
button.frame = CGRect(x: 0, y: 0, width: 70, height: 64)
|
||||
button.configureAppearance()
|
||||
button.addTarget(button, action: #selector(handleScanTap), for: .touchUpInside)
|
||||
let button = MainScanPlusButton()
|
||||
button.setImage(UIImage(named: "icon_scan"), for: .normal)
|
||||
button.titleLabel?.textAlignment = .center
|
||||
|
||||
button.titleLabel?.font = UIFont.systemFont(ofSize: 10)
|
||||
|
||||
button.setTitle("发布", for: .normal)
|
||||
button.setTitleColor(UIColor.gray, for: .normal)
|
||||
|
||||
button.setTitle("选中", for: .selected)
|
||||
button.setTitleColor(UIColor(red:255.0/255.0,green:102.0/255.0,blue:0,alpha:1.0), for: .selected)
|
||||
|
||||
button.adjustsImageWhenHighlighted = false
|
||||
button.sizeToFit()
|
||||
return button
|
||||
}
|
||||
|
||||
@ -46,7 +56,7 @@ final class MainScanPlusButton: CYLPlusButton, CYLPlusButtonSubclassing {
|
||||
|
||||
let symbolConfig = UIImage.SymbolConfiguration(pointSize: 30, weight: .bold)
|
||||
let iconView = UIImageView(
|
||||
image: UIImage(systemName: "qrcode.viewfinder", withConfiguration: symbolConfig)
|
||||
image: UIImage(named: "icon_scan")
|
||||
)
|
||||
iconView.tintColor = .white
|
||||
iconView.contentMode = .scaleAspectFit
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import CYLTabBarController
|
||||
|
||||
/// 窗口 Scene 代理,负责创建根窗口并挂载会话协调器。
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
@ -23,6 +24,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
) {
|
||||
guard let windowScene = scene as? UIWindowScene else { return }
|
||||
|
||||
MainScanPlusButton.register()
|
||||
|
||||
AppUITestLaunchState.resetIfNeeded()
|
||||
|
||||
let rootViewController = RootViewController()
|
||||
|
||||
@ -41,6 +41,21 @@ final class MainScanPlusButtonTests: XCTestCase {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// 测试各 Tab 的图标资源可被加载并挂载到 tabBarItem。
|
||||
func testMainTabBarControllerTabBarItemsHaveImages() {
|
||||
let services = AppServices(apiClient: APIClient(session: MainTabRecordingURLSession()))
|
||||
let controller = MainTabBarController(services: services)
|
||||
controller.loadViewIfNeeded()
|
||||
|
||||
for tab in AppTab.allCases {
|
||||
let item = controller.navigationController(for: tab)?.tabBarItem
|
||||
XCTAssertNotNil(item?.image, "tab \(tab.rawValue) image should not be nil")
|
||||
XCTAssertNotNil(item?.selectedImage, "tab \(tab.rawValue) selectedImage should not be nil")
|
||||
XCTAssertGreaterThan(item?.image?.size.width ?? 0, 0)
|
||||
XCTAssertGreaterThan(item?.selectedImage?.size.width ?? 0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 主 Tab 测试使用的空网络会话,确保容器加载不触发真实请求。
|
||||
|
||||