@momo-kits/native-kits 0.167.1-duo.12 → 0.167.1-duo.13

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.
@@ -219,16 +219,20 @@ public func isIPhoneDuoDevice(windowSize: CGSize? = nil) -> Bool {
219
219
  /// System occlusion regions (iOS 27.1+) in window coordinates: the iPhone Duo camera, and the
220
220
  /// status bar cluster as it grows for a call or a Live Activity. Empty before iOS 27.1.
221
221
  enum ReservedRegions {
222
- // last report of the probe, so `ScreenLayout.current` includes it
223
- static var current: [CGRect] = []
222
+ // last report of a probe, so `ScreenLayout.current` includes it
223
+ private(set) static var current: [CGRect] = []
224
+ static let didChange = Notification.Name("MoMoUIKitsReservedRegionsDidChange")
225
+
226
+ fileprivate static func publish(_ regions: [CGRect]) {
227
+ guard regions != current else { return }
228
+ current = regions
229
+ NotificationCenter.default.post(name: didChange, object: nil)
230
+ }
224
231
  }
225
232
 
226
233
  /// Invisible probe: reading the regions in layoutSubviews makes UIKit run it again when they
227
234
  /// change, so nothing polls. Port of RN `@momo-kits/reserved-regions`.
228
235
  private final class ReservedRegionsProbe: UIView {
229
- var onChange: (() -> Void)?
230
- private var last: [CGRect]?
231
-
232
236
  override init(frame: CGRect) {
233
237
  super.init(frame: frame)
234
238
  isUserInteractionEnabled = false
@@ -238,12 +242,19 @@ private final class ReservedRegionsProbe: UIView {
238
242
 
239
243
  override func layoutSubviews() {
240
244
  super.layoutSubviews()
245
+ // read even when not publishing, so UIKit keeps re-running this when they change
241
246
  let regions = occlusionRegions()
242
- guard regions != last else { return }
243
- last = regions
244
- ReservedRegions.current = regions
247
+ // every screen has a probe but the regions are window-wide: one sliding in on a push or
248
+ // a present, or off the window, would overwrite them with a partial read
249
+ guard spansWindow else { return }
245
250
  // layout pass: publish on the next turn instead of mutating SwiftUI state mid-update
246
- DispatchQueue.main.async { [weak self] in self?.onChange?() }
251
+ DispatchQueue.main.async { ReservedRegions.publish(regions) }
252
+ }
253
+
254
+ private var spansWindow: Bool {
255
+ guard let window else { return false }
256
+ let frame = convert(bounds, to: nil)
257
+ return abs(frame.minX) < 1 && abs(frame.maxX - window.bounds.width) < 1 && abs(frame.minY) < 1
247
258
  }
248
259
 
249
260
  override func didMoveToWindow() {
@@ -265,17 +276,9 @@ private final class ReservedRegionsProbe: UIView {
265
276
  }
266
277
 
267
278
  private struct ReservedRegionsProbeView: UIViewRepresentable {
268
- let onChange: () -> Void
279
+ func makeUIView(context: Context) -> ReservedRegionsProbe { ReservedRegionsProbe() }
269
280
 
270
- func makeUIView(context: Context) -> ReservedRegionsProbe {
271
- let probe = ReservedRegionsProbe()
272
- probe.onChange = onChange
273
- return probe
274
- }
275
-
276
- func updateUIView(_ probe: ReservedRegionsProbe, context: Context) {
277
- probe.onChange = onChange
278
- }
281
+ func updateUIView(_ probe: ReservedRegionsProbe, context: Context) {}
279
282
  }
280
283
 
281
284
  // MARK: - Environment
@@ -307,7 +310,11 @@ private struct ScreenLayoutReader: ViewModifier {
307
310
  .onChange(of: proxy.safeAreaInsets) { _ in refresh() }
308
311
  }
309
312
  )
310
- .background(ReservedRegionsProbeView { refresh() }.ignoresSafeArea())
313
+ .background(ReservedRegionsProbeView().ignoresSafeArea())
314
+ // any screen's probe may report, so every reader follows the shared value
315
+ .onReceive(NotificationCenter.default.publisher(for: ReservedRegions.didChange)) { _ in
316
+ refresh()
317
+ }
311
318
  .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
312
319
  refresh()
313
320
  }
@@ -291,8 +291,9 @@ struct BottomSheetView<Content: View>: View {
291
291
  }
292
292
  }
293
293
 
294
- /// Compose's `heightIn(max:)`: caps the sheet but keeps it hugging its content, which
295
- /// `.frame(maxHeight:)` would not — a flexible frame grows to the cap.
294
+ /// Compose's `heightIn(max:)`. `.frame(maxHeight:)` grows to the cap whenever the proposal is
295
+ /// larger, leaving a gap above the header; this sizes the sheet to its content and only caps it
296
+ /// when taller.
296
297
  @available(iOS 16.0, *)
297
298
  private struct CapHeight: ViewModifier {
298
299
  let maxHeight: CGFloat
@@ -307,8 +308,11 @@ private struct CapHeightLayout: Layout {
307
308
  let maxHeight: CGFloat
308
309
 
309
310
  func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
310
- let height = min(proposal.height ?? .infinity, maxHeight)
311
- return subviews.first?.sizeThatFits(ProposedViewSize(width: proposal.width, height: height)) ?? .zero
311
+ guard let subview = subviews.first else { return .zero }
312
+ let cap = min(maxHeight, proposal.height ?? .infinity)
313
+ // ideal first: a scroll view would otherwise fill whatever height it is offered
314
+ let ideal = subview.sizeThatFits(ProposedViewSize(width: proposal.width, height: nil))
315
+ return subview.sizeThatFits(ProposedViewSize(width: proposal.width, height: min(ideal.height, cap)))
312
316
  }
313
317
 
314
318
  func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKits'
3
- s.version = '0.167.1-duo.12'
3
+ s.version = '0.167.1-duo.13'
4
4
  s.summary = 'MoMoUIKits for iOS'
5
5
  s.homepage = 'https://momo.vn'
6
6
  s.license = { :type => 'MIT' }
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKitsDemo'
3
- s.version = '0.167.1-duo.12'
3
+ s.version = '0.167.1-duo.13'
4
4
  s.summary = 'Demo browser for MoMoUIKits SwiftUI components'
5
5
  s.homepage = 'https://momo.vn'
6
6
  s.license = { :type => 'MIT' }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.167.1-duo.12",
3
+ "version": "0.167.1-duo.13",
4
4
  "private": false,
5
5
  "dependeNavigationncies": {},
6
6
  "devDependencies": {},