From e379deb66589c53464bcd0f8a7b28d59de766530 Mon Sep 17 00:00:00 2001 From: VashenkoNikita Date: Wed, 26 Apr 2023 14:45:57 +0300 Subject: [PATCH 1/9] task fix bottom sheet and data botton sheet --- moko-widgets-bottomsheet.podspec | 4 +- moko-widgets-datetime-picker.podspec | 4 +- .../iosMain/swift/BottomSheetController.swift | 63 ++++++++++++------- .../swift/DateBottomSheetController.swift | 60 ++++++++++-------- 4 files changed, 77 insertions(+), 54 deletions(-) diff --git a/moko-widgets-bottomsheet.podspec b/moko-widgets-bottomsheet.podspec index 5aeb2d86..cce9062e 100644 --- a/moko-widgets-bottomsheet.podspec +++ b/moko-widgets-bottomsheet.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'moko-widgets-bottomsheet' - spec.version = '0.1.0' + spec.version = '0.2.0' spec.homepage = 'https://github.com/icerockdev/moko-widgets' spec.source = { :git => "https://github.com/icerockdev/moko-widgets.git", :tag => "release/#{spec.version}" } spec.authors = 'IceRock Development' @@ -11,7 +11,7 @@ Pod::Spec.new do |spec| spec.source_files = "widgets-bottomSheet/src/iosMain/swift/**/*.{h,m,swift}" spec.resources = "widgets-bottomSheet/src/iosMain/bundle/**/*" - spec.dependency 'FloatingPanel', '~> 1.7.2' + spec.dependency 'FloatingPanel', '~> 2.6.1' spec.ios.deployment_target = '11.0' spec.swift_version = '5.0' diff --git a/moko-widgets-datetime-picker.podspec b/moko-widgets-datetime-picker.podspec index 6a6e398c..7483aeb3 100644 --- a/moko-widgets-datetime-picker.podspec +++ b/moko-widgets-datetime-picker.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'moko-widgets-datetime-picker' - spec.version = '0.1.0' + spec.version = '0.2.0' spec.homepage = 'https://github.com/icerockdev/moko-widgets' spec.source = { :git => "https://github.com/icerockdev/moko-widgets.git", :tag => "release/#{spec.version}" } spec.authors = 'IceRock Development' @@ -11,7 +11,7 @@ Pod::Spec.new do |spec| spec.source_files = "widgets-datetime-picker/src/iosMain/swift/**/*.{h,m,swift}" spec.resources = "widgets-datetime-picker/src/iosMain/bundle/**/*" - spec.dependency 'FloatingPanel', '~> 1.7.2' + spec.dependency 'FloatingPanel', '~> 2.6.1' spec.ios.deployment_target = '11.0' spec.swift_version = '5.0' diff --git a/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift b/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift index 0195de44..cf30c893 100644 --- a/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift +++ b/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift @@ -48,6 +48,9 @@ private var AssociatedDelegateHandle: UInt8 = 0 fpc.delegate = delegate fpc.backdropView.backgroundColor = UIColor.black fpc.isRemovalInteractionEnabled = true + let surface = SurfaceAppearance() + surface.cornerRadius = 14 + fpc.surfaceView.appearance = surface view.superview?.layer.maskedCorners = [CACornerMask.layerMinXMinYCorner, CACornerMask.layerMaxXMinYCorner] view.superview?.layer.masksToBounds = true @@ -59,6 +62,10 @@ private var AssociatedDelegateHandle: UInt8 = 0 objc_setAssociatedObject(fpc, &AssociatedDelegateHandle, delegate, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) vc.present(fpc, animated: true, completion: nil) + + DispatchQueue.main.async { + view.setNeedsLayout() + } } @objc public func dismiss() { @@ -76,44 +83,52 @@ class FloatingDelegate: FloatingPanelControllerDelegate { self.onDismiss = onDismiss } - func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? { + func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout { return floatingLayout } func floatingPanelDidEndDecelerating(_ vc: FloatingPanelController) { - if vc.position == .hidden { + if vc.state == .hidden { vc.removePanelFromParent(animated: true) vc.dismiss(animated: false, completion: nil) onDismiss(false) } } - func floatingPanelDidEndRemove(_ vc: FloatingPanelController) { + func floatingPanelDidRemove(_ vc: FloatingPanelController) { onDismiss(false) } } class BottomSheetLayout: FloatingPanelLayout { - var initialPosition: FloatingPanelPosition = .full - - private let preferredHeight: CGFloat - - init(preferredHeight: CGFloat) { - self.preferredHeight = preferredHeight - } - - func insetFor(position: FloatingPanelPosition) -> CGFloat? { - switch (position) { - case .half: return 0 - case .full: return UIScreen.main.bounds.size.height - preferredHeight - 30.0 - case .tip: return 0 - case .hidden: return 0 + private let preferredHeight: CGFloat + + var position: FloatingPanelPosition { + return .bottom + } + + var initialState: FloatingPanelState { + return .full + } + + var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] { + return [ + .full: FloatingPanelLayoutAnchor(absoluteInset: preferredHeight, edge: .bottom, referenceGuide: .safeArea) + ] + } + + init(preferredHeight: CGFloat) { + self.preferredHeight = preferredHeight + } + + func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] { + return [ + surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 0.0), + surfaceView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: 0.0), + ] + } + + func backdropAlpha(for state: FloatingPanelState) -> CGFloat { + return state == .full ? 0.3 : 0.0 } - } - - var supportedPositions: Set = [.full, .hidden] - - func backdropAlphaFor(position: FloatingPanelPosition) -> CGFloat { - return 0.3 - } } diff --git a/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift b/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift index 56185f1a..59e04be9 100644 --- a/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift +++ b/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift @@ -41,9 +41,9 @@ private var AssociatedDelegateHandle: UInt8 = 0 fpc.backdropView.backgroundColor = UIColor.black fpc.isRemovalInteractionEnabled = true fpc.surfaceView.grabberHandle.isHidden = true - fpc.surfaceView.grabberHandleHeight = 0 - fpc.surfaceView.grabberTopPadding = 0 - fpc.surfaceView.contentInsets = .zero + fpc.surfaceView.grabberHandleSize.height = 0 + fpc.surfaceView.grabberHandlePadding = 0 + //fpc.surfaceView.contentInsets = .zero controller = fpc self.onDismiss = onDismiss @@ -68,12 +68,12 @@ class FloatingDelegate: FloatingPanelControllerDelegate { self.onDismiss = onDismiss } - func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? { + func floatingPanel(_ vc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout { return floatingLayout } func floatingPanelDidEndDecelerating(_ vc: FloatingPanelController) { - if vc.position == .hidden { + if vc.state == .hidden { vc.removePanelFromParent(animated: true) vc.dismiss(animated: false, completion: nil) onDismiss(false) @@ -86,26 +86,34 @@ class FloatingDelegate: FloatingPanelControllerDelegate { } class BottomSheetLayout: FloatingPanelLayout { - var initialPosition: FloatingPanelPosition = .half - - private let preferredHeight: CGFloat - - init(preferredHeight: CGFloat) { - self.preferredHeight = preferredHeight - } - - func insetFor(position: FloatingPanelPosition) -> CGFloat? { - switch (position) { - case .half: return preferredHeight - case .full: return 0 - case .tip: return 0 - case .hidden: return 0 + private let preferredHeight: CGFloat + + var position: FloatingPanelPosition { + return .bottom + } + + var initialState: FloatingPanelState { + return .half + } + + var anchors: [FloatingPanelState : FloatingPanelLayoutAnchoring] { + return [ + .half: FloatingPanelLayoutAnchor(absoluteInset: preferredHeight, edge: .bottom, referenceGuide: .safeArea) + ] + } + + init(preferredHeight: CGFloat) { + self.preferredHeight = preferredHeight + } + + func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] { + return [ + surfaceView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 0.0), + surfaceView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: 0.0), + ] + } + + func backdropAlpha(for state: FloatingPanelState) -> CGFloat { + return 0.3 } - } - - var supportedPositions: Set = [.half, .hidden] - - func backdropAlphaFor(position: FloatingPanelPosition) -> CGFloat { - return 0.3 - } } From 67fff15c612f79a12213eb53a484af5f30c4b06d Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Wed, 26 Apr 2023 19:17:31 +0600 Subject: [PATCH 2/9] fix cinterop errors --- sample/ios-app/Podfile.lock | 18 +++++++++--------- .../iosMain/swift/BottomSheetController.swift | 2 +- .../swift/DateBottomSheetController.swift | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/sample/ios-app/Podfile.lock b/sample/ios-app/Podfile.lock index e6e08332..e6eb4df9 100644 --- a/sample/ios-app/Podfile.lock +++ b/sample/ios-app/Podfile.lock @@ -1,10 +1,10 @@ PODS: - - FloatingPanel (1.7.2) - - moko-widgets-bottomsheet (0.1.0): - - FloatingPanel (~> 1.7.2) + - FloatingPanel (2.6.1) + - moko-widgets-bottomsheet (0.2.0): + - FloatingPanel (~> 2.6.1) - moko-widgets-collection (0.1.0) - - moko-widgets-datetime-picker (0.1.0): - - FloatingPanel (~> 1.7.2) + - moko-widgets-datetime-picker (0.2.0): + - FloatingPanel (~> 2.6.1) - moko-widgets-image-network (0.1.0): - SDWebImage (~> 5.0) - mppLibraryIos (0.1.0) @@ -41,10 +41,10 @@ EXTERNAL SOURCES: :path: "../mpp-library" SPEC CHECKSUMS: - FloatingPanel: b275a35d0a09be4bd37025e710a6a1d063bfc161 - moko-widgets-bottomsheet: 302953af27d9bcb6b0f0f51511ccce5e8ff3aa92 + FloatingPanel: de6bb891912ede28da7b1140f7b583c474fec28a + moko-widgets-bottomsheet: 45cb9eec56ad3d44f0f3f81421f1ae81b671cfdb moko-widgets-collection: 722c6fca0b0dcab0b54fdb674b4638d4f715434c - moko-widgets-datetime-picker: 0d8e11202cfc6041ca70234f2e6994900161fb04 + moko-widgets-datetime-picker: d25eb6926078eae46a311af26294c78b09f0058d moko-widgets-image-network: 074870717767dfcc1a0e942a0a070ad183c01195 mppLibraryIos: 72c3984fbaa53978d678e62096fd613e67839f0c MultiPlatformLibrary: 176fb8ade516666cd47e93de1b71ba0441a541bb @@ -52,4 +52,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: dd6f9b7e6c038aff8581037bf3a09cf3c1c947f9 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.0 diff --git a/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift b/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift index cf30c893..4d4f83be 100644 --- a/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift +++ b/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift @@ -7,7 +7,7 @@ import FloatingPanel private var AssociatedDelegateHandle: UInt8 = 0 -@objc public class BottomSheetController: NSObject, FloatingPanelControllerDelegate { +@objc public class BottomSheetController: NSObject { private weak var controller: FloatingPanelController? private var onDismiss: ((Bool) -> Void)? diff --git a/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift b/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift index 59e04be9..6f7c01a8 100644 --- a/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift +++ b/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift @@ -7,7 +7,7 @@ import FloatingPanel private var AssociatedDelegateHandle: UInt8 = 0 -@objc public class DateBottomSheetController: NSObject, FloatingPanelControllerDelegate { +@objc public class DateBottomSheetController: NSObject { private weak var controller: FloatingPanelController? private var onDismiss: ((Bool) -> Void)? From c60dcceaab832c4f3bc6955fbbeb89b8bfef5cf0 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Wed, 26 Apr 2023 19:17:35 +0600 Subject: [PATCH 3/9] up version --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f92d28ff..c1f0b27d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlinVersion = "1.8.10" -mokoWidgetsVersion = "0.2.1" +mokoWidgetsVersion = "0.2.2" mokoResourcesVersion = "0.21.2" mokoMvvmVersion = "0.16.0" mokoFieldsVersion = "0.12.0" From 8aacebec3f3121e50332c95051d6ab09f406f38a Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Thu, 27 Apr 2023 18:46:09 +0600 Subject: [PATCH 4/9] displayLink weak reference --- .../core/factory/ButtonWithIconViewFactory.kt | 46 ++++--- .../moko/widgets/core/utils/BackgroundExt.kt | 118 +++++++++--------- .../moko/widgets/core/utils/UIControlExt.kt | 29 ++++- 3 files changed, 115 insertions(+), 78 deletions(-) diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt index 070f6a5b..257a272b 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt @@ -90,6 +90,7 @@ actual class ButtonWithIconViewFactory actual constructor( button.setTitle(title = processedText, forState = UIControlStateNormal) } } + else -> throw Exception("Not supported content type") } @@ -109,14 +110,17 @@ actual class ButtonWithIconViewFactory actual constructor( contentAttribute = UISemanticContentAttributeForceLeftToRight contentAlignment = UIControlContentHorizontalAlignmentLeft } + IconGravity.END -> { contentAttribute = UISemanticContentAttributeForceRightToLeft contentAlignment = UIControlContentHorizontalAlignmentLeft } + IconGravity.TEXT_START, null -> { contentAttribute = UISemanticContentAttributeForceLeftToRight contentAlignment = UIControlContentHorizontalAlignmentCenter } + IconGravity.TEXT_END -> { contentAttribute = UISemanticContentAttributeForceRightToLeft contentAlignment = UIControlContentHorizontalAlignmentCenter @@ -127,27 +131,36 @@ actual class ButtonWithIconViewFactory actual constructor( button.imageView?.let { button.bringSubviewToFront(it) } - var buttonWidth = 0.0 - // TODO remove this bad :( - button.displayLink { - val icPadding: Double = iconPadding?.toDouble() ?: 0.0 + setupLayoutUpdate(button, viewFactory = this) + + return ViewBundle( + view = button, + size = size, + margins = margins + ) + } - val newButtonWidth = button.bounds.useContents { this.size.width } - if (buttonWidth == newButtonWidth) return@displayLink - buttonWidth = newButtonWidth + private fun setupLayoutUpdate(button: UIButton, viewFactory: ButtonWithIconViewFactory) { + button.displayLink( + context = viewFactory, + objectForSkipCheck = { it.bounds } + ) { button, viewFactory -> + val icPadding: Double = viewFactory.iconPadding?.toDouble() ?: 0.0 + + val buttonWidth: CGFloat = button.bounds.useContents { this.size.width } val iconWidth = button.imageView?.frame?.useContents { this.size.width } ?: 0.0 val titleWidth = button.titleLabel?.frame?.useContents { this.size.width } ?: 0.0 - val paddingTop = padding?.top?.toDouble() ?: 0.0 - var paddingLeft = padding?.start?.toDouble() ?: 0.0 - var paddingRight = padding?.end?.toDouble() ?: 0.0 - val paddingBottom = padding?.bottom?.toDouble() ?: 0.0 + val paddingTop = viewFactory.padding?.top?.toDouble() ?: 0.0 + var paddingLeft = viewFactory.padding?.start?.toDouble() ?: 0.0 + var paddingRight = viewFactory.padding?.end?.toDouble() ?: 0.0 + val paddingBottom = viewFactory.padding?.bottom?.toDouble() ?: 0.0 val titleLeftInset: CGFloat val titleRightInset: CGFloat - when (iconGravity) { + when (viewFactory.iconGravity) { IconGravity.START -> { val inset = buttonWidth - iconWidth - titleWidth - @@ -157,6 +170,7 @@ actual class ButtonWithIconViewFactory actual constructor( titleRightInset = -inset paddingRight += inset } + IconGravity.END -> { val inset = buttonWidth - iconWidth - titleWidth - @@ -166,11 +180,13 @@ actual class ButtonWithIconViewFactory actual constructor( titleRightInset = inset paddingLeft += inset } + IconGravity.TEXT_START, null -> { titleLeftInset = icPadding titleRightInset = -icPadding paddingRight += icPadding } + IconGravity.TEXT_END -> { titleLeftInset = -icPadding titleRightInset = icPadding @@ -191,11 +207,5 @@ actual class ButtonWithIconViewFactory actual constructor( right = paddingRight ) } - - return ViewBundle( - view = button, - size = size, - margins = margins - ) } } diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt index ca317114..9964feaf 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt @@ -10,21 +10,19 @@ import dev.icerock.moko.widgets.core.style.background.Direction import dev.icerock.moko.widgets.core.style.background.Fill import dev.icerock.moko.widgets.core.style.state.PressableState import kotlinx.cinterop.useContents +import platform.CoreGraphics.CGFloat import platform.CoreGraphics.CGPointMake import platform.CoreGraphics.CGRectMake -import platform.Foundation.NSRunLoop -import platform.Foundation.NSRunLoopCommonModes -import platform.QuartzCore.CADisplayLink import platform.QuartzCore.CAGradientLayer import platform.QuartzCore.CALayer import platform.QuartzCore.CATransaction import platform.UIKit.UIButton import platform.UIKit.UIColor +import platform.UIKit.UIControl import platform.UIKit.UIView import platform.UIKit.adjustsImageWhenDisabled import platform.UIKit.adjustsImageWhenHighlighted import platform.UIKit.backgroundColor -import platform.UIKit.window @Suppress("MagicNumber", "ComplexMethod") fun Background.caLayer(): CALayer { @@ -35,6 +33,7 @@ fun Background.caLayer(): CALayer { is Fill.Solid -> backgroundLayer = CALayer().apply { backgroundColor = fill.color.toUIColor().CGColor } + is Fill.Gradient -> { backgroundLayer = CAGradientLayer().apply { colors = cgColors(fill.colors.map { @@ -58,6 +57,7 @@ fun Background.caLayer(): CALayer { endPoint = end } } + null -> { backgroundLayer = CALayer() } @@ -84,56 +84,62 @@ fun UIButton.applyStateBackgroundIfNeeded(background: PressableState + val (width: CGFloat, height: CGFloat) = button.layer.bounds.useContents { + this.size.width to this.size.height + } - // FIXME memoryleak, perfomance problem !!! - var link: CADisplayLink? = null + CATransaction.begin() + CATransaction.setDisableActions(true) - link = displayLink { - if (window != null) { - val (width, height) = layer.bounds.useContents { size.width to size.height } + stateLayers.normal.frame = CGRectMake(0.0, 0.0, width, height) + stateLayers.disabled.frame = CGRectMake(0.0, 0.0, width, height) + stateLayers.pressed.frame = CGRectMake(0.0, 0.0, width, height) - CATransaction.begin() - CATransaction.setDisableActions(true) + stateLayers.update(button) - normalBg.frame = CGRectMake(0.0, 0.0, width, height) - disabledBg.frame = CGRectMake(0.0, 0.0, width, height) - pressedBg.frame = CGRectMake(0.0, 0.0, width, height) + CATransaction.commit() + } +} - updateLayers() +private data class StateLayers( + val normal: CALayer, + val disabled: CALayer, + val pressed: CALayer +) { + fun update(control: UIControl) { + if (!control.isEnabled()) { + disabled.opacity = 1.0f + normal.opacity = 0f + pressed.opacity = 0f + return + } - CATransaction.commit() + if (control.isHighlighted()) { + pressed.opacity = 1.0f + normal.opacity = 0f } else { - link?.removeFromRunLoop(NSRunLoop.currentRunLoop, NSRunLoopCommonModes) + normal.opacity = 1.0f + pressed.opacity = 0f } + disabled.opacity = 0f } } @@ -156,24 +162,22 @@ fun UIView.applyBackgroundIfNeeded(background: Background?) { this.backgroundColor = UIColor.clearColor - val bgLayer = background.caLayer() + val bgLayer: CALayer = background.caLayer() layer.insertSublayer(bgLayer, 0U) - // FIXME memoryleak, perfomance problem !!! - var link: CADisplayLink? = null - - link = displayLink { - if (window != null) { - val (width, height) = layer.bounds.useContents { size.width to size.height } + this.displayLink( + context = bgLayer, + objectForSkipCheck = { it.layer.bounds } + ) { view, bgLayer -> + val (width: CGFloat, height: CGFloat) = view.layer.bounds.useContents { + this.size.width to this.size.height + } - CATransaction.begin() - CATransaction.setDisableActions(true) + CATransaction.begin() + CATransaction.setDisableActions(true) - bgLayer.frame = CGRectMake(0.0, 0.0, width, height) + bgLayer.frame = CGRectMake(0.0, 0.0, width, height) - CATransaction.commit() - } else { - link?.removeFromRunLoop(NSRunLoop.currentRunLoop, NSRunLoopCommonModes) - } + CATransaction.commit() } } diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt index aa8ee137..88b5d0c7 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt @@ -13,9 +13,11 @@ import platform.QuartzCore.CADisplayLink import platform.UIKit.UIControl import platform.UIKit.UIControlEvents import platform.UIKit.UIGestureRecognizer +import platform.UIKit.UIView import platform.darwin.NSObject import platform.objc.OBJC_ASSOCIATION_RETAIN import platform.objc.objc_setAssociatedObject +import kotlin.native.ref.WeakReference fun UIControl.setEventHandler(controlEvent: UIControlEvents, action: () -> Unit) { val target = LambdaTarget(action) @@ -50,10 +52,31 @@ fun UIGestureRecognizer.setHandler(action: () -> Unit) { ) } -fun NSObject.displayLink(action: () -> Unit): CADisplayLink { - val target = LambdaTarget(action) +fun T.displayLink( + context: CTX, + objectForSkipCheck: (T) -> Any, + action: (T, CTX) -> Unit +) { + val ref: WeakReference = WeakReference(this) + + var displayLink: CADisplayLink? = null + + var oldState: Any = objectForSkipCheck(this) + val target = LambdaTarget { + val strongRef: T? = ref.get() + if (strongRef == null) { + displayLink?.removeFromRunLoop(NSRunLoop.currentRunLoop, NSRunLoopCommonModes) + displayLink = null + return@LambdaTarget + } + + val newState: Any = objectForSkipCheck(strongRef) + if (newState == oldState) return@LambdaTarget + + action(strongRef, context) + } - return CADisplayLink.displayLinkWithTarget( + CADisplayLink.displayLinkWithTarget( target = target, selector = NSSelectorFromString("displayLink:") ).apply { From 48e46f9863e7c3e030dc6a8e02d93de566aa43e6 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Thu, 27 Apr 2023 19:43:16 +0600 Subject: [PATCH 5/9] KVO instead of displayLink to fix perfomance problem --- widgets/src/iosMain/def/objcAddtition.def | 4 ++ .../core/factory/ButtonWithIconViewFactory.kt | 5 +- .../moko/widgets/core/utils/BackgroundExt.kt | 6 +- .../moko/widgets/core/utils/UIControlExt.kt | 67 ++++++++++--------- 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/widgets/src/iosMain/def/objcAddtition.def b/widgets/src/iosMain/def/objcAddtition.def index 9579f379..32f65e86 100644 --- a/widgets/src/iosMain/def/objcAddtition.def +++ b/widgets/src/iosMain/def/objcAddtition.def @@ -36,3 +36,7 @@ NSURLSessionDataTask* dataTask(NSURLSession* session, NSURL* url, void (^complet } ]; } + +@protocol KeyValueObserver +- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context; +@end diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt index 257a272b..ce3bbed6 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt @@ -20,7 +20,7 @@ import dev.icerock.moko.widgets.core.style.view.WidgetSize import dev.icerock.moko.widgets.core.utils.applyStateBackgroundIfNeeded import dev.icerock.moko.widgets.core.utils.applyTextStyleIfNeeded import dev.icerock.moko.widgets.core.utils.bind -import dev.icerock.moko.widgets.core.utils.displayLink +import dev.icerock.moko.widgets.core.utils.onBoundsChanged import dev.icerock.moko.widgets.core.utils.setEventHandler import dev.icerock.moko.widgets.core.widget.ButtonWidget import kotlinx.cinterop.useContents @@ -141,9 +141,8 @@ actual class ButtonWithIconViewFactory actual constructor( } private fun setupLayoutUpdate(button: UIButton, viewFactory: ButtonWithIconViewFactory) { - button.displayLink( + button.onBoundsChanged( context = viewFactory, - objectForSkipCheck = { it.bounds } ) { button, viewFactory -> val icPadding: Double = viewFactory.iconPadding?.toDouble() ?: 0.0 diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt index 9964feaf..18651878 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt @@ -98,9 +98,8 @@ fun UIButton.applyStateBackgroundIfNeeded(background: PressableState val (width: CGFloat, height: CGFloat) = button.layer.bounds.useContents { this.size.width to this.size.height @@ -165,9 +164,8 @@ fun UIView.applyBackgroundIfNeeded(background: Background?) { val bgLayer: CALayer = background.caLayer() layer.insertSublayer(bgLayer, 0U) - this.displayLink( + this.onBoundsChanged( context = bgLayer, - objectForSkipCheck = { it.layer.bounds } ) { view, bgLayer -> val (width: CGFloat, height: CGFloat) = view.layer.bounds.useContents { this.size.width to this.size.height diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt index 88b5d0c7..e3af7a9d 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt @@ -4,12 +4,13 @@ package dev.icerock.moko.widgets.core.utils +import dev.icerock.moko.widgets.objc.KeyValueObserverProtocol +import kotlinx.cinterop.COpaquePointer import kotlinx.cinterop.ObjCAction import kotlinx.cinterop.cstr -import platform.Foundation.NSRunLoop -import platform.Foundation.NSRunLoopCommonModes +import platform.Foundation.NSKeyValueObservingOptionNew import platform.Foundation.NSSelectorFromString -import platform.QuartzCore.CADisplayLink +import platform.Foundation.addObserver import platform.UIKit.UIControl import platform.UIKit.UIControlEvents import platform.UIKit.UIGestureRecognizer @@ -52,49 +53,51 @@ fun UIGestureRecognizer.setHandler(action: () -> Unit) { ) } -fun T.displayLink( +fun V.onBoundsChanged( context: CTX, - objectForSkipCheck: (T) -> Any, - action: (T, CTX) -> Unit + action: (V, CTX) -> Unit ) { - val ref: WeakReference = WeakReference(this) + val ref: WeakReference = WeakReference(this) - var displayLink: CADisplayLink? = null - - var oldState: Any = objectForSkipCheck(this) - val target = LambdaTarget { - val strongRef: T? = ref.get() - if (strongRef == null) { - displayLink?.removeFromRunLoop(NSRunLoop.currentRunLoop, NSRunLoopCommonModes) - displayLink = null - return@LambdaTarget - } - - val newState: Any = objectForSkipCheck(strongRef) - if (newState == oldState) return@LambdaTarget + val target = ObserverObject { + val strongRef: V = ref.get() ?: return@ObserverObject action(strongRef, context) } - CADisplayLink.displayLinkWithTarget( - target = target, - selector = NSSelectorFromString("displayLink:") - ).apply { - frameInterval = 1 - addToRunLoop(NSRunLoop.currentRunLoop, NSRunLoopCommonModes) - } + objc_setAssociatedObject( + `object` = this, + key = "onBoundsChanged".cstr, + value = target, + policy = OBJC_ASSOCIATION_RETAIN + ) + + this.addObserver( + observer = target, + forKeyPath = "bounds", + options = NSKeyValueObservingOptionNew, + context = null + ) } -class LambdaTarget(val lambda: () -> Unit) : NSObject() { +private class ObserverObject( + private val lambda: () -> Unit +) : NSObject(), KeyValueObserverProtocol { - @ObjCAction - fun action() { + override fun observeValueForKeyPath( + keyPath: String?, + ofObject: Any?, + change: Map?, + context: COpaquePointer? + ) { lambda() } +} + +class LambdaTarget(val lambda: () -> Unit) : NSObject() { @ObjCAction - @Suppress("UnusedPrivateMember") - fun displayLink(link: CADisplayLink) { + fun action() { lambda() } } From 864e7d7e863b5321d7bc0cf03621e9d75ca2c0a4 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Thu, 27 Apr 2023 20:33:58 +0600 Subject: [PATCH 6/9] weakref for control events --- .../widgets/core/factory/ButtonWithIconViewFactory.kt | 2 +- .../dev/icerock/moko/widgets/core/utils/UIControlExt.kt | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt index ce3bbed6..3a595843 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt @@ -100,7 +100,7 @@ actual class ButtonWithIconViewFactory actual constructor( button.setEventHandler( controlEvent = UIControlEventTouchUpInside, - action = widget.onTap + action = { widget.onTap } ) val contentAttribute: UISemanticContentAttribute diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt index e3af7a9d..21464542 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt @@ -20,8 +20,13 @@ import platform.objc.OBJC_ASSOCIATION_RETAIN import platform.objc.objc_setAssociatedObject import kotlin.native.ref.WeakReference -fun UIControl.setEventHandler(controlEvent: UIControlEvents, action: () -> Unit) { - val target = LambdaTarget(action) +fun V.setEventHandler(controlEvent: UIControlEvents, action: (V) -> Unit) { + val weakReference: WeakReference = WeakReference(this) + val target = LambdaTarget { + val strongRef: V = weakReference.get() ?: return@LambdaTarget + + action(strongRef) + } addTarget( target = target, From d3069fd7f6c3bcf032f013c30da387fa81ea32c3 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Thu, 27 Apr 2023 22:12:40 +0600 Subject: [PATCH 7/9] fix missed highlighted & enabled update of background --- .../core/factory/ButtonWithIconViewFactory.kt | 5 +++-- .../core/factory/SystemButtonViewFactory.kt | 7 +++--- .../moko/widgets/core/utils/BackgroundExt.kt | 22 +++++++++++++++---- .../moko/widgets/core/utils/UIControlExt.kt | 9 ++++---- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt index 3a595843..2bf8906b 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/ButtonWithIconViewFactory.kt @@ -20,7 +20,7 @@ import dev.icerock.moko.widgets.core.style.view.WidgetSize import dev.icerock.moko.widgets.core.utils.applyStateBackgroundIfNeeded import dev.icerock.moko.widgets.core.utils.applyTextStyleIfNeeded import dev.icerock.moko.widgets.core.utils.bind -import dev.icerock.moko.widgets.core.utils.onBoundsChanged +import dev.icerock.moko.widgets.core.utils.observeKeyChanges import dev.icerock.moko.widgets.core.utils.setEventHandler import dev.icerock.moko.widgets.core.widget.ButtonWidget import kotlinx.cinterop.useContents @@ -141,7 +141,8 @@ actual class ButtonWithIconViewFactory actual constructor( } private fun setupLayoutUpdate(button: UIButton, viewFactory: ButtonWithIconViewFactory) { - button.onBoundsChanged( + button.observeKeyChanges( + keyPath = "bounds", context = viewFactory, ) { button, viewFactory -> val icPadding: Double = viewFactory.iconPadding?.toDouble() ?: 0.0 diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/SystemButtonViewFactory.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/SystemButtonViewFactory.kt index 80f336ae..d2401e61 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/SystemButtonViewFactory.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/factory/SystemButtonViewFactory.kt @@ -22,6 +22,7 @@ import dev.icerock.moko.widgets.core.utils.bind import dev.icerock.moko.widgets.core.utils.setEventHandler import dev.icerock.moko.widgets.core.widget.ButtonWidget import platform.UIKit.UIButton +import platform.UIKit.UIButtonType import platform.UIKit.UIButtonTypeCustom import platform.UIKit.UIButtonTypeSystem import platform.UIKit.UIControlEventTouchUpInside @@ -44,14 +45,13 @@ actual class SystemButtonViewFactory actual constructor( size: WS, viewFactoryContext: ViewFactoryContext ): ViewBundle { - - val buttonType = if (widget.content is ButtonWidget.Content.Icon) { + val buttonType: UIButtonType = if (widget.content is ButtonWidget.Content.Icon) { UIButtonTypeCustom } else { UIButtonTypeSystem } - val button = UIButton.buttonWithType(buttonType).apply { + val button: UIButton = UIButton.buttonWithType(buttonType).apply { translatesAutoresizingMaskIntoConstraints = false applyStateBackgroundIfNeeded(background) @@ -81,6 +81,7 @@ actual class SystemButtonViewFactory actual constructor( button.setTitle(title = processedText, forState = UIControlStateNormal) } } + is ButtonWidget.Content.Icon -> { content.image.bind { image -> image.apply(button) { diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt index 18651878..b467b549 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/BackgroundExt.kt @@ -98,7 +98,8 @@ fun UIButton.applyStateBackgroundIfNeeded(background: PressableState val (width: CGFloat, height: CGFloat) = button.layer.bounds.useContents { @@ -112,10 +113,22 @@ fun UIButton.applyStateBackgroundIfNeeded(background: PressableState + this.observeKeyChanges( + keyPath = keyPath, + context = stateLayers, + ) { button, stateLayers -> + CATransaction.begin() + CATransaction.setDisableActions(true) + + stateLayers.update(button) + + CATransaction.commit() + } + } } private data class StateLayers( @@ -164,7 +177,8 @@ fun UIView.applyBackgroundIfNeeded(background: Background?) { val bgLayer: CALayer = background.caLayer() layer.insertSublayer(bgLayer, 0U) - this.onBoundsChanged( + this.observeKeyChanges( + keyPath = "bounds", context = bgLayer, ) { view, bgLayer -> val (width: CGFloat, height: CGFloat) = view.layer.bounds.useContents { diff --git a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt index 21464542..398a9d40 100644 --- a/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt +++ b/widgets/src/iosMain/kotlin/dev/icerock/moko/widgets/core/utils/UIControlExt.kt @@ -20,7 +20,7 @@ import platform.objc.OBJC_ASSOCIATION_RETAIN import platform.objc.objc_setAssociatedObject import kotlin.native.ref.WeakReference -fun V.setEventHandler(controlEvent: UIControlEvents, action: (V) -> Unit) { +fun V.setEventHandler(controlEvent: UIControlEvents, action: (V) -> Unit) { val weakReference: WeakReference = WeakReference(this) val target = LambdaTarget { val strongRef: V = weakReference.get() ?: return@LambdaTarget @@ -58,7 +58,8 @@ fun UIGestureRecognizer.setHandler(action: () -> Unit) { ) } -fun V.onBoundsChanged( +fun V.observeKeyChanges( + keyPath: String, context: CTX, action: (V, CTX) -> Unit ) { @@ -72,14 +73,14 @@ fun V.onBoundsChanged( objc_setAssociatedObject( `object` = this, - key = "onBoundsChanged".cstr, + key = "observeKeyChanges-$keyPath".cstr, value = target, policy = OBJC_ASSOCIATION_RETAIN ) this.addObserver( observer = target, - forKeyPath = "bounds", + forKeyPath = keyPath, options = NSKeyValueObservingOptionNew, context = null ) From dae180882e0efc6df3e6adcb112c01bbe9e89255 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Thu, 27 Apr 2023 22:19:25 +0600 Subject: [PATCH 8/9] review fixes --- moko-widgets-bottomsheet.podspec | 2 +- moko-widgets-collection.podspec | 2 +- moko-widgets-datetime-picker.podspec | 2 +- moko-widgets-image-network.podspec | 2 +- .../src/iosMain/swift/BottomSheetController.swift | 4 ---- .../src/iosMain/swift/DateBottomSheetController.swift | 1 - 6 files changed, 4 insertions(+), 9 deletions(-) diff --git a/moko-widgets-bottomsheet.podspec b/moko-widgets-bottomsheet.podspec index cce9062e..479b3c1e 100644 --- a/moko-widgets-bottomsheet.podspec +++ b/moko-widgets-bottomsheet.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'moko-widgets-bottomsheet' - spec.version = '0.2.0' + spec.version = '0.2.2' spec.homepage = 'https://github.com/icerockdev/moko-widgets' spec.source = { :git => "https://github.com/icerockdev/moko-widgets.git", :tag => "release/#{spec.version}" } spec.authors = 'IceRock Development' diff --git a/moko-widgets-collection.podspec b/moko-widgets-collection.podspec index 40a89b27..1c4d2f3a 100644 --- a/moko-widgets-collection.podspec +++ b/moko-widgets-collection.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'moko-widgets-collection' - spec.version = '0.1.0' + spec.version = '0.2.2' spec.homepage = 'https://github.com/icerockdev/moko-widgets' spec.source = { :git => "https://github.com/icerockdev/moko-widgets.git", :tag => "release/#{spec.version}" } spec.authors = 'IceRock Development' diff --git a/moko-widgets-datetime-picker.podspec b/moko-widgets-datetime-picker.podspec index 7483aeb3..a422144f 100644 --- a/moko-widgets-datetime-picker.podspec +++ b/moko-widgets-datetime-picker.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'moko-widgets-datetime-picker' - spec.version = '0.2.0' + spec.version = '0.2.2' spec.homepage = 'https://github.com/icerockdev/moko-widgets' spec.source = { :git => "https://github.com/icerockdev/moko-widgets.git", :tag => "release/#{spec.version}" } spec.authors = 'IceRock Development' diff --git a/moko-widgets-image-network.podspec b/moko-widgets-image-network.podspec index 823b90fd..b25c0131 100644 --- a/moko-widgets-image-network.podspec +++ b/moko-widgets-image-network.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'moko-widgets-image-network' - spec.version = '0.1.0' + spec.version = '0.2.2' spec.homepage = 'https://github.com/icerockdev/moko-widgets' spec.source = { :git => "https://github.com/icerockdev/moko-widgets.git", :tag => "release/#{spec.version}" } spec.authors = 'IceRock Development' diff --git a/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift b/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift index 4d4f83be..35a96596 100644 --- a/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift +++ b/widgets-bottomsheet/src/iosMain/swift/BottomSheetController.swift @@ -62,10 +62,6 @@ private var AssociatedDelegateHandle: UInt8 = 0 objc_setAssociatedObject(fpc, &AssociatedDelegateHandle, delegate, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) vc.present(fpc, animated: true, completion: nil) - - DispatchQueue.main.async { - view.setNeedsLayout() - } } @objc public func dismiss() { diff --git a/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift b/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift index 6f7c01a8..6f8fa365 100644 --- a/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift +++ b/widgets-datetime-picker/src/iosMain/swift/DateBottomSheetController.swift @@ -43,7 +43,6 @@ private var AssociatedDelegateHandle: UInt8 = 0 fpc.surfaceView.grabberHandle.isHidden = true fpc.surfaceView.grabberHandleSize.height = 0 fpc.surfaceView.grabberHandlePadding = 0 - //fpc.surfaceView.contentInsets = .zero controller = fpc self.onDismiss = onDismiss From 403f38b91407557087105c9deba0c75033ac289c Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Thu, 27 Apr 2023 22:20:11 +0600 Subject: [PATCH 9/9] update readme --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b3a12187..66cd273f 100755 --- a/README.md +++ b/README.md @@ -209,15 +209,15 @@ allprojects { project build.gradle ```groovy dependencies { - commonMainApi("dev.icerock.moko:widgets:0.2.1") - - commonMainApi("dev.icerock.moko:widgets-bottomsheet:0.2.1") // show bottom sheets - commonMainApi("dev.icerock.moko:widgets-collection:0.2.1") // collection widget - commonMainApi("dev.icerock.moko:widgets-datetime-picker:0.2.1") // show datepicker - commonMainApi("dev.icerock.moko:widgets-image-network:0.2.1") // images with load from url - commonMainApi("dev.icerock.moko:widgets-sms:0.2.1") // input with sms autofill - commonMainApi("dev.icerock.moko:widgets-media:0.2.1") // moko-media integration - commonMainApi("dev.icerock.moko:widgets-permissions:0.2.1") // moko-permissions integration + commonMainApi("dev.icerock.moko:widgets:0.2.2") + + commonMainApi("dev.icerock.moko:widgets-bottomsheet:0.2.2") // show bottom sheets + commonMainApi("dev.icerock.moko:widgets-collection:0.2.2") // collection widget + commonMainApi("dev.icerock.moko:widgets-datetime-picker:0.2.2") // show datepicker + commonMainApi("dev.icerock.moko:widgets-image-network:0.2.2") // images with load from url + commonMainApi("dev.icerock.moko:widgets-sms:0.2.2") // input with sms autofill + commonMainApi("dev.icerock.moko:widgets-media:0.2.2") // moko-media integration + commonMainApi("dev.icerock.moko:widgets-permissions:0.2.2") // moko-permissions integration } ``` @@ -230,7 +230,7 @@ buildscript { } dependencies { - classpath "dev.icerock.moko.widgets:gradle-plugin:0.2.1" + classpath "dev.icerock.moko.widgets:gradle-plugin:0.2.2" } } ```