@craft-native/ios 0.0.185 → 0.0.187

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/index.js CHANGED
@@ -156,7 +156,12 @@ function serializeSimulatorSteps(steps) {
156
156
  `;
157
157
  }
158
158
  function createNativeMutationValidator() {
159
- const output = mkdtempSync(join2(tmpdir(), "craft-native-mutation-"));
159
+ const output = mkdtempSync(join2(tmpdir(), `craft-native-mutation-p${process.pid}-`));
160
+ const removeOutput = () => {
161
+ process.off("exit", removeOutput);
162
+ rmSync(output, { recursive: true, force: true });
163
+ };
164
+ process.on("exit", removeOutput);
160
165
  const binary = join2(output, "validate");
161
166
  try {
162
167
  writeFileSync(join2(output, "main.swift"), readFileSync(join2(templates, "MutationValidator.swift")));
@@ -164,7 +169,7 @@ function createNativeMutationValidator() {
164
169
  if (build.exitCode !== 0)
165
170
  throw new Error(build.stderr.toString());
166
171
  } catch (error) {
167
- rmSync(output, { recursive: true, force: true });
172
+ removeOutput();
168
173
  throw error;
169
174
  }
170
175
  let disposed = false;
@@ -185,7 +190,7 @@ function createNativeMutationValidator() {
185
190
  },
186
191
  dispose() {
187
192
  disposed = true;
188
- rmSync(output, { recursive: true, force: true });
193
+ removeOutput();
189
194
  }
190
195
  };
191
196
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craft-native/ios",
3
- "version": "0.0.185",
3
+ "version": "0.0.187",
4
4
  "homepage": "https://github.com/craft-native/craft/tree/main/packages/ios#readme",
5
5
  "repository": {
6
6
  "type": "git",
@@ -577,6 +577,9 @@ private final class CraftNativeVideoView: UIView, AVPlayerViewControllerDelegate
577
577
  // A muted video makes no sound, so it leaves the session as it is.
578
578
  if muted { releaseSession() } else { claimSession() }
579
579
  playerController.showsPlaybackControls = controls
580
+ // The system's controls are for VoiceOver too; a bare video stays one element.
581
+ playerController.view.accessibilityElementsHidden = !controls
582
+ isUserInteractionEnabled = true
580
583
  playerController.videoGravity = resizeMode == "cover" ? .resizeAspectFill
581
584
  : resizeMode == "stretch" ? .resize
582
585
  : .resizeAspect
@@ -5687,6 +5690,13 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
5687
5690
  return handler
5688
5691
  }
5689
5692
 
5693
+ /// Views that take their own touches whatever handlers the screen gives
5694
+ /// them: a scroll view, a modal, and a video's system controls (with
5695
+ /// interaction off, its play, scrubber and full screen never answered).
5696
+ private func ownsTouches(_ view: UIView) -> Bool {
5697
+ view is UIScrollView || view is CraftNativeModalView || view is CraftNativeVideoView
5698
+ }
5699
+
5690
5700
  private func updatePressHandler(_ handler: String?, for view: UIView) {
5691
5701
  let id = ObjectIdentifier(view)
5692
5702
  let handler = handler?.isEmpty == false ? handler : nil
@@ -5701,7 +5711,7 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
5701
5711
  }
5702
5712
  // A container keeps receiving touches without a handler of its own,
5703
5713
  // or every control inside it (and the screen root) would be dead.
5704
- if !(view is UIScrollView || view is CraftNativeModalView) { view.isUserInteractionEnabled = actionable || longPressHandlers[id] != nil || view is CraftNativeFlowView }
5714
+ if !ownsTouches(view) { view.isUserInteractionEnabled = actionable || longPressHandlers[id] != nil || view is CraftNativeFlowView }
5705
5715
  }
5706
5716
 
5707
5717
  private func updatePressFeedback(_ pressIn: String?, _ pressOut: String?, for view: UIView) {
@@ -5710,7 +5720,7 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
5710
5720
  if let pressOut, !pressOut.isEmpty { pressOutHandlers[id] = pressOut } else { pressOutHandlers.removeValue(forKey: id) }
5711
5721
  guard pressInHandlers[id] != nil || pressOutHandlers[id] != nil else {
5712
5722
  if let recognizer = pressFeedbackRecognizers.removeValue(forKey: id) { view.removeGestureRecognizer(recognizer) }
5713
- if !(view is UIScrollView || view is CraftNativeModalView) { view.isUserInteractionEnabled = handlers[id] != nil || linkTargets[id] != nil || longPressHandlers[id] != nil || view is CraftNativeFlowView }
5723
+ if !ownsTouches(view) { view.isUserInteractionEnabled = handlers[id] != nil || linkTargets[id] != nil || longPressHandlers[id] != nil || view is CraftNativeFlowView }
5714
5724
  return
5715
5725
  }
5716
5726
  if pressFeedbackRecognizers[id] == nil {
@@ -5722,14 +5732,14 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
5722
5732
  pressFeedbackRecognizers[id] = recognizer
5723
5733
  view.addGestureRecognizer(recognizer)
5724
5734
  }
5725
- if !(view is UIScrollView || view is CraftNativeModalView) { view.isUserInteractionEnabled = true }
5735
+ if !ownsTouches(view) { view.isUserInteractionEnabled = true }
5726
5736
  }
5727
5737
 
5728
5738
  private func updateLongPressHandler(_ handler: String?, for view: UIView) {
5729
5739
  let id = ObjectIdentifier(view)
5730
5740
  if let handler, !handler.isEmpty {
5731
5741
  longPressHandlers[id] = handler
5732
- if !(view is UIScrollView || view is CraftNativeModalView) { view.isUserInteractionEnabled = true }
5742
+ if !ownsTouches(view) { view.isUserInteractionEnabled = true }
5733
5743
  if longPressRecognizers[id] == nil {
5734
5744
  let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(viewLongPressed(_:)))
5735
5745
  longPressRecognizers[id] = recognizer
@@ -5740,7 +5750,7 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
5740
5750
  if let recognizer = longPressRecognizers.removeValue(forKey: id) {
5741
5751
  view.removeGestureRecognizer(recognizer)
5742
5752
  }
5743
- if !(view is UIScrollView || view is CraftNativeModalView) { view.isUserInteractionEnabled = handlers[id] != nil || linkTargets[id] != nil || view is CraftNativeFlowView }
5753
+ if !ownsTouches(view) { view.isUserInteractionEnabled = handlers[id] != nil || linkTargets[id] != nil || view is CraftNativeFlowView }
5744
5754
  }
5745
5755
  }
5746
5756
 
@@ -6336,10 +6346,14 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
6336
6346
  // reads: its label names the group, as a tab strip or a pager's
6337
6347
  // name, and never stands in for its content. As an element of its
6338
6348
  // own, a labelled pager hid every page from VoiceOver.
6349
+ // A video with its system controls is a group too: VoiceOver reaches
6350
+ // play, the scrubber, skipping and full screen inside it.
6351
+ let controlledVideo = type == "Video" && (props["controls"] as? Bool) == true
6339
6352
  let container = ["ScrollView", "FlatList", "SectionList"].contains(type)
6340
6353
  || ["tablist", "radiogroup", "list", "menu", "menubar", "toolbar"].contains(role ?? "")
6354
+ || controlledVideo
6341
6355
  view.isAccessibilityElement = !container && role != "none" && (view.accessibilityLabel != nil || role != nil || ["Text", "Button", "Link", "Checkbox", "Image", "ImageBackground", "Video", "TextInput", "Picker", "DatePicker", "Switch", "Slider"].contains(type))
6342
- view.accessibilityContainerType = container && view.accessibilityLabel != nil ? .semanticGroup : .none
6356
+ view.accessibilityContainerType = container && (view.accessibilityLabel != nil || controlledVideo) ? .semanticGroup : .none
6343
6357
  var traits: UIAccessibilityTraits = []
6344
6358
  switch role ?? type.lowercased() {
6345
6359
  case "tablist": traits.insert(.tabBar)