问题
I have an iOS app running right now in a storyboard with 3 viewcontrollers. The first one (initial view) features a play button to start a music stream and image for album cover of currently playing song. This scene has a a navigation controller and a bar button on it that will lead the user to the next view...
A list view populated with hard coded stream's that the user can choose from. Very simple and working fine still.
After choosing one, the user goes to a preview page that tells them about the stream before it begins to play. Still working like a charm until they want to continue from here.
If they user selects the stream from the preview page, the app "should" return the user to the initial ViewController and swap out the playing stream for the one selected. At first, I was mistakenly creating a new instance of the initial viewController, and after fixing that mistake, now have a few more questions someone might be able to help me with.
here is the IBAction for the button to select the stream:
- (IBAction)returnHome:(id)sender
{
[[self navigationController] popToRootViewControllerAnimated:YES]; // goes back to first view on the stack
}
Before finding this logic, I was using the prepareForSegue and setting the stream value of the destination to be what was selected. I was also trying to save the state of the first view but was unsure how to pass it down the line (or retain it) since I am moving through 3 ViewControllers and using a modal segue so they can go back if they choose to not pick a new stream.
Any advice will help, but please refrain from simply posting a link to the references. I have been there for 3 days straight and they do not speak a very beginner friendly lingo in the iOS reference docs.
回答1:
You want to be able to inform Your first VC that user changed track? You can simply use NSNotificationCenter.
First You have to "tune in", i.e. in viewDidLoad, Your view controller to listen to particular notifications:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeStream:) name:@"UserWantToChangeStream" object:nil];
}
and implement method:
- (void)changeStream:(NSNotification *)notification
{
NSString *newStreamName = notification.object;
/* Change the stream code */
}
Don't forget to stop listening to the notifications:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Then You post notification after user has performed an action:
[[NSNotificationCenter defaultCenter] postNotificationName:@"UserWantToChangeStream" object:@"new_stream_name"];
In the example I pass NSString new_stream_name but You can pass any object.
Your first view controller will be informed.
回答2:
Have you considered the usage of unwinding segues? they are a little bit tricky to understand and use, but they surely can help you out.
First of all, you have to create an IBAction on your FIRST viewcontroller (== the view controller where you want to "land" and pass your list selection to) that takes a UIStoryboardSegue as single parameter and leave the implementation empty. For example
- (IBAction) returnToHome:(UIStoryboardSegue*) segue{;}
Then, in your preview page view controller (in storyboard), drag a segue from your button (the one which is triggerng the IBAction that pops the navigation controller) to the little exit symbol in the lower right side of the view controller. A menu should pop out asking for returnToHome method. Delete from the button the previous IBAction as well (the one you called -(IBAction)returnHome:(id)sender
)
In this way you should be able to do the same thing as before (popping back to the root view controller) without solving the problem BUT! if you implement an override of prepareForSegue:sender in your last view controller you'll have a reference to the root view controller where you can do whatever you need.
- (void) prepareForSegue:(UIStoryboardSegue*) segue sender:(id)sender
{
ViewController *vc = (ViewController *)segue.destinationViewController;
[vc.audioplayer pause];
vc.stream = self.stream;
}
This is a cleaner way to achieve the same result, since you're not making assumpions on your viewcontrollers hierarchy (what if tomorrow you'll add another view controller BEFORE the first one? The app will surely crash).
By the way, if you're into Storyboard/Segue business, check out my library which really simplifies storyboard work when it comes to "passing parameters": https://github.com/stefanomondino/SMQuickSegue (you can install it via cocoapods with pod 'SMQuickSegue')
回答3:
I have figured out at least how to get it to work. I had a second navigation controller in the storyboard and after deleting that it successfully went back 2 views instead of just one.
Here is my logic for retaining the stream data and bringing it back to the initial view controller if anyone is curious.
- (IBAction)returnHome:(id)sender
{
ViewController *vc = (ViewController *)[self.navigationController.viewControllers objectAtIndex:0];
[vc.audioplayer pause];
vc.stream = self.stream;
[self.navigationController popToRootViewControllerAnimated:YES];
}
since I now only have one Navigation Controller, the index for the initial will be 0. I can grab the initial view and put a reference to it in vc.
Then I simply access the audioplayer from it, and if it is still playing, stop it and load up the scene.
Then in the initial ViewController logic, I have the viewDidAppear method to load the new stream selection into the display labels and images.
- (void)viewDidAppear:(BOOL)animated
{
// If there is no stream selected (first run)
// set the stream to RAPstation default
if (self.stream == nil)
{
self.stream = [[Stream alloc]initWithName:@"RAPstation" ...];
}
self.snameLabel.text = self.stream.sName;
self.sdescLabel.text = self.stream.sDesc;
}
Not sure if this is the right but it definitely works for now. If anyone can help make it cleaner please feel free.
来源:https://stackoverflow.com/questions/22514270/saving-or-returning-to-previous-ios-scenes