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>
@ -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()
|
||||
|
||||