Skip to content

0.12.0

Latest

Choose a tag to compare

@Luminoid Luminoid released this 05 Aug 06:35

[0.12.0] - 2026-08-04

Changed

  • BREAKING: LMKPhotoGridDataSource.photoGridImage(at:) is now asyncfunc photoGridImage(at index: Int) -> UIImage?func photoGridImage(at index: Int) async -> UIImage?, so providers can decode images off the main actor instead of being forced into synchronous loads. Migration: add async to the implementation. A source holding decoded images returns them unchanged; a disk- or network-backed source should decode off the main actor and return a ready-to-display image. Grid cells show a neutral placeholder while an image loads, and a slow result never lands on the wrong cell. Sort, pinch-to-zoom columns, content-mode toggle, and Live Photo badges are unchanged.
  • BREAKING: LMKPhotoBrowserDataSource.photo(at:) is now async — same shape and migration as the grid method. Browser pages show a placeholder while loading; zoom, delete, and Live Photo playback behave as before. LMKSinglePhotoViewer conforms by returning its in-memory image immediately.
  • SnapKit 6 — Dependency floor raised from 5.7.0 to 6.0.0. SnapKit 6's breaking changes are packaging-only (CocoaPods/Carthage removal, platform floors, Swift 6 toolchain); no source changes are needed at this package's iOS 18 floor.
  • UIView.lmk_applyBorder hairline default — The width parameter now defaults to LMKLayout.hairline (one physical pixel at the current screen scale) instead of 1pt. Callers passing an explicit width are unaffected.

Added

  • LMKEmptyStateView action buttonconfigure(message:icon:style:action:) gains an optional Action (title, optional leading SF Symbol, LMKButton.Style, tap handler), rendered as a button below the message in the .fullScreen and .card styles; the horizontal .inline style ignores it. setAction(_:) adds, replaces, or removes the button after configure. The view sizes itself to its content, so a long message at a large Dynamic Type size can never overlap the button; hosts should pass an Action instead of anchoring their own button to the view's bottom edge. With an action present, VoiceOver exposes the message and button as separate elements.
  • LMKBottomSheetController.avoidsKeyboard — Default true: the sheet lifts by the keyboard's actual overlap (floating keyboards and reduced-size windows lift only as much as they are covered), animates with the keyboard, and restores on hide; drag-to-dismiss still works while lifted. While enabled, the controller owns the keyboard offset: subclasses with hand-rolled keyboard observers should delete them, or return false for fully manual control. LMKKeyboardObserver.KeyboardInfo gains a frameEnd rect.
  • LMKScrollStackViewController navigation bar + keyboard hooks — Overridable navigationBar: LMKNavigationBar? (default nil) pins a custom bar with the scroll view starting below it, and installsKeyboardAdjustment (default true) enables keyboard avoidance on the scroll view out of the box. Existing subclasses keep identical layout with both defaults.
  • LMKFormScaffold — Static builders for form screens that are not LMKScrollStackViewController subclasses: makeScrollView(keyboardDismissMode:) (keyboard avoidance pre-installed), makeContentStack(spacing:), and install(scrollView:stack:in:below:contentInsets:) pinning the pair into a host view below an optional top anchor with design-token insets.
  • LMKSegmentedPageController.pageContainerView — Overridable container view for the paged content (default: the controller's own view), letting subclasses confine pages to a sub-region, for example below a fixed header.
  • LMKPointerStyle — Window-safe UIPointerStyle factories for UIPointerInteractionDelegate: automatic(for:), highlight(for:), lift(for:), and hover(for:preferredTintMode:prefersShadow:prefersScaledContent:). Each returns nil unless the view is non-nil and attached to a window, so the result can be returned straight from the delegate. Use these instead of constructing UITargetedPreview(view:) directly, which crashes when the view has left its window (a cell recycled or a sheet dismissed mid-hover).
  • Icon list row pointer hoverlmk_configureIconListRow gains pointerEnabled: Bool = true, giving the row pointer hover feedback on iPad and Mac Catalyst. Inert on platforms without pointer support.
  • LMKLayout.hairline — One physical pixel at the current screen scale, with hairline(forScale:) for an explicit scale. Deliberately not a theme axis: display physics, not branding.

Fixed

  • Keyboard avoidance leaving the focused field coveredUIScrollView.lmk_enableKeyboardAdjustment() (added in 0.11.0) could fail to bring the focused field above the keyboard, including when focus moved between fields or the layout resized while the keyboard stayed up. Fields now reliably scroll into view.
  • Bottom sheet invisible in container controllers — A sheet added via LMKBottomSheetController.addAsChild to a container such as UINavigationController never animated in: it sat invisible while its clear dimming view blocked touches. The sheet now appears regardless of the host.
  • LMKPhotoBrowserCell pointer crash — Hovering a photo cell on iPad or Mac Catalyst while it left the view hierarchy could crash the app. The cell now routes its pointer style through LMKPointerStyle.
  • LMKEmptyStateView zero-height collapse — With no host-imposed height the view collapsed to zero and its content spilled over neighboring views (most visibly in stack layouts). The view now takes its content height when the host imposes none; a host-imposed height still wins and centers the content.
  • LMKEmptyStateView Dynamic Type reflow — A mid-session text-size change now reflows the message immediately instead of waiting for the next configure() call.
  • LMKActionSheet subtitle crush in tall sheets — When enough rows pushed the sheet past its max-height cap, the scroll view's content-hugging constraint tied with the labels' vertical compression resistance (both priority 750) and the solver squashed every title and subtitle toward zero height instead of scrolling. The hug constraint now sits below label compression resistance, so overflowing sheets scroll and rows keep their content height.

Infrastructure

  • 1008 → 1073 tests (147 suites); 120 → 122 source files.
  • Example app: the photo grid and browser pages demonstrate the async image data sources, and the catalog showcases the other new APIs.