From f6ab6b347ae7387da77ef780f66b1e9baae51702 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 2 Jul 2020 01:03:58 +0100 Subject: [PATCH 1/4] Add AppearanceActionModifier, onAppear/onDisappear --- .../Modifiers/AppearanceActionModifier.swift | 45 +++++++++++++++++++ .../TokamakCore/Modifiers/ViewModifier.swift | 4 +- .../MountedViews/MountedCompositeView.swift | 8 ++++ .../Modifiers/_ViewModifier_Content.swift | 8 ++-- Sources/TokamakDemo/Counter.swift | 2 + 5 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift diff --git a/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift b/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift new file mode 100644 index 000000000..f3823d0d5 --- /dev/null +++ b/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift @@ -0,0 +1,45 @@ +// Copyright 2020 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +protocol AppearanceActionProtocol { + var appear: (() -> ())? { get } + var disappear: (() -> ())? { get } +} + +struct _AppearanceActionModifier: ViewModifier { + var appear: (() -> ())? + var disappear: (() -> ())? + init(appear: (() -> ())? = nil, disappear: (() -> ())? = nil) { + self.appear = appear + self.disappear = disappear + } + + typealias Body = Never +} + +extension ModifiedContent: AppearanceActionProtocol + where Content: View, Modifier == _AppearanceActionModifier { + var appear: (() -> ())? { modifier.appear } + var disappear: (() -> ())? { modifier.disappear } +} + +extension View { + public func onAppear(perform action: (() -> Swift.Void)? = nil) -> some View { + modifier(_AppearanceActionModifier(appear: action, disappear: nil)) + } + + public func onDisappear(perform action: (() -> Swift.Void)? = nil) -> some View { + modifier(_AppearanceActionModifier(appear: nil, disappear: action)) + } +} diff --git a/Sources/TokamakCore/Modifiers/ViewModifier.swift b/Sources/TokamakCore/Modifiers/ViewModifier.swift index 68cf2c83b..422b07405 100644 --- a/Sources/TokamakCore/Modifiers/ViewModifier.swift +++ b/Sources/TokamakCore/Modifiers/ViewModifier.swift @@ -28,8 +28,8 @@ public struct _ViewModifier_Content: View where Modifier: ViewModifier } public extension View { - func modifier(_ modifier: Modifier) -> Modifier.Body where Modifier: ViewModifier { - modifier.body(content: .init(modifier: modifier, view: AnyView(self))) + func modifier(_ modifier: Modifier) -> ModifiedContent { + .init(content: self, modifier: modifier) } } diff --git a/Sources/TokamakCore/MountedViews/MountedCompositeView.swift b/Sources/TokamakCore/MountedViews/MountedCompositeView.swift index c3ead1f73..08c88085d 100644 --- a/Sources/TokamakCore/MountedViews/MountedCompositeView.swift +++ b/Sources/TokamakCore/MountedViews/MountedCompositeView.swift @@ -44,6 +44,10 @@ final class MountedCompositeView: MountedView, Hashable { override func mount(with reconciler: StackReconciler) { let childBody = reconciler.render(compositeView: self) + if let appearanceAction = view.view as? AppearanceActionProtocol { + appearanceAction.appear?() + } + let child: MountedView = childBody.makeMountedView(parentTarget, environmentValues) mountedChildren = [child] @@ -52,6 +56,10 @@ final class MountedCompositeView: MountedView, Hashable { override func unmount(with reconciler: StackReconciler) { mountedChildren.forEach { $0.unmount(with: reconciler) } + + if let appearanceAction = view.view as? AppearanceActionProtocol { + appearanceAction.disappear?() + } } override func update(with reconciler: StackReconciler) { diff --git a/Sources/TokamakDOM/Modifiers/_ViewModifier_Content.swift b/Sources/TokamakDOM/Modifiers/_ViewModifier_Content.swift index 34f8f160e..9dc6a986f 100644 --- a/Sources/TokamakDOM/Modifiers/_ViewModifier_Content.swift +++ b/Sources/TokamakDOM/Modifiers/_ViewModifier_Content.swift @@ -15,17 +15,15 @@ import Runtime import TokamakCore -public typealias _ViewModifier_Content = TokamakCore._ViewModifier_Content - -extension _ViewModifier_Content: ViewDeferredToRenderer { +extension ModifiedContent: ViewDeferredToRenderer where Content: View { public var deferredBody: AnyView { if let domModifier = modifier as? DOMViewModifier { return AnyView(HTML("div", domModifier.attributes) { - view + content }) } else { return AnyView(HTML("div") { - view + content }) } } diff --git a/Sources/TokamakDemo/Counter.swift b/Sources/TokamakDemo/Counter.swift index 519279974..66a023d4b 100644 --- a/Sources/TokamakDemo/Counter.swift +++ b/Sources/TokamakDemo/Counter.swift @@ -29,6 +29,8 @@ public struct Counter: View { Button("Increment") { count += 1 } Text("\(count)") } + .onAppear { print("Counter.VStack onAppear") } + .onDisappear { print("Counter.VStack onDisappear") } ) : AnyView( VStack { Text("Limit exceeded") } ) From 152219aff7f7589abffc878e17ca4e81318c7405 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 2 Jul 2020 01:07:34 +0100 Subject: [PATCH 2/4] Refine doc comment --- Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift b/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift index f3823d0d5..1910fbc5b 100644 --- a/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift +++ b/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift @@ -17,6 +17,7 @@ protocol AppearanceActionProtocol { var disappear: (() -> ())? { get } } +/// Underscore is present in the name for SwiftUI compatibility. struct _AppearanceActionModifier: ViewModifier { var appear: (() -> ())? var disappear: (() -> ())? From eb2c70b6c664cd05e0dde3843754af7ffa1cec46 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 2 Jul 2020 14:43:24 +0100 Subject: [PATCH 3/4] Use default arguments in AppearanceActionModifier --- Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift b/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift index 1910fbc5b..9cc48b56e 100644 --- a/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift +++ b/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift @@ -37,10 +37,10 @@ extension ModifiedContent: AppearanceActionProtocol extension View { public func onAppear(perform action: (() -> Swift.Void)? = nil) -> some View { - modifier(_AppearanceActionModifier(appear: action, disappear: nil)) + modifier(_AppearanceActionModifier(appear: action)) } public func onDisappear(perform action: (() -> Swift.Void)? = nil) -> some View { - modifier(_AppearanceActionModifier(appear: nil, disappear: action)) + modifier(_AppearanceActionModifier(disappear: action)) } } From bd3cceca254470c3fbf6e5cc575bfb8c312eba72 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 2 Jul 2020 16:08:11 +0100 Subject: [PATCH 4/4] Make return types consistent --- Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift b/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift index 9cc48b56e..a10399c22 100644 --- a/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift +++ b/Sources/TokamakCore/Modifiers/AppearanceActionModifier.swift @@ -36,11 +36,11 @@ extension ModifiedContent: AppearanceActionProtocol } extension View { - public func onAppear(perform action: (() -> Swift.Void)? = nil) -> some View { + public func onAppear(perform action: (() -> ())? = nil) -> some View { modifier(_AppearanceActionModifier(appear: action)) } - public func onDisappear(perform action: (() -> Swift.Void)? = nil) -> some View { + public func onDisappear(perform action: (() -> ())? = nil) -> some View { modifier(_AppearanceActionModifier(disappear: action)) } }