Comments (8)
@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.
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.
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.
@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.
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.
Somewhat related. Nice example of table view based animations with ReSwift, using a diffing library: https://github.com/koheku/ReSwift-Todo
from reswift.
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.
Seems like the original question has been answered!
from reswift.
Related Issues (20)
- Concurrency - protected reads HOT 2
- How to subscribe/unsubscribe store in a UIView like in UITableViewCell, CollectionViewCell etc ? HOT 1
- What's a good way to cast results of a subscription in SwiftUI to a desired output type? HOT 3
- Managing ReSwift app state inside FileDocument HOT 1
- Remove Travis from all ReSwift repos? / How to set up GH Actions
- ReSwift 7 Roadmap HOT 20
- Docs: Update outdated and vulnerable jQuery version HOT 3
- Array as a State HOT 1
- Clarification on Single Store object for an application. HOT 1
- Change visibility of Store from 'open' to 'public final'? HOT 2
- SIGSEGV on state.didSet HOT 4
- Using Inject with ReSwift HOT 1
- Testing: Waiting for store.dispatch() to complete HOT 2
- Huge CPU gap between ReSwift Store vs just SwiftUI ObservableObject HOT 1
- Crash: sometimes will crash at this point, what can i do to find the root cause HOT 11
- Crash when dispatching actions from inside StoreSubscriber.newState() HOT 2
- How to use a state on recursive screens? HOT 4
- Filter on updates not working HOT 3
- When do you upgrade ReSwiftRouter ? Now,ReSwiftRouter just depends ReSwift 5.0.0, but lasted ReSwift is 6.1.1 HOT 2
- Privacy Manifest HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from reswift.