chriseidhof / laufpark-stechlin

App for Laufpark Stechlin
http://www.laufpark-stechlin.de/
33 stars 4 forks source link

inconsistencies when state changes during animation #3

Closed pteasima closed 6 years ago

pteasima commented 7 years ago

Hi Chris, thanks for the all the awesome work. I've been trying to do similar things for a long time and have been having a lot of trouble with animations. I've used ReactiveSwift with MVVM in production a lot and I've been playing around with the Elm architecture (best / most promising VDOM I've seen so far is https://github.com/inamiy/VTree). I appreciate what you are doing with Incremental, although I dont yet see much of a difference from FRP frameworks (except for the simplicity and lack of glitches, which are nice). Unfortunately, all of these approaches seem to break down when animation is involved, and I havent found a way around it. For example, the following screenshot shows how I broke your StackView playground (I'm sure there's ways to fix this but I'm also sure there's other ways to break it, this is just the first thing that I was able to do in 5 minutes):

screen shot 2017-10-11 at 00 56 27

I think the problem above (2 and 3 end up in the wrong order) may be related to the fact that you are first hiding the arrangedSubview and only calling removeArrangedSubview in the completion handler of the animation. It may be something else, but the big picture is: If state changes while a transition/animation is in progress, bad things can happen. By using any reactive/incremental framework or purely functional approach (unidirectional + VDOM/IncrementalDOM), we essentially give up control of time (any event can come in at any time and the view layer needs to be ready for it). Your approach seems to have improved performance over a VDOM solution, but it doesnt seem to address this problem in any way. The most scary thing for me are ViewController transitions.

Have you thought about this? I've been trying to solve this for a long time and feel like I need input from someone smarter, so Im glad you're exploring this space ;) Some of my thoughts:

Sorry for the wall of text. Since I couldnt attend #funswiftconf, this seems like a good place to start a conversation. But feel free to take this as a bug report on the StackView stuff and ignore the rest :)

chriseidhof commented 7 years ago

Wow, thanks for such a long comment! I'll need some time to digest and think about it, but here's my thinking:

pteasima commented 7 years ago

yeah the comment’s way too long cause Ive thought about this a lot and had noone to whine to :D Anyway, thanks for the reply. I do agree that it should be much easier with Incremental than a VDOM. I thought some more about how one would even solve this in MVC. I think a good model example is:

An external event comes in that indicates that 2 more controllers should be presented compared to the current state (e.g. a modal and another modal over that).

The usual MVC solution is: Present the first, in completion handler of the first presentation, reprocess the current state to see if we still need to present the second. Are there other ways to do this? E.g. setup the flow upfront in a way thats interruptible if the state changes again?

I think that this is doable with incremental, but wonder how much of it we can hide from the programmer and how much needs to be run through the store and kept in the model.

Anyway, I will stop talking now and hopefully start doing later this week. Just wanted to put my thoughts down and have them reviewed. Also, cant wait for the animation library by my favorite trio in iOS programming :D

chriseidhof commented 7 years ago

I think the first step is to understand the underlying problem. What are some simple cases that break? Not just with stack views, but animations in general. Understanding that will definitely help!

On Wed 11. Oct 2017 at 10:57, Petr Sima notifications@github.com wrote:

yeah the comment’s way too long cause Ive thought about this a lot and had noone to whine to :D Anyway, thanks for the reply. I do agree that it should be much easier with Incremental than a VDOM. I thought some more about how one would even solve this in MVC. I think a good model example is:

An external event comes in that indicates that 2 more controllers should be presented compared to the current state (e.g. a modal and another modal over that).

The usual MVC solution is: Present the first, in completion handler of the first presentation, reprocess the current state to see if we still need to present the second. Are there other ways to do this? E.g. setup the flow upfront in a way thats interruptible if the state changes again?

I think that this is doable with incremental, but wonder how much of it we can hide from the programmer and how much needs to be run through the store and kept in the model.

Anyway, I will stop talking now and hopefully start doing later this week. Just wanted to put my thoughts down and have them reviewed. Also, cant wait for the animation library by my favorite trio in iOS programming :D

— You are receiving this because you commented.

Reply to this email directly, view it on GitHub https://github.com/chriseidhof/laufpark-stechlin/issues/3#issuecomment-335743355, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAVBheWCWtiHpj_AtpOPVkPcPdFc49Bks5srILtgaJpZM4P0wEG .

-- Sent from my phone

chriseidhof commented 7 years ago

Also, I think I solved the problem with stack views. I’m traveling so don’t have an internet connection on my laptop.

On Wed 11. Oct 2017 at 11:16, Chris Eidhof chris@eidhof.nl wrote:

I think the first step is to understand the underlying problem. What are some simple cases that break? Not just with stack views, but animations in general. Understanding that will definitely help!

On Wed 11. Oct 2017 at 10:57, Petr Sima notifications@github.com wrote:

yeah the comment’s way too long cause Ive thought about this a lot and had noone to whine to :D Anyway, thanks for the reply. I do agree that it should be much easier with Incremental than a VDOM. I thought some more about how one would even solve this in MVC. I think a good model example is:

An external event comes in that indicates that 2 more controllers should be presented compared to the current state (e.g. a modal and another modal over that).

The usual MVC solution is: Present the first, in completion handler of the first presentation, reprocess the current state to see if we still need to present the second. Are there other ways to do this? E.g. setup the flow upfront in a way thats interruptible if the state changes again?

I think that this is doable with incremental, but wonder how much of it we can hide from the programmer and how much needs to be run through the store and kept in the model.

Anyway, I will stop talking now and hopefully start doing later this week. Just wanted to put my thoughts down and have them reviewed. Also, cant wait for the animation library by my favorite trio in iOS programming :D

— You are receiving this because you commented.

Reply to this email directly, view it on GitHub https://github.com/chriseidhof/laufpark-stechlin/issues/3#issuecomment-335743355, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAVBheWCWtiHpj_AtpOPVkPcPdFc49Bks5srILtgaJpZM4P0wEG .

-- Sent from my phone

-- Sent from my phone

pteasima commented 7 years ago

Nice, I still have another wall of text in the works, so I will post it anyway and please disregard it (I will inspect your solution when its available).

As I said I mainly thought about VC transitions, the example above can be represented as

typealias Model = (_ firstScreenPresented: Bool, _secondScreenPresented: Bool). ( (false, true) is an inacessible state, just want to keep the model simple). The cases that break with a naive approach are:

Please put this on hold until I can come up with an example playground.

As for other animations, I havent thought about them in the context of incremental (only VDOM). In general I think that anything that relies on animation completion handlers (like your stackview) has potential for breaking. In your stackview example we end up with correct number of views but indices to get messed up. This could probably be solved by marking the views as willBeRemoved and shifting the indices accordingly. However, this seems like an ad-hoc solution for stackview and I wonder if theres a silver bullet we can use everywhere (probably not).

chriseidhof commented 7 years ago

Commit a7436dbbed68fcb6f7b868967f4d45cf744d59b8 solves the problem with stackview animations.

I think one-off animations that are not interactive are fine, you can basically fire and forget. Things become a little bit more complicated with completion handlers that change the view hierarchy, but I think you can cancel them. I'm not sure if I'm being to optimistic, but I don't think these will pose a real problem.

What I really want to do right is interactive animations. For example, when transitioning between two states in an animated way, you should be able to grab a view that's animating, and it should stop the automatic animation and instead turn into an interactive animation that you control with a gesture. I'm not completely sure yet how to best model this...

pteasima commented 7 years ago

I finally got around to building that playground. I wasnt able to break it with simultaneous transitions (think they must have improved UIKit recently). But I could quickly break it in another way - trying to dismiss a VC while its being presented.

Its in this file in my fork https://github.com/pteasima/laufpark-stechlin/tree/ps/modals/iOSPlayground.playground/Pages/ViewControllerPresentations.xcplaygroundpage , let me know if I should move it to your repo (pull request or otherwise). It might be a problem with my code, but right now I cant think of a way to fix it. Any suggestions?