craft-native 0.0.184 → 0.0.186

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 CHANGED
@@ -3303,7 +3303,7 @@ function pinCraftNativeDependency(packagePath, craftVersion) {
3303
3303
  `);
3304
3304
  }
3305
3305
  // package.json
3306
- var version = "0.0.184";
3306
+ var version = "0.0.186";
3307
3307
 
3308
3308
  // bin/cli.ts
3309
3309
  var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
package/dist/index.cjs CHANGED
@@ -310,7 +310,7 @@ function craftBinaryNotFoundMessage(triedPath) {
310
310
  `);
311
311
  }
312
312
  // package.json
313
- var version = "0.0.184";
313
+ var version = "0.0.186";
314
314
 
315
315
  // src/sdk-version.ts
316
316
  var SDK_VERSION = version;
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ function craftBinaryNotFoundMessage(triedPath) {
58
58
  `);
59
59
  }
60
60
  // package.json
61
- var version = "0.0.184";
61
+ var version = "0.0.186";
62
62
 
63
63
  // src/sdk-version.ts
64
64
  var SDK_VERSION = version;
@@ -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)
@@ -6925,10 +6939,15 @@ final class CraftNativeAnimationRecord {
6925
6939
  let fontSize: CGFloat?
6926
6940
  let textColor: UIColor?
6927
6941
  let transitionID: String?
6942
+ /// Where it sat in its parent, to be put back after it faded elsewhere.
6943
+ let index: Int?
6944
+ let arrangedIndex: Int?
6928
6945
 
6929
6946
  init(view: UIView, screen: UIView, transitionID: String?) {
6930
6947
  self.view = view
6931
6948
  superview = view.superview
6949
+ index = view.superview?.subviews.firstIndex(of: view)
6950
+ arrangedIndex = (view.superview as? UIStackView)?.arrangedSubviews.firstIndex(of: view)
6932
6951
  frame = view.frame
6933
6952
  frameOnScreen = view.superview?.convert(view.frame, to: screen) ?? view.frame
6934
6953
  alpha = view.alpha
@@ -6947,6 +6966,10 @@ final class CraftNativeAnimationTransaction {
6947
6966
  var screenBackground: UIColor?
6948
6967
  /// Reduce Motion: the screen as it was, crossfaded into the new one.
6949
6968
  var picture: UIView?
6969
+ /// Commits the change as the run loop finishes the pass it came in.
6970
+ var observer: CFRunLoopObserver?
6971
+ /// The screen as it was, drawn over it until the animation is set up.
6972
+ var cover: UIView?
6950
6973
 
6951
6974
  init(spec: CraftNativeAnimationSpec, reduceMotion: Bool) {
6952
6975
  self.spec = spec
@@ -6956,7 +6979,11 @@ final class CraftNativeAnimationTransaction {
6956
6979
 
6957
6980
  /// An animation under way: finished at once if another change comes first.
6958
6981
  final class CraftNativeRunningAnimation {
6982
+ /// The Core Animation key every animation of a change goes under.
6983
+ static let key = "craft.change"
6959
6984
  var animators: [UIViewPropertyAnimator] = []
6985
+ /// Live layers carrying an animation of the change, under `key` + a suffix.
6986
+ var layers: [(CALayer, String)] = []
6960
6987
  var cleanup: [() -> Void] = []
6961
6988
  private var done = false
6962
6989
 
@@ -6967,6 +6994,10 @@ final class CraftNativeRunningAnimation {
6967
6994
  animator.stopAnimation(false)
6968
6995
  animator.finishAnimation(at: .end)
6969
6996
  }
6997
+ // A live view's own values were never touched: taking the animation
6998
+ // off leaves it exactly as the screen last drew it.
6999
+ for (layer, key) in layers { layer.removeAnimation(forKey: key) }
7000
+ layers.removeAll()
6970
7001
  cleanup.forEach { $0() }
6971
7002
  cleanup.removeAll()
6972
7003
  }
@@ -6991,16 +7022,40 @@ extension CraftNativeScreenController {
6991
7022
  let transaction = CraftNativeAnimationTransaction(spec: CraftNativeAnimationSpec(payload), reduceMotion: UIAccessibility.isReduceMotionEnabled)
6992
7023
  transaction.screenBackground = view.backgroundColor
6993
7024
  if transaction.reduceMotion {
6994
- transaction.picture = view.window == nil ? nil : view.snapshotView(afterScreenUpdates: false)
7025
+ // Held over the screen until the change is in, then faded away.
7026
+ if view.window != nil, let picture = view.snapshotView(afterScreenUpdates: false) {
7027
+ picture.frame = view.bounds
7028
+ picture.isUserInteractionEnabled = false
7029
+ view.addSubview(picture)
7030
+ transaction.picture = picture
7031
+ }
6995
7032
  } else {
6996
7033
  view.layoutIfNeeded()
6997
7034
  recordAnimationState(renderedRoot, into: transaction)
7035
+ // The screen as it is, held over it until the change's animation
7036
+ // is set up: UIKit can draw the new screen before the run loop's
7037
+ // pass ends, which showed it for a frame before it animated.
7038
+ if view.window != nil, let cover = view.snapshotView(afterScreenUpdates: false) {
7039
+ cover.frame = view.bounds
7040
+ cover.isUserInteractionEnabled = false
7041
+ view.addSubview(cover)
7042
+ transaction.cover = cover
7043
+ }
6998
7044
  }
6999
7045
  animationTransaction = transaction
7000
7046
  CraftNativeAnimationClock.duration = transaction.spec.duration
7001
7047
  // The change comes from the same turn of the script (the state it
7002
- // changed renders in a microtask); a slow one still animates.
7003
- DispatchQueue.main.async { [weak self] in self?.commitAnimatedChange() }
7048
+ // changed renders in a microtask), so it is set up as the run loop
7049
+ // finishes this pass, before Core Animation draws it: drawn first, the
7050
+ // new screen showed for a frame and then jumped back to animate. A
7051
+ // change that comes later still animates.
7052
+ let observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.beforeWaiting.rawValue, false, 0) { [weak self] _, _ in
7053
+ self?.commitAnimatedChange()
7054
+ }
7055
+ if let observer {
7056
+ CFRunLoopAddObserver(CFRunLoopGetMain(), observer, .commonModes)
7057
+ transaction.observer = observer
7058
+ }
7004
7059
  DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { [weak self] in self?.commitAnimatedChange() }
7005
7060
  }
7006
7061
 
@@ -7020,6 +7075,13 @@ extension CraftNativeScreenController {
7020
7075
  private func commitAnimatedChange() {
7021
7076
  guard let transaction = animationTransaction else { return }
7022
7077
  animationTransaction = nil
7078
+ if let observer = transaction.observer {
7079
+ CFRunLoopRemoveObserver(CFRunLoopGetMain(), observer, .commonModes)
7080
+ transaction.observer = nil
7081
+ }
7082
+ // Taken off in the same transaction the animations start in.
7083
+ transaction.cover?.removeFromSuperview()
7084
+ transaction.cover = nil
7023
7085
  let spec = transaction.spec
7024
7086
  let running = CraftNativeRunningAnimation()
7025
7087
  runningAnimation = running
@@ -7027,9 +7089,6 @@ extension CraftNativeScreenController {
7027
7089
 
7028
7090
  if transaction.reduceMotion {
7029
7091
  guard let picture = transaction.picture else { running.finish(); return }
7030
- picture.frame = view.bounds
7031
- picture.isUserInteractionEnabled = false
7032
- view.addSubview(picture)
7033
7092
  let fade = UIViewPropertyAnimator(duration: 0.3, curve: .easeInOut) { picture.alpha = 0 }
7034
7093
  fade.addCompletion { _ in running.finish() }
7035
7094
  running.animators.append(fade)
@@ -7073,26 +7132,69 @@ extension CraftNativeScreenController {
7073
7132
  view.addSubview(overlay)
7074
7133
  running.cleanup.append { overlay.removeFromSuperview() }
7075
7134
 
7076
- let main = UIViewPropertyAnimator(duration: spec.duration, timingParameters: spec.timing)
7077
- // Fades take the first part, so nothing is half there for long.
7135
+ // Views that are going are the change's own now: they move into the
7136
+ // overlay where they were and fade there. Put back as they were
7137
+ // afterwards, in case the screen shows them again.
7078
7138
  let fadeOut = UIViewPropertyAnimator(duration: spec.duration * 0.5, curve: .easeOut)
7079
- let fadeIn = UIViewPropertyAnimator(duration: spec.duration * 0.6, curve: .easeInOut)
7080
- running.animators = [main, fadeOut, fadeIn]
7081
-
7082
- // Removed views fade where they were, over what replaced them.
7083
- for record in removedRoots {
7139
+ let quickFade = UIViewPropertyAnimator(duration: spec.duration * 0.35, curve: .easeOut)
7140
+ let travel = UIViewPropertyAnimator(duration: spec.duration, timingParameters: spec.timing)
7141
+ running.animators = [fadeOut, quickFade, travel]
7142
+ func adopt(_ record: CraftNativeAnimationRecord, fading fade: UIViewPropertyAnimator) {
7084
7143
  let going = record.view
7144
+ let resizes = going.translatesAutoresizingMaskIntoConstraints
7145
+ let base = going.transform
7085
7146
  going.translatesAutoresizingMaskIntoConstraints = true
7086
7147
  going.transform = .identity
7087
7148
  going.frame = record.frameOnScreen
7149
+ going.alpha = record.alpha
7088
7150
  overlay.addSubview(going)
7089
- fadeOut.addAnimations { going.alpha = 0 }
7151
+ fade.addAnimations { going.alpha = 0 }
7152
+ running.cleanup.append {
7153
+ going.removeFromSuperview()
7154
+ going.transform = base
7155
+ going.alpha = record.alpha
7156
+ going.translatesAutoresizingMaskIntoConstraints = resizes
7157
+ going.frame = record.frame
7158
+ // Back in a parent that went with it, as it was: a screen
7159
+ // that shows that subtree again finds it whole.
7160
+ if let parent = record.superview, parent.window == nil {
7161
+ if let stack = parent as? UIStackView, let at = record.arrangedIndex {
7162
+ stack.insertArrangedSubview(going, at: min(at, stack.arrangedSubviews.count))
7163
+ } else if let at = record.index {
7164
+ parent.insertSubview(going, at: min(at, parent.subviews.count))
7165
+ }
7166
+ }
7167
+ }
7168
+ }
7169
+ for record in removedRoots { adopt(record, fading: fadeOut) }
7170
+
7171
+ // Live views are animated on their layers only, from how they were to
7172
+ // how the screen draws them now: nothing the screen sets on them
7173
+ // meanwhile (the next second on a timer, a style patch) is undone.
7174
+ let fades = spec.duration * 0.6
7175
+ func animate(_ layer: CALayer, _ keyPath: String, from: Any, duration: TimeInterval? = nil, spring: Bool = true) {
7176
+ let animation: CABasicAnimation
7177
+ if spring, duration == nil, spec.curve == "spring" {
7178
+ let bouncy = CASpringAnimation(keyPath: keyPath)
7179
+ bouncy.mass = 1
7180
+ bouncy.stiffness = pow(2 * .pi / spec.duration, 2)
7181
+ bouncy.damping = 2 * sqrt(bouncy.stiffness) * (1 - spec.bounce)
7182
+ bouncy.duration = bouncy.settlingDuration
7183
+ animation = bouncy
7184
+ } else {
7185
+ animation = CABasicAnimation(keyPath: keyPath)
7186
+ animation.duration = duration ?? spec.duration
7187
+ animation.timingFunction = CAMediaTimingFunction(name: spec.curve == "linear" ? .linear : .easeInEaseOut)
7188
+ }
7189
+ animation.fromValue = from
7190
+ let key = "\(CraftNativeRunningAnimation.key).\(keyPath)"
7191
+ layer.add(animation, forKey: key)
7192
+ running.layers.append((layer, key))
7090
7193
  }
7091
7194
 
7092
- // The background, the screen's own included, goes from the old colour.
7195
+ // The background, the screen's own included, comes from the old colour.
7093
7196
  if let before = transaction.screenBackground, let now = view.backgroundColor, !before.isEqual(now) {
7094
- view.backgroundColor = before
7095
- main.addAnimations { self.view.backgroundColor = now }
7197
+ animate(view.layer, "backgroundColor", from: before.cgColor, spring: false)
7096
7198
  }
7097
7199
 
7098
7200
  // Shared elements: the new view starts where the old one was, at its
@@ -7100,58 +7202,56 @@ extension CraftNativeScreenController {
7100
7202
  for (old, new) in pairs {
7101
7203
  let target = new.superview?.convert(new.frame, to: view) ?? new.frame
7102
7204
  let move = morph(from: old.frameOnScreen, to: target, uniform: true)
7103
- let finalAlpha = new.alpha
7104
- let base = new.transform
7105
- new.transform = base.concatenating(move)
7106
- new.alpha = 0
7107
- main.addAnimations { new.transform = base }
7108
- fadeIn.addAnimations { new.alpha = finalAlpha }
7205
+ animate(new.layer, "transform", from: CATransform3DMakeAffineTransform(new.transform.concatenating(move)))
7206
+ // Crossed quickly, so the two are never both half there for long.
7207
+ animate(new.layer, "opacity", from: 0, duration: spec.duration * 0.35)
7208
+ adopt(old, fading: quickFade)
7109
7209
  let going = old.view
7110
- going.translatesAutoresizingMaskIntoConstraints = true
7111
- going.transform = .identity
7112
- going.frame = old.frameOnScreen
7113
- going.alpha = old.alpha
7114
- overlay.addSubview(going)
7115
- main.addAnimations { going.transform = move.inverted() }
7116
- fadeOut.addAnimations { going.alpha = 0 }
7117
- running.cleanup.append { new.transform = base; new.alpha = finalAlpha }
7210
+ travel.addAnimations { going.transform = move.inverted() }
7118
7211
  }
7119
7212
 
7120
7213
  // New views fade in. A new container holding a shared element keeps it
7121
7214
  // in sight: only the rest of it fades, and its colour comes up.
7215
+ func fadeIn(_ added: UIView) {
7216
+ if pairedNew.contains(ObjectIdentifier(added)) { return }
7217
+ guard containsShared(added, pairedNew: pairedNew) else {
7218
+ animate(added.layer, "opacity", from: 0, duration: fades)
7219
+ return
7220
+ }
7221
+ if let colour = added.backgroundColor, colour != .clear {
7222
+ animate(added.layer, "backgroundColor", from: colour.withAlphaComponent(0).cgColor, duration: fades)
7223
+ }
7224
+ added.subviews.forEach(fadeIn)
7225
+ }
7122
7226
  for added in inserted where !(added.superview.map { insertedIds.contains(ObjectIdentifier($0)) } ?? false) {
7123
- fadeInInserted(added, pairedNew: pairedNew, animator: fadeIn, running: running)
7227
+ fadeIn(added)
7124
7228
  }
7125
7229
 
7126
7230
  // Views that stay go to their new places and sizes.
7127
7231
  for record in records.values where present.contains(ObjectIdentifier(record.view)) {
7128
7232
  let stays = record.view
7129
7233
  if let label = stays as? UILabel, let before = record.fontSize, abs(before - label.font.pointSize) > 0.5 {
7130
- let base = label.transform
7131
7234
  let target = label.superview?.convert(label.frame, to: view) ?? label.frame
7132
- label.transform = base.concatenating(morph(from: record.frameOnScreen, to: target, uniform: true))
7133
- main.addAnimations { label.transform = base }
7134
- running.cleanup.append { label.transform = base }
7135
- } else if record.superview === stays.superview, record.frame != stays.frame {
7136
- let final = stays.frame
7137
- stays.frame = record.frame
7138
- main.addAnimations { stays.frame = final }
7235
+ let move = morph(from: record.frameOnScreen, to: target, uniform: true)
7236
+ animate(label.layer, "transform", from: CATransform3DMakeAffineTransform(label.transform.concatenating(move)))
7237
+ } else if record.superview === stays.superview, record.frame != stays.frame, stays.transform.isIdentity {
7238
+ animate(stays.layer, "position", from: CGPoint(x: record.frame.midX, y: record.frame.midY))
7239
+ if record.frame.size != stays.frame.size {
7240
+ animate(stays.layer, "bounds.size", from: record.frame.size)
7241
+ }
7139
7242
  }
7140
7243
  if let before = record.backgroundColor, let now = stays.backgroundColor, !before.isEqual(now) {
7141
- stays.backgroundColor = before
7142
- main.addAnimations { stays.backgroundColor = now }
7143
- }
7144
- if let label = stays as? UILabel, let before = record.textColor, !before.isEqual(label.textColor) {
7145
- let now = label.textColor
7146
- label.textColor = before
7147
- UIView.transition(with: label, duration: spec.duration * 0.6, options: [.transitionCrossDissolve, .allowAnimatedContent]) { label.textColor = now }
7244
+ animate(stays.layer, "backgroundColor", from: before.cgColor, spring: false)
7148
7245
  }
7149
7246
  }
7150
7247
 
7151
- main.addCompletion { _ in running.finish() }
7248
+ travel.addCompletion { _ in running.finish() }
7152
7249
  fadeOut.startAnimation()
7153
- fadeIn.startAnimation()
7154
- main.startAnimation()
7250
+ quickFade.startAnimation()
7251
+ travel.startAnimation()
7252
+ // The springs settle a little after `duration`; the views are already
7253
+ // where the screen draws them, so finishing then changes nothing.
7254
+ DispatchQueue.main.asyncAfter(deadline: .now() + spec.duration * 2) { running.finish() }
7155
7255
  }
7156
7256
 
7157
7257
  /// The transform that puts a view at `final` where `start` was: moved
@@ -7166,23 +7266,6 @@ extension CraftNativeScreenController {
7166
7266
  return CGAffineTransform(translationX: dx, y: dy).scaledBy(x: sx, y: sy)
7167
7267
  }
7168
7268
 
7169
- private func fadeInInserted(_ view: UIView, pairedNew: Set<ObjectIdentifier>, animator: UIViewPropertyAnimator, running: CraftNativeRunningAnimation) {
7170
- if pairedNew.contains(ObjectIdentifier(view)) { return }
7171
- guard containsShared(view, pairedNew: pairedNew) else {
7172
- let finalAlpha = view.alpha
7173
- view.alpha = 0
7174
- animator.addAnimations { view.alpha = finalAlpha }
7175
- running.cleanup.append { view.alpha = finalAlpha }
7176
- return
7177
- }
7178
- if let colour = view.backgroundColor, colour != .clear {
7179
- view.backgroundColor = colour.withAlphaComponent(0)
7180
- animator.addAnimations { view.backgroundColor = colour }
7181
- running.cleanup.append { view.backgroundColor = colour }
7182
- }
7183
- for child in view.subviews { fadeInInserted(child, pairedNew: pairedNew, animator: animator, running: running) }
7184
- }
7185
-
7186
7269
  private func containsShared(_ view: UIView, pairedNew: Set<ObjectIdentifier>) -> Bool {
7187
7270
  if pairedNew.contains(ObjectIdentifier(view)) { return true }
7188
7271
  return view.subviews.contains { containsShared($0, pairedNew: pairedNew) }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "craft-native",
3
- "version": "0.0.184",
3
+ "version": "0.0.186",
4
4
  "type": "module",
5
5
  "description": "Build desktop apps with web languages - TypeScript SDK for Craft",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",