@craft-native/ios 0.0.243 → 0.0.244
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/package.json
CHANGED
package/templates/CraftApp.swift
CHANGED
|
@@ -670,6 +670,7 @@ final class CraftChrome: ObservableObject {
|
|
|
670
670
|
|
|
671
671
|
@Published var tabs: [Tab] = []
|
|
672
672
|
@Published var tabAccessory: Tab?
|
|
673
|
+
private weak var tabAccessoryOwner: AnyObject?
|
|
673
674
|
@Published var selected: String?
|
|
674
675
|
let tabSwipeProgress = CraftTabSwipeProgress()
|
|
675
676
|
private(set) var tabSwipePosition: CGFloat? {
|
|
@@ -771,7 +772,7 @@ final class CraftChrome: ObservableObject {
|
|
|
771
772
|
tabs[index] = next
|
|
772
773
|
}
|
|
773
774
|
/// A destination beside the tab pill, outside the tab stacks and swipe order.
|
|
774
|
-
func updateTabAccessory(_ item: [String: Any]?) {
|
|
775
|
+
func updateTabAccessory(_ item: [String: Any]?, owner: AnyObject? = nil) {
|
|
775
776
|
let next: Tab?
|
|
776
777
|
if let item, let path = item["path"] as? String,
|
|
777
778
|
path.hasPrefix("/"), !path.hasPrefix("//"),
|
|
@@ -782,10 +783,17 @@ final class CraftChrome: ObservableObject {
|
|
|
782
783
|
} else {
|
|
783
784
|
next = nil
|
|
784
785
|
}
|
|
786
|
+
tabAccessoryOwner = owner
|
|
785
787
|
guard tabAccessory != next else { return }
|
|
786
788
|
tabAccessory = next
|
|
787
789
|
}
|
|
788
790
|
|
|
791
|
+
/// A retained or prepared screen cannot clear another screen's chrome.
|
|
792
|
+
func clearTabAccessory(ownedBy owner: AnyObject) {
|
|
793
|
+
guard tabAccessoryOwner === owner else { return }
|
|
794
|
+
updateTabAccessory(nil)
|
|
795
|
+
}
|
|
796
|
+
|
|
789
797
|
func setHiddenByScreen(_ hidden: Bool) {
|
|
790
798
|
guard hiddenByScreen != hidden else { return }
|
|
791
799
|
hiddenByScreen = hidden
|
|
@@ -29,6 +29,7 @@ struct CraftNativeMutationResult {
|
|
|
29
29
|
final class CraftNativeMutationDocument {
|
|
30
30
|
private struct Node {
|
|
31
31
|
let id: String
|
|
32
|
+
let key: String?
|
|
32
33
|
var type: String
|
|
33
34
|
var props: [String: Any]
|
|
34
35
|
var style: [String: Any]
|
|
@@ -177,6 +178,7 @@ final class CraftNativeMutationDocument {
|
|
|
177
178
|
}
|
|
178
179
|
nodes[id] = Node(
|
|
179
180
|
id: id,
|
|
181
|
+
key: value["key"] as? String,
|
|
180
182
|
type: type,
|
|
181
183
|
props: dictionary(value["props"]),
|
|
182
184
|
style: dictionary(value["style"]),
|
|
@@ -330,6 +332,7 @@ final class CraftNativeMutationDocument {
|
|
|
330
332
|
"style": node.style,
|
|
331
333
|
"events": node.events,
|
|
332
334
|
]
|
|
335
|
+
if let key = node.key { value["key"] = key }
|
|
333
336
|
let text: [Any] = node.text
|
|
334
337
|
let children: [Any] = node.children.compactMap { materialize($0, nodes: nodes) }
|
|
335
338
|
value["children"] = text + children
|
|
@@ -350,6 +353,7 @@ final class CraftNativeMutationDocument {
|
|
|
350
353
|
let children = value["children"] as? [Any] ?? []
|
|
351
354
|
nodes[id] = Node(
|
|
352
355
|
id: id,
|
|
356
|
+
key: value["key"] as? String,
|
|
353
357
|
type: value["type"] as? String ?? "View",
|
|
354
358
|
props: props,
|
|
355
359
|
style: dictionary(value["style"]),
|
|
@@ -1435,21 +1435,47 @@ private final class CraftNativeMapView: MKMapView, MKMapViewDelegate {
|
|
|
1435
1435
|
|
|
1436
1436
|
private func configureMarkerView(_ view: MKAnnotationView, id: String) {
|
|
1437
1437
|
if let bearing = markerBearings[id] {
|
|
1438
|
-
let
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
// the contrasting route arrow when displayed over map content.
|
|
1442
|
-
view.image = UIImage(systemName: "arrow.up.circle.fill", withConfiguration: configuration)?
|
|
1443
|
-
.withRenderingMode(.alwaysOriginal)
|
|
1438
|
+
let color = (markerPinColors[id] ?? .systemGreen).resolvedColor(with: traitCollection)
|
|
1439
|
+
view.tintColor = color
|
|
1440
|
+
view.image = Self.markerImage(color: color, direction: true)
|
|
1444
1441
|
view.transform = CGAffineTransform(rotationAngle: CGFloat((bearing - camera.heading) * .pi / 180))
|
|
1445
1442
|
} else {
|
|
1446
|
-
|
|
1447
|
-
view.
|
|
1448
|
-
|
|
1443
|
+
let color = (markerPinColors[id] ?? .systemRed).resolvedColor(with: traitCollection)
|
|
1444
|
+
view.tintColor = color
|
|
1445
|
+
view.image = Self.markerImage(color: color, direction: false)
|
|
1449
1446
|
view.transform = .identity
|
|
1450
1447
|
}
|
|
1451
1448
|
}
|
|
1452
1449
|
|
|
1450
|
+
private static let markerImages: NSCache<NSString, UIImage> = {
|
|
1451
|
+
let cache = NSCache<NSString, UIImage>()
|
|
1452
|
+
cache.countLimit = 64
|
|
1453
|
+
return cache
|
|
1454
|
+
}()
|
|
1455
|
+
|
|
1456
|
+
private static func markerImage(color: UIColor, direction: Bool) -> UIImage {
|
|
1457
|
+
let key = "\(direction ? "direction" : "pin"):\(color.description)" as NSString
|
|
1458
|
+
if let cached = markerImages.object(forKey: key) { return cached }
|
|
1459
|
+
let size = direction ? CGSize(width: 20, height: 24) : CGSize(width: 28, height: 28)
|
|
1460
|
+
// Explicit native drawing preserves the light backing independently
|
|
1461
|
+
// of MapKit's tint and SF Symbols' multicolor rendering preferences.
|
|
1462
|
+
let image = UIGraphicsImageRenderer(size: size).image { _ in
|
|
1463
|
+
let border = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size).insetBy(dx: 1, dy: 1),
|
|
1464
|
+
cornerRadius: (size.width - 2) / 2)
|
|
1465
|
+
UIColor.white.setFill()
|
|
1466
|
+
border.fill()
|
|
1467
|
+
color.setStroke()
|
|
1468
|
+
border.lineWidth = 1.5
|
|
1469
|
+
border.stroke()
|
|
1470
|
+
let symbol = UIImage(systemName: direction ? "arrow.up" : "mappin",
|
|
1471
|
+
withConfiguration: UIImage.SymbolConfiguration(pointSize: 16, weight: .semibold))?
|
|
1472
|
+
.withTintColor(color, renderingMode: .alwaysOriginal)
|
|
1473
|
+
symbol?.draw(in: direction ? CGRect(x: 5, y: 6, width: 10, height: 12) : CGRect(x: 7, y: 5, width: 14, height: 18))
|
|
1474
|
+
}.withRenderingMode(.alwaysOriginal)
|
|
1475
|
+
markerImages.setObject(image, forKey: key)
|
|
1476
|
+
return image
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1453
1479
|
override func layoutSubviews() {
|
|
1454
1480
|
super.layoutSubviews()
|
|
1455
1481
|
if tileTemplate != nil {
|
|
@@ -4621,10 +4647,9 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
|
|
|
4621
4647
|
super.viewWillDisappear(animated)
|
|
4622
4648
|
screenVisibilityChanged(false)
|
|
4623
4649
|
stopCelebrations(in: view)
|
|
4624
|
-
//
|
|
4625
|
-
//
|
|
4626
|
-
|
|
4627
|
-
CraftChrome.shared.updateTabAccessory(nil)
|
|
4650
|
+
// Release only this screen's accessory. A retained or prepared screen
|
|
4651
|
+
// can disappear after the destination has already published its own.
|
|
4652
|
+
CraftChrome.shared.clearTabAccessory(ownedBy: self)
|
|
4628
4653
|
// Popped (or its stack dismissed) while the bar was away: it comes
|
|
4629
4654
|
// back for whatever shows next, a web page included.
|
|
4630
4655
|
if tabBarHidden, isMovingFromParent || isBeingDismissed || navigationController?.isBeingDismissed == true {
|
|
@@ -4669,7 +4694,7 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
|
|
|
4669
4694
|
private func restoreShellChrome() {
|
|
4670
4695
|
guard !isAppScript else { return }
|
|
4671
4696
|
CraftChrome.shared.setHiddenByScreen(tabBarHidden)
|
|
4672
|
-
CraftChrome.shared.updateTabAccessory(tabAccessory)
|
|
4697
|
+
CraftChrome.shared.updateTabAccessory(tabAccessory, owner: self)
|
|
4673
4698
|
}
|
|
4674
4699
|
|
|
4675
4700
|
/// The screen came on screen or went off it: a screen pushed over it was
|
|
@@ -5340,7 +5365,7 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
|
|
|
5340
5365
|
if options.keys.contains("tabAccessory") {
|
|
5341
5366
|
tabAccessory = options["tabAccessory"] as? [String: Any]
|
|
5342
5367
|
if navigationController?.topViewController === self, view.window != nil {
|
|
5343
|
-
CraftChrome.shared.updateTabAccessory(tabAccessory)
|
|
5368
|
+
CraftChrome.shared.updateTabAccessory(tabAccessory, owner: self)
|
|
5344
5369
|
}
|
|
5345
5370
|
}
|
|
5346
5371
|
if options.keys.contains("title") {
|
|
@@ -6865,7 +6890,11 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
|
|
|
6865
6890
|
|
|
6866
6891
|
private func explicitKey(_ node: [String: Any], props: [String: Any]) -> String? {
|
|
6867
6892
|
if let authored = authoredKey(node, props: props) { return authored }
|
|
6868
|
-
|
|
6893
|
+
// Repeated test selectors are valid for list rows. Retain each row by
|
|
6894
|
+
// its unique protocol ID before considering legacy selector identity.
|
|
6895
|
+
return [node["id"], props["testID"]]
|
|
6896
|
+
.compactMap { $0 as? String }
|
|
6897
|
+
.first { !$0.isEmpty }
|
|
6869
6898
|
}
|
|
6870
6899
|
|
|
6871
6900
|
/// The key a node's author gave it. A wrapper of one child (a
|
|
@@ -6877,7 +6906,7 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
|
|
|
6877
6906
|
private func authoredKey(_ node: [String: Any], props: [String: Any]) -> String? {
|
|
6878
6907
|
// The current @stacksjs/stx native compiler emits key in props; accept the IR
|
|
6879
6908
|
// field too so keyed children keep working when the compiler adopts it.
|
|
6880
|
-
let direct = [node["key"], props["key"]
|
|
6909
|
+
let direct = [node["key"], props["key"]]
|
|
6881
6910
|
.compactMap { $0 as? String }
|
|
6882
6911
|
.first { !$0.isEmpty }
|
|
6883
6912
|
if let direct { return direct }
|
|
@@ -448,7 +448,11 @@ final class DriverTests: XCTestCase {
|
|
|
448
448
|
var found = element(containing: arg)
|
|
449
449
|
var swipes = 0
|
|
450
450
|
while (found == nil || !(found!.isHittable)) && swipes < 8 {
|
|
451
|
-
app.
|
|
451
|
+
if let target = found, !target.frame.isEmpty, target.frame.midY < app.frame.midY {
|
|
452
|
+
app.swipeDown()
|
|
453
|
+
} else {
|
|
454
|
+
app.swipeUp()
|
|
455
|
+
}
|
|
452
456
|
swipes += 1
|
|
453
457
|
found = element(containing: arg)
|
|
454
458
|
}
|