I'm creating an app that plays music and has a nice UI for the album art and progress bar. The problem is when the user leaves the player view and goes back, the player UI has to be reloaded and you see a flash while the progress bar is resized and the album art is loaded.
Is there any way to keep items in memory when you leave a controller? Similar to how you go between playlists and player view on the iPhone music app, there is no lag or delay in seeing the artwork or song progress, it's there from the beginning.
Or am I thinking about this the wrong way?
I am assuming you are going back and forth using a UINavigationController, if so the default behavior is that the view controller will be released when you go back, as the only strong reference to it is in the UINavigationController stack, when it is popped the reference is lost, and therefore your controller is deallocated.
If you want to avoid this, all you need to do is have any other object have a strong reference to your view controller. An easy way to do it would be whenever you initialize your controller, in that class have a strong property that holds a reference to the view controller you do not want to lose.
Hope that helps.
Related
I'm developing an app in swift language to reproduce some live streaming or video on demand and I have 3 view controllers, the first is for live streaming, the second is a collection view with video on demand tuhmbnail and selecting one of this, it opens the third view controller for play the video, but when I come back from the third view controller, If I select a new video, the debugger says "received memory warning" and the app crashes, but before, I dismiss previous view controller and set to nil value the AVPlayer object and the AVPlayerLayer and I don't understand why this happen.
May depend on the buffer AVPlayerItem? is possible to set it? or when I dismiss I view controller I don't destroy his instance?
I'd need more information to be sure, but you likely have a retain cycle where you have a strong reference to self inside of a block.
Apple explicitly states that you'll need to use weak self to avoid a retain cycle with addPeriodicTimeObserver.
Hopefully that helps. If not, could you edit and add code?
I am approaching mental trying to solve this issue:
The situation:
I have a pretty basic application, an an MMDrawerController with a tableview inside, which links to a uitabbarcontroller when an item is pressed. Inside the first page of that is an embedded youtube UIWebView which when pressed played a youtube video fullscreen.
Here is what is happening.
Press video, opens in fullscreen and starts playing
UITabBar viewWillDisappear fires
Root MMDrawerController viewWillAppear fires
Video Ends
User back to UITabBar they started and everything seems fine even though it 'disappeared' earlier
Now I put booleans in the ViewWillAppears / Disappears of the two views to check what the current state of the app is like. Usually it's 0,1 indicating either the table is open or the uitabbarcontroller is open. After the video, they show 0,0. If I press back on the navigation I get "Unbalanced calls to begin/end appearance transitions" when navigating from wherever I am.
Right now If I listen for the start of the youtube video and then fire:
[self.navigationController popToRootViewControllerAnimated:NO];
I can prevent the unbalanced calls, from occurring and the user can continue to navigate the app. However they don't get to watch the video and they just get thrown back a view.
As well in 3. I can check for 0, 0 on the two controllers and then pretty much reboot the whole app. But thats not a good solution.
Intended outcome:
The user can press the uiwebview, watch the video, and afterwards they are returned to where they left off. If they hit back on the navigation controller, no unbalanced appearance transitions.
I've tried a bunch of stuff, like
[self.navigationController poptoViewController:...]
From what I gather at this point, it has something to do with fullscreen videos loading in the root controller (hence its viewWillAppear firing at 3.) but I'm not getting 'placed' back correctly afterwards. Something like
[self.navigationController heyTheUserIsPresentlyIn:self]
That I can call after the video goes away would be crazy good.
Any assistance is much appreciated, although I've been on this for hours and hours, If I'm a moron any links to docs or sections in books would help a lot. Thanks.
It sounds like ViewController life-cycle issue.
Are you sure you want to navigate from a UITableView to a UITabBarController? Try remove the latter because it's normally the root ViewController and calls to viewWillDisappear and viewWillAppear could be made based on this assumption.
If you need a tabbed like control which is not the root ViewController maybe consider rolling your own?
Well this happens when a sequence of navigation animation or animation started without properly ending first animation, so i can guess the same is with your animation, take care of your animation even if it is not due to Navigation controller.
Hope it helps you
I have created my own MoviePlayerViewController derived from MPMoviePlayerViewController, I present it using the standard presentMoviePlayerViewController method - all works fine. On top of the movie player controller, I present another ShareThisMovie view controller when the user clicks some button (after I pause the movie). I do this using presentViewController.
I don't know why but as soon as the new VC comes up, the underlying MoviePlayerViewController immediately fires MPMoviePlayerPlaybackDidFinishNotification, which I respond to by dismissing the entire VC hierarchy, so the user does not have a chance to interact with the ShareThisMovie controller.
Even if I ignore the notification, still the fact that the movie player fires it means that once the ShareThisMovie controller closes, the movie STARTS OVER FROM THE BEGINNING. This is clearly not what I want...
Why is the MoviePlayerController firing this event? How do I ensure it doesn't, or how to I workaround this? I tried storing the last playback location before showing the ShareThisVideo controller, and setting it back afterwards, but it still plays from the beginning...
tnx
This is happening because when you moving from MoviePlayerViewController to another view controller the MPMoviePlayer stops it playing...so your MPMoviePlayerPlaybackDidFinishNotification Delegate is calling.
you can not continue playing MPMoviePlayer when you are on another ViewController.
one solution is insted of presenting ShareThisMovieviewcontroller, you can simply add UIView for sharing on top of MPMoviePlayer in same controller. this will prevents the MPMoviePlayer to stop.
Not exactly an answer rather than a workaround, but I ended up changing the design: instead of deriving from MPMoviePlayerViewController, I derived from the standard UIViewController, and embedded an MPMoviePlayerController inside it. For some reason, when doing this, the player did not raise a didFinish event when I presented some other VC on top of my player view controller...
Guess my conclusion for now is not to derive a VC from MPMoviePlayerViewController...
Very simple use case: Let's say an iOS app displays a MovieListController view (inside of a UINavigationController) with a list of movies. When the user touches on one, the app pushes a MovieDetailController onto the navigation stack (i.e. [[MovieDetailController alloc] initWithMovieId:(NSString *). In the MovieDetailController's viewDidAppear: method, it makes an HTTP call to retrieve details based on the movie ID passed into it.
The challenge is that the MovieDetailController gets pushed onto the navigation stack right away, and for a second or two while the details haven't been retrieved, the view shows a bunch of blank fields, which is undesirable.
To get around this, I'm thinking of having the MovieListController not push the MovieDetailController onto the stack right away. Instead, it would put up a progress indicator (I'm using SVProgressHUD), then call MovieDetailController's initWithMovieId: method which would kick off the HTTP call. Then when the data is received, the MovieDetailController would make a callback back to MovieListController to remove the progress indicator and then push the MovieDetailController onto the navigation stack.
Is there a better pattern for this type of scenario? Should I be considering having the MovieDetailController push itself onto the navigation stack when it's ready?
Note: I have considered loading the detail view and putting up an activity indicator, but you'll still be able to see an 'empty view' behind it which looks a bit weird. I have also considered just having the MovieListController retrieve the details itself but this seems to break the encapsulation model - the MovieListController should just be concerned about listing movies, not about their details.
Any thoughts? This Movie stuff is just an example - looking for a general pattern here.
Personally I would take the following approach.
User selects the movie they want details for
Push to the detail view and rather than showing your skeleton view with empty fields, overlay a loading view, you could continue using your progress HUD on top of this for any animations you gain with that.
Once the results come down, remove your HUD and the loading overlay view that is hiding all the data/fields
The reason I would go this route rather than showing the HUD before pushing the view controller is that you can give the user the opportunity to cancel their selection. I am not familiar with SVProgressHUD but hopefully when a HUD is displayed you can enable touches, specifically the user touching Back on your UINavigationController in the event they accidentally selected the movie or the request is just taking longer than they are willing to wait for.
It also separates the logic form your list view, your detail view is standalone and could be initialized anywhere in your app (maybe you want to cross link similar movies within a movie detail view) and you do not need to rewrite the logic of the presenting view waiting on the results to come back.
In this situation, I personally would return to the model-view-controller pattern.
There are several system apps that display a detail view from a list of objects, e.g. Calendar, Contacts, etc. Presumably, as with EKEvent and ABPerson in their respective apps, the main view controller maintains a list of the model objects. When the user selects one of the items, the main view controller passes the selected model object to the detail view controller. The detail view controller itself doesn't have to do any data loading. So, like #ChrisWagner said, we want to separate the logic from the view controller.
Method
Similarly, you might want to use a MovieList class that stores an array of Movie objects. Each Movie stores the values for all the fields in the detail view controller - essentially, all the information the app needs about the movie. For example, you might have a property NSString *movieTitle, or NSDate *premiereDate. The movieTitle would be set by the MovieList at initialization because it's just metadata; on the other hand, the premiereDate might be nil if the data hasn't loaded, so you would have a property BOOL isLoaded to check for this condition.
You could then proceed in one of two ways:
1) Say the main view controller wants to push a detail view controller for a movie. Then the main view controller would dig the appropriate Movie out of the MovieList and check if it's loaded. If not, it would call something like -(void)loadContents on the Movie. When the model object is finished loading, it will post a notification that it's finished loading. At this point the main view controller will dismiss its progress view and push the detail view. If you use (1), it's not as important to use a MovieList coordinator.
2) If you want to be more aggressive about loading the movie information, you could implement a method on MovieList that calls loadContents on its Movies in the background. Then there's a higher chance that a movie will already be loaded when the user selects it.
Edit: Note that if you decide to use a MovieList type object, only the main view controller should be allowed to access this list. In a way, the model structure I've described parallels the view controller structure. The detail view controller doesn't know about the list view controller, so it shouldn't know about the MovieList either.
Benefit
The benefit of using this Movie and MovieList structure is that the data model is completely separated from the view controllers. That way, the user is free to cancel, select, present and dismiss view controllers while the data is loading. (Imagine if the user pressed Back to dismiss the progress HUD, canceling any HTTP data it would have gathered. Then, if he decides to come back to that same movie, the new detail view controller has to start loading afresh.) Also, if you decide to replace view controller classes in the development process, you won't have to copy and paste a bunch of data-handling code.
I think you'll find that structuring your app in this way not only gives you and the user more freedom, it also makes your app efficient and open to later extensions. (Sorry to post so late, but I wanted to include MVC in this pattern-related discussion!)
I think a more advanced route would be to start the detail requests as the cells are being being shown on in the tableview/collection view. Then as the cells move offscreen, cancel the requests. You might be loading movie details unnecessarily, but I don't think this is a big deal, because you'd only be filling your db with movie details you might not need and coredata can handle that. This is assuming that your api requests are happening on a bg thread and they don't affect the main(UI) thread.
I've got a view Controller which manages my TV Playout (HDMI Apple AV Adapter). Everything works fine. I call my view Controller using a popover on the iPad.
When I open the popover, the external screen is recognized and I can work with it. But when I close the popover View (which means I send my TV Playout View Controller to the background) the TV screen (logically) turns black.
Is there a possibility to tell my view Controller to hold the picture on the TV screen by quit?
Like "pseudocode"
[TVOutViewController stayActiveInBackground]; //pseudocode
Thanks in advance!
I suspect you've coded your view controller so it explicitly shuts down the external UIScreen/UIWindow pair when it becomes inactive. I suggest you move the external screen code out of your popup's view controller and into an object which has a lifetime independent of what's going on on the internal screen, e.g. your application delegate or an object referenced by it. Then just send that object messages from your popup view controller in response to user events.
There's nothing about the Apple APIs that causes this kind of behaviour - it's purely a consequence of how you've designed your app. As such I don't recommend trying to force the view controller to stick around. Instead, try to find a better structure for the app. A view controller should only be responsible for its view, not application state.
OK guys, By chance, I found a solution (or at least a workaround). Be sure to manage the problem using a UISplitViewController with your TVOutViewController as the masterViewController and the ContentViewController as the detailViewController. Apple already did the work. Thanks anyway! :)