Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling modals #41

Closed
Nek opened this issue May 10, 2020 · 7 comments
Closed

Handling modals #41

Nek opened this issue May 10, 2020 · 7 comments

Comments

@Nek
Copy link

Nek commented May 10, 2020

Hello,

I'm trying to build an app with a modal window using SwiftUI .sheet functionality. It uses bindings and I can't wrap my head over the correct way of integrating it into SwiftDux project. Right now I update the binding right inside the map function which is obviously not the right way to go.

Any hints?

@Nek
Copy link
Author

Nek commented May 10, 2020

Figured it out.
I've just put this in the View function:

let showModal = Binding<Bool>(
            get: { return props.showSettingsFor != nil },
            set: {val in ()}
        )

@Nek Nek closed this as completed May 10, 2020
@Nek
Copy link
Author

Nek commented May 10, 2020

Update:
Now I know why one needs binder. :)

@StevenLambion
Copy link
Owner

Hi, I saw that you closed this, but I wanted to mention the built-in API called ActionBinder that can help. This construct creates bindings between a state value and a dispatchable action. You can also return nil to not dispatch an action.

There's an optional map function you can use that adds the ActionBinder:

struct MyView: ConnectableView
  struct Props: Equatable {
    @ActionBinding var showModel: Bool
  }

  func map(state: AppState, binder: ActionBinder) -> Props? {
     Props(
       showModel: binder.bind(state.showSettingsFor != nil) { _ in nil }
     )
  }

   func body(props: Props) -> some View {
      InnerView().sheet(isPresented: props.$showingDetail) {
        DetailView()
      }
    }
}

@Nek
Copy link
Author

Nek commented May 11, 2020

Thanks! That's why I've closed it actually. Another thing to mention. A View embedded in .sheet doesn't get store injected for some reason. I'm doing it manually at the moment but is it the right thing to do?
Example:

import SwiftUI
import SwiftDux

struct RootContainer : ConnectableView {
    var store: Store<AppState>
    
    struct Props: Equatable {
        @ActionBinding var showSettings: Bool
        var sequences: [SequenceDescription]
    }
    
    @MappedDispatch() private var dispatch
    
    func map(state: AppState, binder: ActionBinder) -> Props? {
        return Props(showSettings: binder.bind(state.showSettingsFor != nil, dispatch: { _ in
            EmptyAction()
        }), sequences: state.sequences.enumerated().map{$0.element.value}
        )
    }
    
    func body(props: Props) -> some View {
        return VStack(spacing: 0) {
            ForEach(props.sequences, id: \.self.id) { (seq: SequenceDescription) in HStack(spacing: 0) {
                DirectionContainer(sequencerId: seq.id)
                StepSequencerContainer(sequencerId: seq.id, midiChannel: seq.midiChannel)
                RandomizeContainer(sequencerId: seq.id)
                SettingsButtonContainer(sequencerId: seq.id)
                }
            }}.sheet(isPresented: props.$showSettings) {
                SettingsContainer().provideStore(self.store)
        }
    }
}

@StevenLambion
Copy link
Owner

StevenLambion commented May 11, 2020

I haven't worked with sheets much myself, but I do recall sheets having a problem with environment objects. An environment object is passed down the view hierarchy within a UIHostingController. Typically, a UIHostingController passes its environment information to its children and siblings, however, I suspect a sheet creates a new UIWindow. Because the sheet's UIHostingController isn't a direct child of the RootContainer's, the information isn't able to be passed down.

Maybe Apple will find a way to fix it come WWDC, but for now you might just have to pass the store to the sheet explicitly.

@Nek
Copy link
Author

Nek commented May 11, 2020

Yep. That's what I'm doing. I'm week new to Swift development. Good to know I'm on the right track. Thanks!

@StevenLambion
Copy link
Owner

I kind of found a way to pass environment objects across view hierarchies in my new navigation library. You need to provide a UIHostingController at the root the application, and then assign your environment objects to it. This will make the custom view host any child view controllers, keeping them under the same view hierarchy.

Here's a custom view where I've Handle this if you wanted to try it out:

https://github.com/StevenLambion/SwiftDuxNavigation/blob/master/Sources/AppNavigation/UI/RootNavigationView.swift

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants