Code Monkey home page Code Monkey logo

Comments (8)

maximkhatskevich avatar maximkhatskevich commented on May 29, 2024

@shovonnn I assume handling simple non-interactive animations is easy - you just have a temporary (transition) state while you are animating UI from one state to another, instead of switching immediately. Another bigger question is how to implement interactive transitions (for example, swipe from left edge of the screen to go "back" inside NavigationViewController), when user should be able to control transition/animation progress and can let it complete or cancel.

@Ben-G any ideas so far? Thanks!

from reswift.

Ben-G avatar Ben-G commented on May 29, 2024

@shovonnn @maximkhatskevich

On a conceptual level every animation, even a very interactive one, will have a limited set of discrete states that are semantically different.

In ReSwift we really only care about dispatching actions when semantically relevant changes happen, e.g. something that brings the app into a new state. What is part of that state depends on a lot your individual application.

Let's take a drag & drop animation within a grid as an example. The sub state to model drag and drop of an element might look like this:

struct DragAndDropState {
    var currentGridIndex: GridIndex
}

You would now implement any UI code just as you usually would. Once the user drops an element at a new index, you would fire an action to update currentGridIndex.

Assuming a more complicated example, in which you might want to change what the UI looks like when dragging, you would expand what is stored in the state accordingly:

struct DragAndDropState {
    var currentGridIndex: GridIndex
    var dragging: Bool
}

You now would fire additional actions for when drag & drop begins and ends.

These two examples show how you can implement user interactive animations that end in a state transition.

What about state transitions that cause interactive animations? My current thinking is that you would need to set aside an action to indicate when an animation triggered by a state change has completed.
Conceptually this should look somewhat like this:

State' -> AnimationStart -> State'' -> Animation Ends -> State'''

The start of the animation would be the reaction to a state change. Your view would dispatch an action when it actually starts the animation and another action when it ends the animation.

This should theoretically also allow you to throttle/buffer animations. Most animations in your views should not happen in parallel; You could implement some logic in the reducer that checks if any animations are currently going on. In the case of other ongoing animations the you could add the new animation action to a list of queued animations. You could then deliver these queued animations once the active animation ends.

Most of this is still theoretical - we will need to find the time to come up with some good examples! I hope this is some food for thought.

from reswift.

Ben-G avatar Ben-G commented on May 29, 2024

A somewhat related discussion came up in #22. FRP frameworks such as RxSwift should also provide us with some insights on how to best implement animations.

from reswift.

maximkhatskevich avatar maximkhatskevich commented on May 29, 2024

@Ben-G thanks for the response, my ideas are close to what you've described. Here is what I think.

Do you remember an example with TabBar-based UI in your talk at Realm (https://realm.io/news/benji-encz-unidirectional-data-flow-swift/)? You said we would need to implement TabBarDelegate and cancel immediate switch, dispatch an action instead that would generate new state and then make the switch when the new state will come to the this view/viewController. So it looks like UI should not make any changes directly at all, and I agree with that. This approach works well for non-interactive animation, just have a special state for this non-interactive transition/animation and make view/observer react accordingly, but it's not so clear with interactive animation/transition - it looks like we should not prevent that kind of animation\transition from happening right away and should dispatch an action with new state "post factum".

Here comes another question, what if that action would be declined/cancelled/ignored (will not be processed by any of reducers or declined/cancelled explicitly) and will not move the app into the new state, that will bring the app in an inconsistent state.

A solution here might be a special approach for that kind of actions, lets call them "mandatory actions". Since action does not know how exactly it should affect app state, there is no way to force new state propagation for that kind of actions, so it's developer's responsibility to make sure that mandatory actions are always processed. On framework level dispatcher can only check if app state has been changed after passing through all reducers or not, and if it was a mandatory action, but the state has not been changed - we can print a warning in console, or even throw an exception (NSAssert?).

What do you think?

from reswift.

agentk avatar agentk commented on May 29, 2024

With the TabBar you have two options as you identified.

  • Intercept tab item taps, dispatch them as route change actions. Tab bar changes view based on new state.
  • Let the default UITabBarController change the view, also dispatch a route change action, then when the tab bar updates there is now change required.

The latter makes interactions and transition animations easier to think about, and slightly harder to implement. The default UITabBarController is a pretty simple example because there is no animation when changing tabs, but say you subclassed it as a SlidingTabBarController that slid views horizontally when tapping tab items. You can let the sliding tab bar start the animation when the user taps, then dispatch the route change, then route change complete action.
When the sliding tab bar receives the state update, you would need to check what the destination state of the animation is. If the animation was user initiated and the routes are the same, it can be classed as representing the current state.

The advantage is you can have seperate animations / interactions for a user gesturing a slide between views, and initiating an animation from a state change. If replaying events or winding back, you can choose to cancel or speed up currently running animations too.

from reswift.

Ben-G avatar Ben-G commented on May 29, 2024

Somewhat related. Nice example of table view based animations with ReSwift, using a diffing library: https://github.com/koheku/ReSwift-Todo

from reswift.

russbishop avatar russbishop commented on May 29, 2024

I ran into a situation where I wanted to have a film-strip draggable view that updates as you scroll across it (using a UICollectionView underneath). I found that doing it the "pure" way was simply far too slow, as it resulted in dispatching actions on every drag update and it made my state -> view logic fairly complicated. I ended up cheating and deferring sending any actions until the scroll completed. I'm not 100% happy with the solution.

Conceptually you could imagine a UIView that supported this kind of interaction and only called it's target/action on completion. If you model it that way then the intermediate states become less relevant. Whether that's cheating or not may depend on your mood at the time :)

from reswift.

Ben-G avatar Ben-G commented on May 29, 2024

Seems like the original question has been answered!

from reswift.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.