@thelacanians/vue-native-cli 0.4.3 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +34 -17
- package/native/android/README.md +205 -0
- package/native/android/VueNativeCore/build.gradle.kts +100 -0
- package/native/android/VueNativeCore/consumer-rules.pro +12 -0
- package/native/android/VueNativeCore/proguard-rules.pro +33 -0
- package/native/android/VueNativeCore/src/main/AndroidManifest.xml +17 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/ErrorOverlayView.kt +94 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/HotReloadManager.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSPolyfills.kt +652 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSRuntime.kt +207 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +417 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/ComponentRegistry.kt +76 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActionSheetFactory.kt +78 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActivityIndicatorFactory.kt +46 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VAlertDialogFactory.kt +84 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VButtonFactory.kt +73 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VCheckboxFactory.kt +93 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VDropdownFactory.kt +125 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VImageFactory.kt +75 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VInputFactory.kt +210 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VKeyboardAvoidingFactory.kt +31 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VListFactory.kt +183 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VModalFactory.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPickerFactory.kt +57 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPressableFactory.kt +109 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VProgressBarFactory.kt +43 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRadioFactory.kt +103 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRefreshControlFactory.kt +73 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRootFactory.kt +39 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSafeAreaFactory.kt +48 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VScrollViewFactory.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSectionListFactory.kt +144 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSegmentedControlFactory.kt +77 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSliderFactory.kt +74 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VStatusBarFactory.kt +52 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSwitchFactory.kt +62 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VTextFactory.kt +53 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VVideoFactory.kt +191 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +48 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VWebViewFactory.kt +90 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/NativeComponentFactory.kt +40 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/VTextNodeView.kt +23 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/GestureHelper.kt +16 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/TouchableView.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AnimationModule.kt +292 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AppStateModule.kt +41 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AsyncStorageModule.kt +59 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AudioModule.kt +331 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BackgroundTaskModule.kt +166 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BiometryModule.kt +56 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BluetoothModule.kt +302 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CalendarModule.kt +198 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CameraModule.kt +64 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ClipboardModule.kt +36 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ContactsModule.kt +288 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DatabaseModule.kt +229 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DeviceInfoModule.kt +39 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/FileSystemModule.kt +193 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeolocationModule.kt +68 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HapticsModule.kt +61 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HttpModule.kt +111 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/IAPModule.kt +302 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/KeyboardModule.kt +26 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/LinkingModule.kt +43 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModule.kt +27 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +92 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NetworkModule.kt +75 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NotificationsModule.kt +181 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/OTAModule.kt +255 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PerformanceModule.kt +147 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PermissionsModule.kt +126 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SecureStorageModule.kt +51 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SensorsModule.kt +134 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ShareModule.kt +36 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SocialAuthModule.kt +160 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/WebSocketModule.kt +155 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Styling/StyleEngine.kt +802 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Tags.kt +43 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/VueNativeActivity.kt +169 -0
- package/native/android/VueNativeCore/src/main/res/values/ids.xml +8 -0
- package/native/android/app/build.gradle.kts +45 -0
- package/native/android/app/proguard-rules.pro +5 -0
- package/native/android/app/src/main/AndroidManifest.xml +25 -0
- package/native/android/app/src/main/assets/.gitkeep +0 -0
- package/native/android/app/src/main/kotlin/com/vuenative/example/counter/MainActivity.kt +14 -0
- package/native/android/app/src/main/res/layout/activity_main.xml +6 -0
- package/native/android/app/src/main/res/values/strings.xml +3 -0
- package/native/android/app/src/main/res/values/themes.xml +9 -0
- package/native/android/app/src/main/res/xml/network_security_config.xml +8 -0
- package/native/android/build.gradle.kts +6 -0
- package/native/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/native/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/native/android/gradle.properties +4 -0
- package/native/android/gradlew +87 -0
- package/native/android/gradlew.bat +48 -0
- package/native/android/settings.gradle.kts +20 -0
- package/native/ios/VueNativeCore/Package.resolved +23 -0
- package/native/ios/VueNativeCore/Package.swift +32 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/CertificatePinning.swift +132 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/ErrorOverlayView.swift +92 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/HotReloadManager.swift +147 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/JSPolyfills.swift +711 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/JSRuntime.swift +421 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +891 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/VueNativeViewController.swift +88 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/ComponentRegistry.swift +193 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VActionSheetFactory.swift +91 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VActivityIndicatorFactory.swift +74 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VAlertDialogFactory.swift +150 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VButtonFactory.swift +93 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VCheckboxFactory.swift +114 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VDropdownFactory.swift +112 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VImageFactory.swift +172 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VInputFactory.swift +357 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VKeyboardAvoidingFactory.swift +99 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VListFactory.swift +250 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VModalFactory.swift +112 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VPickerFactory.swift +96 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VPressableFactory.swift +168 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VProgressBarFactory.swift +39 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VRadioFactory.swift +167 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VRefreshControlFactory.swift +153 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSafeAreaFactory.swift +56 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VScrollViewFactory.swift +240 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSectionListFactory.swift +248 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSegmentedControlFactory.swift +73 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSliderFactory.swift +63 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VStatusBarFactory.swift +50 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSwitchFactory.swift +108 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +290 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VVideoFactory.swift +246 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +157 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VWebViewFactory.swift +172 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/NativeComponentFactory.swift +53 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +107 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/TouchableView.swift +136 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/UIColor+Hex.swift +80 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AnimationModule.swift +291 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AppStateModule.swift +65 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AsyncStorageModule.swift +68 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AudioModule.swift +366 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BackgroundTaskModule.swift +135 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BiometryModule.swift +61 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BluetoothModule.swift +387 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/CalendarModule.swift +161 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/CameraModule.swift +318 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ClipboardModule.swift +33 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ContactsModule.swift +173 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/DatabaseModule.swift +259 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/DeviceInfoModule.swift +34 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/FileSystemModule.swift +233 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeolocationModule.swift +147 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/HapticsModule.swift +50 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/IAPModule.swift +194 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/KeyboardModule.swift +31 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/LinkingModule.swift +42 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModule.swift +28 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +78 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NetworkModule.swift +62 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NotificationsModule.swift +215 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/OTAModule.swift +281 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/PerformanceModule.swift +138 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/PermissionsModule.swift +190 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SecureStorageModule.swift +118 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SensorsModule.swift +103 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ShareModule.swift +49 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SocialAuthModule.swift +240 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/WebSocketModule.swift +213 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Resources/vue-native-placeholder.js +8 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Styling/StyleEngine.swift +885 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSRuntimeTests.swift +362 -0
- package/package.json +3 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#if canImport(UIKit)
|
|
2
|
+
import UIKit
|
|
3
|
+
import FlexLayout
|
|
4
|
+
import ObjectiveC
|
|
5
|
+
|
|
6
|
+
private var segOnChangeKey: UInt8 = 0
|
|
7
|
+
private var segTargetKey: UInt8 = 1
|
|
8
|
+
|
|
9
|
+
final class VSegmentedControlFactory: NativeComponentFactory {
|
|
10
|
+
|
|
11
|
+
func createView() -> UIView {
|
|
12
|
+
let seg = UISegmentedControl()
|
|
13
|
+
_ = seg.flex
|
|
14
|
+
return seg
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
func updateProp(view: UIView, key: String, value: Any?) {
|
|
18
|
+
guard let seg = view as? UISegmentedControl else {
|
|
19
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
switch key {
|
|
23
|
+
case "values":
|
|
24
|
+
guard let items = value as? [String] else { return }
|
|
25
|
+
seg.removeAllSegments()
|
|
26
|
+
for (i, item) in items.enumerated() {
|
|
27
|
+
seg.insertSegment(withTitle: item, at: i, animated: false)
|
|
28
|
+
}
|
|
29
|
+
case "selectedIndex":
|
|
30
|
+
let idx = (value as? Int) ?? (value as? NSNumber)?.intValue ?? 0
|
|
31
|
+
seg.selectedSegmentIndex = idx
|
|
32
|
+
case "tintColor":
|
|
33
|
+
if let str = value as? String { seg.selectedSegmentTintColor = UIColor.fromHex(str) }
|
|
34
|
+
case "enabled":
|
|
35
|
+
seg.isEnabled = (value as? Bool) ?? (value as? NSNumber)?.boolValue ?? true
|
|
36
|
+
default:
|
|
37
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {
|
|
42
|
+
guard event == "change", let seg = view as? UISegmentedControl else { return }
|
|
43
|
+
objc_setAssociatedObject(view, &segOnChangeKey, handler as AnyObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
44
|
+
let target = SegmentedTarget(view: view)
|
|
45
|
+
objc_setAssociatedObject(view, &segTargetKey, target, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
46
|
+
seg.addTarget(target, action: #selector(SegmentedTarget.handleChange(_:)), for: .valueChanged)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func removeEventListener(view: UIView, event: String) {
|
|
50
|
+
if event == "change" {
|
|
51
|
+
// Remove the UIControl target before clearing the reference
|
|
52
|
+
if let target = objc_getAssociatedObject(view, &segTargetKey) as? SegmentedTarget,
|
|
53
|
+
let seg = view as? UISegmentedControl {
|
|
54
|
+
seg.removeTarget(target, action: #selector(SegmentedTarget.handleChange(_:)), for: .valueChanged)
|
|
55
|
+
}
|
|
56
|
+
objc_setAssociatedObject(view, &segOnChangeKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
57
|
+
objc_setAssociatedObject(view, &segTargetKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private final class SegmentedTarget: NSObject {
|
|
63
|
+
private weak var view: UIView?
|
|
64
|
+
init(view: UIView) { self.view = view }
|
|
65
|
+
|
|
66
|
+
@objc func handleChange(_ seg: UISegmentedControl) {
|
|
67
|
+
guard let view = view else { return }
|
|
68
|
+
if let handler = objc_getAssociatedObject(view, &segOnChangeKey) as? ((Any?) -> Void) {
|
|
69
|
+
handler(["selectedIndex": seg.selectedSegmentIndex, "value": seg.titleForSegment(at: seg.selectedSegmentIndex) ?? ""])
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
#endif
|
package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSliderFactory.swift
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#if canImport(UIKit)
|
|
2
|
+
import UIKit
|
|
3
|
+
import FlexLayout
|
|
4
|
+
import ObjectiveC
|
|
5
|
+
|
|
6
|
+
/// Factory for VSlider — maps to UISlider.
|
|
7
|
+
final class VSliderFactory: NativeComponentFactory {
|
|
8
|
+
|
|
9
|
+
private static var onChangeKey: UInt8 = 0
|
|
10
|
+
|
|
11
|
+
func createView() -> UIView {
|
|
12
|
+
let slider = UISlider()
|
|
13
|
+
slider.minimumValue = 0
|
|
14
|
+
slider.maximumValue = 1
|
|
15
|
+
_ = slider.flex
|
|
16
|
+
return slider
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
func updateProp(view: UIView, key: String, value: Any?) {
|
|
20
|
+
guard let slider = view as? UISlider else {
|
|
21
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
switch key {
|
|
25
|
+
case "value":
|
|
26
|
+
slider.value = Float(value as? Double ?? Double(value as? Float ?? 0))
|
|
27
|
+
case "minimumValue", "min":
|
|
28
|
+
slider.minimumValue = Float(value as? Double ?? 0)
|
|
29
|
+
case "maximumValue", "max":
|
|
30
|
+
slider.maximumValue = Float(value as? Double ?? 1)
|
|
31
|
+
case "minimumTrackTintColor":
|
|
32
|
+
slider.minimumTrackTintColor = UIColor.fromHex(value as? String ?? "")
|
|
33
|
+
case "maximumTrackTintColor":
|
|
34
|
+
slider.maximumTrackTintColor = UIColor.fromHex(value as? String ?? "")
|
|
35
|
+
default:
|
|
36
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {
|
|
41
|
+
guard event == "change", let slider = view as? UISlider else { return }
|
|
42
|
+
let target = SliderTarget(handler: handler)
|
|
43
|
+
slider.addTarget(target, action: #selector(SliderTarget.handleValueChanged(_:)), for: .valueChanged)
|
|
44
|
+
objc_setAssociatedObject(slider, &VSliderFactory.onChangeKey, target, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func removeEventListener(view: UIView, event: String) {
|
|
48
|
+
guard event == "change", let slider = view as? UISlider else { return }
|
|
49
|
+
if let target = objc_getAssociatedObject(slider, &VSliderFactory.onChangeKey) as? SliderTarget {
|
|
50
|
+
slider.removeTarget(target, action: #selector(SliderTarget.handleValueChanged(_:)), for: .valueChanged)
|
|
51
|
+
}
|
|
52
|
+
objc_setAssociatedObject(slider, &VSliderFactory.onChangeKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private final class SliderTarget: NSObject {
|
|
57
|
+
let handler: (Any?) -> Void
|
|
58
|
+
init(handler: @escaping (Any?) -> Void) { self.handler = handler }
|
|
59
|
+
@objc func handleValueChanged(_ slider: UISlider) {
|
|
60
|
+
handler(Double(slider.value))
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
#endif
|
package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VStatusBarFactory.swift
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#if canImport(UIKit)
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/// Factory for VStatusBar — a zero-size, hidden placeholder view that controls
|
|
5
|
+
/// the system status bar appearance by posting notifications to the root
|
|
6
|
+
/// view controller, which must observe them to update its `preferredStatusBarStyle`
|
|
7
|
+
/// and `prefersStatusBarHidden` overrides.
|
|
8
|
+
@MainActor
|
|
9
|
+
final class VStatusBarFactory: NativeComponentFactory {
|
|
10
|
+
|
|
11
|
+
func createView() -> UIView {
|
|
12
|
+
let v = UIView()
|
|
13
|
+
v.isHidden = true
|
|
14
|
+
return v
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
func updateProp(view: UIView, key: String, value: Any?) {
|
|
18
|
+
switch key {
|
|
19
|
+
case "barStyle":
|
|
20
|
+
guard let style = value as? String else { return }
|
|
21
|
+
DispatchQueue.main.async {
|
|
22
|
+
NotificationCenter.default.post(
|
|
23
|
+
name: Notification.Name("VueNativeStatusBarStyleChange"),
|
|
24
|
+
object: nil,
|
|
25
|
+
userInfo: ["style": style]
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
case "hidden":
|
|
29
|
+
let hidden = (value as? Bool) ?? (value as? NSNumber)?.boolValue ?? false
|
|
30
|
+
DispatchQueue.main.async {
|
|
31
|
+
NotificationCenter.default.post(
|
|
32
|
+
name: Notification.Name("VueNativeStatusBarHiddenChange"),
|
|
33
|
+
object: nil,
|
|
34
|
+
userInfo: ["hidden": hidden]
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
default:
|
|
38
|
+
break
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {
|
|
43
|
+
// VStatusBar emits no events
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func removeEventListener(view: UIView, event: String) {
|
|
47
|
+
// VStatusBar emits no events
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
#endif
|
package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSwitchFactory.swift
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#if canImport(UIKit)
|
|
2
|
+
import UIKit
|
|
3
|
+
import ObjectiveC
|
|
4
|
+
import FlexLayout
|
|
5
|
+
|
|
6
|
+
/// Factory for VSwitch — the boolean toggle switch component.
|
|
7
|
+
/// Maps to UISwitch with FlexLayout enabled.
|
|
8
|
+
/// Supports v-model via the `value` prop and `change` event.
|
|
9
|
+
final class VSwitchFactory: NativeComponentFactory {
|
|
10
|
+
|
|
11
|
+
private static var changeHandlerKey: UInt8 = 0
|
|
12
|
+
|
|
13
|
+
// MARK: - NativeComponentFactory
|
|
14
|
+
|
|
15
|
+
func createView() -> UIView {
|
|
16
|
+
let sw = UISwitch()
|
|
17
|
+
_ = sw.flex
|
|
18
|
+
return sw
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func updateProp(view: UIView, key: String, value: Any?) {
|
|
22
|
+
guard let sw = view as? UISwitch else {
|
|
23
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
switch key {
|
|
28
|
+
case "value":
|
|
29
|
+
let newValue: Bool
|
|
30
|
+
if let val = value as? Bool {
|
|
31
|
+
newValue = val
|
|
32
|
+
} else if let val = value as? Int {
|
|
33
|
+
newValue = val != 0
|
|
34
|
+
} else {
|
|
35
|
+
newValue = false
|
|
36
|
+
}
|
|
37
|
+
if sw.isOn != newValue {
|
|
38
|
+
sw.setOn(newValue, animated: true)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
case "disabled":
|
|
42
|
+
if let disabled = value as? Bool {
|
|
43
|
+
sw.isEnabled = !disabled
|
|
44
|
+
} else if let disabled = value as? Int {
|
|
45
|
+
sw.isEnabled = disabled == 0
|
|
46
|
+
} else {
|
|
47
|
+
sw.isEnabled = true
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
case "onTintColor":
|
|
51
|
+
if let colorStr = value as? String {
|
|
52
|
+
sw.onTintColor = UIColor.fromHex(colorStr)
|
|
53
|
+
} else {
|
|
54
|
+
sw.onTintColor = nil
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
case "thumbTintColor":
|
|
58
|
+
if let colorStr = value as? String {
|
|
59
|
+
sw.thumbTintColor = UIColor.fromHex(colorStr)
|
|
60
|
+
} else {
|
|
61
|
+
sw.thumbTintColor = nil
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
default:
|
|
65
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {
|
|
70
|
+
guard let sw = view as? UISwitch, event == "change" else { return }
|
|
71
|
+
|
|
72
|
+
let proxy = SwitchActionProxy(handler: handler)
|
|
73
|
+
sw.addTarget(proxy, action: #selector(SwitchActionProxy.valueChanged(_:)), for: .valueChanged)
|
|
74
|
+
objc_setAssociatedObject(
|
|
75
|
+
sw,
|
|
76
|
+
&VSwitchFactory.changeHandlerKey,
|
|
77
|
+
proxy,
|
|
78
|
+
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
func removeEventListener(view: UIView, event: String) {
|
|
83
|
+
guard let sw = view as? UISwitch, event == "change" else { return }
|
|
84
|
+
|
|
85
|
+
if let proxy = objc_getAssociatedObject(sw, &VSwitchFactory.changeHandlerKey) as? SwitchActionProxy {
|
|
86
|
+
sw.removeTarget(proxy, action: #selector(SwitchActionProxy.valueChanged(_:)), for: .valueChanged)
|
|
87
|
+
}
|
|
88
|
+
objc_setAssociatedObject(sw, &VSwitchFactory.changeHandlerKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// MARK: - SwitchActionProxy
|
|
93
|
+
|
|
94
|
+
/// Target-action proxy that routes UISwitch.valueChanged to a closure handler.
|
|
95
|
+
/// Stored as an associated object on the UISwitch.
|
|
96
|
+
final class SwitchActionProxy: NSObject {
|
|
97
|
+
|
|
98
|
+
let handler: (Any?) -> Void
|
|
99
|
+
|
|
100
|
+
init(handler: @escaping (Any?) -> Void) {
|
|
101
|
+
self.handler = handler
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@objc func valueChanged(_ sender: UISwitch) {
|
|
105
|
+
handler(sender.isOn)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
#endif
|
package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
#if canImport(UIKit)
|
|
2
|
+
import UIKit
|
|
3
|
+
import FlexLayout
|
|
4
|
+
|
|
5
|
+
/// Factory for VText — the text display component.
|
|
6
|
+
/// Maps to a UILabel with FlexLayout enabled.
|
|
7
|
+
/// After any text or font change, calls flex.markDirty() to trigger Yoga remeasurement.
|
|
8
|
+
final class VTextFactory: NativeComponentFactory {
|
|
9
|
+
|
|
10
|
+
// MARK: - Font weight mapping
|
|
11
|
+
|
|
12
|
+
/// Maps CSS-like font weight strings to UIFont.Weight values.
|
|
13
|
+
static let fontWeightMap: [String: UIFont.Weight] = [
|
|
14
|
+
"100": .ultraLight,
|
|
15
|
+
"200": .thin,
|
|
16
|
+
"300": .light,
|
|
17
|
+
"400": .regular,
|
|
18
|
+
"normal": .regular,
|
|
19
|
+
"500": .medium,
|
|
20
|
+
"600": .semibold,
|
|
21
|
+
"semibold": .semibold,
|
|
22
|
+
"700": .bold,
|
|
23
|
+
"bold": .bold,
|
|
24
|
+
"800": .heavy,
|
|
25
|
+
"900": .black,
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
// MARK: - Text alignment mapping
|
|
29
|
+
|
|
30
|
+
static let textAlignMap: [String: NSTextAlignment] = [
|
|
31
|
+
"left": .left,
|
|
32
|
+
"center": .center,
|
|
33
|
+
"right": .right,
|
|
34
|
+
"justify": .justified,
|
|
35
|
+
"auto": .natural,
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
// MARK: - Associated object keys for stored state
|
|
39
|
+
|
|
40
|
+
private static var fontSizeKey: UInt8 = 0
|
|
41
|
+
private static var fontWeightKey: UInt8 = 0
|
|
42
|
+
private static var fontFamilyKey: UInt8 = 0
|
|
43
|
+
|
|
44
|
+
// MARK: - NativeComponentFactory
|
|
45
|
+
|
|
46
|
+
func createView() -> UIView {
|
|
47
|
+
let label = UILabel()
|
|
48
|
+
// Multi-line by default
|
|
49
|
+
label.numberOfLines = 0
|
|
50
|
+
// Accessing .flex automatically enables Yoga layout
|
|
51
|
+
_ = label.flex
|
|
52
|
+
return label
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
func updateProp(view: UIView, key: String, value: Any?) {
|
|
56
|
+
guard let label = view as? UILabel else { return }
|
|
57
|
+
|
|
58
|
+
switch key {
|
|
59
|
+
case "text":
|
|
60
|
+
if let text = value as? String {
|
|
61
|
+
label.text = text
|
|
62
|
+
} else {
|
|
63
|
+
label.text = nil
|
|
64
|
+
}
|
|
65
|
+
label.flex.markDirty()
|
|
66
|
+
|
|
67
|
+
case "numberOfLines":
|
|
68
|
+
if let lines = value as? Int {
|
|
69
|
+
label.numberOfLines = lines
|
|
70
|
+
} else if let lines = value as? Double {
|
|
71
|
+
label.numberOfLines = Int(lines)
|
|
72
|
+
} else {
|
|
73
|
+
label.numberOfLines = 0
|
|
74
|
+
}
|
|
75
|
+
label.flex.markDirty()
|
|
76
|
+
|
|
77
|
+
case "color":
|
|
78
|
+
if let colorStr = value as? String {
|
|
79
|
+
label.textColor = UIColor.fromHex(colorStr)
|
|
80
|
+
} else {
|
|
81
|
+
label.textColor = .label
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
case "fontSize":
|
|
85
|
+
let size: CGFloat
|
|
86
|
+
if let num = value as? Double {
|
|
87
|
+
size = CGFloat(num)
|
|
88
|
+
} else if let num = value as? Int {
|
|
89
|
+
size = CGFloat(num)
|
|
90
|
+
} else {
|
|
91
|
+
size = 17.0 // System default
|
|
92
|
+
}
|
|
93
|
+
storeFontSize(size, on: label)
|
|
94
|
+
rebuildFont(for: label)
|
|
95
|
+
label.flex.markDirty()
|
|
96
|
+
|
|
97
|
+
case "fontWeight":
|
|
98
|
+
if let str = value as? String {
|
|
99
|
+
storeFontWeight(str, on: label)
|
|
100
|
+
} else {
|
|
101
|
+
storeFontWeight(nil, on: label)
|
|
102
|
+
}
|
|
103
|
+
rebuildFont(for: label)
|
|
104
|
+
label.flex.markDirty()
|
|
105
|
+
|
|
106
|
+
case "fontFamily":
|
|
107
|
+
if let family = value as? String {
|
|
108
|
+
storeFontFamily(family, on: label)
|
|
109
|
+
} else {
|
|
110
|
+
storeFontFamily(nil, on: label)
|
|
111
|
+
}
|
|
112
|
+
rebuildFont(for: label)
|
|
113
|
+
label.flex.markDirty()
|
|
114
|
+
|
|
115
|
+
case "textAlign":
|
|
116
|
+
if let alignStr = value as? String {
|
|
117
|
+
label.textAlignment = VTextFactory.textAlignMap[alignStr] ?? .natural
|
|
118
|
+
} else {
|
|
119
|
+
label.textAlignment = .natural
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case "lineBreakMode":
|
|
123
|
+
if let mode = value as? String {
|
|
124
|
+
switch mode {
|
|
125
|
+
case "clip": label.lineBreakMode = .byClipping
|
|
126
|
+
case "head": label.lineBreakMode = .byTruncatingHead
|
|
127
|
+
case "middle": label.lineBreakMode = .byTruncatingMiddle
|
|
128
|
+
case "tail": label.lineBreakMode = .byTruncatingTail
|
|
129
|
+
case "wordwrap": label.lineBreakMode = .byWordWrapping
|
|
130
|
+
default: label.lineBreakMode = .byTruncatingTail
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
case "fontStyle":
|
|
135
|
+
if let str = value as? String {
|
|
136
|
+
let currentSize = label.font.pointSize
|
|
137
|
+
if str == "italic" {
|
|
138
|
+
let descriptor = label.font.fontDescriptor.withSymbolicTraits(.traitItalic) ?? label.font.fontDescriptor
|
|
139
|
+
label.font = UIFont(descriptor: descriptor, size: currentSize)
|
|
140
|
+
} else {
|
|
141
|
+
var traits = label.font.fontDescriptor.symbolicTraits
|
|
142
|
+
traits.remove(.traitItalic)
|
|
143
|
+
if let descriptor = label.font.fontDescriptor.withSymbolicTraits(traits) {
|
|
144
|
+
label.font = UIFont(descriptor: descriptor, size: currentSize)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
label.flex.markDirty()
|
|
149
|
+
|
|
150
|
+
case "lineHeight":
|
|
151
|
+
if let num = value as? Double {
|
|
152
|
+
let paragraphStyle = NSMutableParagraphStyle()
|
|
153
|
+
paragraphStyle.minimumLineHeight = CGFloat(num)
|
|
154
|
+
paragraphStyle.maximumLineHeight = CGFloat(num)
|
|
155
|
+
paragraphStyle.alignment = label.textAlignment
|
|
156
|
+
let text = label.text ?? ""
|
|
157
|
+
let attrs: [NSAttributedString.Key: Any] = [
|
|
158
|
+
.paragraphStyle: paragraphStyle,
|
|
159
|
+
.font: label.font as Any
|
|
160
|
+
]
|
|
161
|
+
label.attributedText = NSAttributedString(string: text, attributes: attrs)
|
|
162
|
+
label.flex.markDirty()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
case "letterSpacing":
|
|
166
|
+
if let num = value as? Double {
|
|
167
|
+
let text = label.text ?? ""
|
|
168
|
+
let attrs: [NSAttributedString.Key: Any] = [
|
|
169
|
+
.kern: CGFloat(num),
|
|
170
|
+
.font: label.font as Any
|
|
171
|
+
]
|
|
172
|
+
label.attributedText = NSAttributedString(string: text, attributes: attrs)
|
|
173
|
+
label.flex.markDirty()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
case "textDecorationLine":
|
|
177
|
+
if let str = value as? String {
|
|
178
|
+
let text = label.text ?? ""
|
|
179
|
+
var attrs: [NSAttributedString.Key: Any] = [.font: label.font as Any]
|
|
180
|
+
switch str {
|
|
181
|
+
case "underline":
|
|
182
|
+
attrs[.underlineStyle] = NSUnderlineStyle.single.rawValue
|
|
183
|
+
case "line-through", "lineThrough":
|
|
184
|
+
attrs[.strikethroughStyle] = NSUnderlineStyle.single.rawValue
|
|
185
|
+
case "underline line-through":
|
|
186
|
+
attrs[.underlineStyle] = NSUnderlineStyle.single.rawValue
|
|
187
|
+
attrs[.strikethroughStyle] = NSUnderlineStyle.single.rawValue
|
|
188
|
+
default:
|
|
189
|
+
break
|
|
190
|
+
}
|
|
191
|
+
label.attributedText = NSAttributedString(string: text, attributes: attrs)
|
|
192
|
+
label.flex.markDirty()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
case "textTransform":
|
|
196
|
+
if let str = value as? String {
|
|
197
|
+
let original = label.text ?? ""
|
|
198
|
+
switch str {
|
|
199
|
+
case "uppercase": label.text = original.uppercased()
|
|
200
|
+
case "lowercase": label.text = original.lowercased()
|
|
201
|
+
case "capitalize": label.text = original.capitalized
|
|
202
|
+
default: break
|
|
203
|
+
}
|
|
204
|
+
label.flex.markDirty()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
default:
|
|
208
|
+
// Delegate unknown props to StyleEngine for layout/visual styling
|
|
209
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
func addEventListener(view: UIView, event: String, handler: @escaping (Any?) -> Void) {
|
|
214
|
+
// Tap/press support via gesture recognizer.
|
|
215
|
+
if event == "press" {
|
|
216
|
+
let wrapper = GestureWrapper(handler: handler)
|
|
217
|
+
let tap = UITapGestureRecognizer(
|
|
218
|
+
target: wrapper,
|
|
219
|
+
action: #selector(GestureWrapper.handleGesture(_:))
|
|
220
|
+
)
|
|
221
|
+
view.addGestureRecognizer(tap)
|
|
222
|
+
view.isUserInteractionEnabled = true
|
|
223
|
+
GestureStorage.store(wrapper, for: view, event: event)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
func removeEventListener(view: UIView, event: String) {
|
|
228
|
+
if event == "press" {
|
|
229
|
+
GestureStorage.remove(for: view, event: event)
|
|
230
|
+
view.gestureRecognizers?.forEach { recognizer in
|
|
231
|
+
if recognizer is UITapGestureRecognizer {
|
|
232
|
+
view.removeGestureRecognizer(recognizer)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// MARK: - Font rebuilding
|
|
239
|
+
|
|
240
|
+
/// Rebuild the UIFont from stored fontSize, fontWeight, and fontFamily.
|
|
241
|
+
private func rebuildFont(for label: UILabel) {
|
|
242
|
+
let size = storedFontSize(on: label) ?? 17.0
|
|
243
|
+
let weightStr = storedFontWeight(on: label)
|
|
244
|
+
let family = storedFontFamily(on: label)
|
|
245
|
+
|
|
246
|
+
let weight = weightStr.flatMap { VTextFactory.fontWeightMap[$0] } ?? .regular
|
|
247
|
+
|
|
248
|
+
if let family = family, !family.isEmpty {
|
|
249
|
+
// Try to create a font with the specified family
|
|
250
|
+
if let customFont = UIFont(name: family, size: size) {
|
|
251
|
+
label.font = customFont
|
|
252
|
+
} else {
|
|
253
|
+
// Fallback: try as a font descriptor family
|
|
254
|
+
let descriptor = UIFontDescriptor()
|
|
255
|
+
.withFamily(family)
|
|
256
|
+
.addingAttributes([.traits: [UIFontDescriptor.TraitKey.weight: weight]])
|
|
257
|
+
label.font = UIFont(descriptor: descriptor, size: size)
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
label.font = UIFont.systemFont(ofSize: size, weight: weight)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// MARK: - Font state storage via associated objects
|
|
265
|
+
|
|
266
|
+
private func storeFontSize(_ size: CGFloat, on view: UIView) {
|
|
267
|
+
objc_setAssociatedObject(view, &VTextFactory.fontSizeKey, size, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private func storedFontSize(on view: UIView) -> CGFloat? {
|
|
271
|
+
return objc_getAssociatedObject(view, &VTextFactory.fontSizeKey) as? CGFloat
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private func storeFontWeight(_ weight: String?, on view: UIView) {
|
|
275
|
+
objc_setAssociatedObject(view, &VTextFactory.fontWeightKey, weight, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private func storedFontWeight(on view: UIView) -> String? {
|
|
279
|
+
return objc_getAssociatedObject(view, &VTextFactory.fontWeightKey) as? String
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private func storeFontFamily(_ family: String?, on view: UIView) {
|
|
283
|
+
objc_setAssociatedObject(view, &VTextFactory.fontFamilyKey, family, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private func storedFontFamily(on view: UIView) -> String? {
|
|
287
|
+
return objc_getAssociatedObject(view, &VTextFactory.fontFamilyKey) as? String
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
#endif
|