I have two views :
View1 (Shop) : URL stocked in NSString for displaying image.
View2 (ModifyShop) : Text field with URL from view1.
I can pass data from view1 to view2 : The URL stocked in NSString appears in Text field.
Now I would like to modify this URL with Text field from view2 and that modify the NSString in view1. How can I make that ?
Here is my code :
Shop:
- (void)viewDidLoad {
[super viewDidLoad];
[self.modifyButton setHidden:YES];
dispatch_async(dispatch_get_global_queue(0,0), ^{
self.imageButtonURL = #"http://bundoransurfshop.com/wp-content/uploads/2015/02/72-torq-pink.jpg";
imageButtonData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:self.imageButtonURL]];
if ( imageButtonData == nil )
return;
dispatch_async(dispatch_get_main_queue(), ^{
self.imageButton.imageView.image = [UIImage imageWithData: imageButtonData];
});
});
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"modifyShop"]) {
ShopModify *viewcontroller = (ShopModify *)segue.destinationViewController;
viewcontroller.imageButtonURL = self.imageButtonURL; }
}
-(IBAction)prepareForUnwind:(UIStoryboardSegue *)segue {
NSLog(#"%#", self.imageButtonURL);}
ModifyShop:
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.photoURL.text = [NSString stringWithFormat:#"%#", self.imageButtonURL];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
Shop *viewcontroller = (Shop *)segue.destinationViewController;
viewcontroller.imageButtonURL = self.photoURL.text;
}
That makes my app crashes :
[Reports setImageButtonURL:]: unrecognized selector sent to instance
The error is saying that you are tying to set imageButtonURL on an instance of Reports, not on Shop which is what you think your destination view controller is. It appears that your unwind is going to the wrong controller. You must have hooked up the unwind segue incorrectly. You say that you have 2 views (view controllers actually), but you must also have a Reports class in your app.
Related
I am trying to pass a string value namely,'_passedDataDate' from a viewController to a TableView Controller. And there in the TableViewController,I am trying to keep it in a array namely,'_dateArray'. while trying to do so, at first a nil object gets inserted and then the real object. how to avoid this nil value getting added?
Here is the code bellow,
- (void)viewDidLoad {
[super viewDidLoad];
_dateArray = [[NSMutableArray alloc] init];
_dateString = _passedDataDate;
if(_dateString){
[_dateArray addObject:_passedDataDate];
NSLog(#"Added Passed Data");
NSLog(#"%ld",[_dateArray count]);
}
else{
NSLog(#"No object Added");
NSLog(#"%ld",[_dateArray count]);
}
}
Output is as follow:
No object Added
0
Added Passed Data
1
Why No object Added is getting printed? I do not want it what shall Ido about it?
Also,I get the below warning,
Warning: Attempt to present on whose view is not in the window hierarchy!
Don't use Segue inside the button action.
Hide the segue
[self performSegueWithIdentifier:#"pass" sender:nil];
And try this :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString: #"pass"])
{
TableViewController *tableView = [segue destinationViewController];
tableView.passedDataDate = #"HAI";tableView.passedDataDestination = #"Hellio";
}
}
I have passed button tag to another Viewcontroller.
It's passed but when I'm calling any another method like play audio play on that selected button, player not working...
I have tried below code :
It passes id of button clicked to the other view. Where I build player and and based on button id I want to play a poem on other view and control functions like volume control, progressbar, duration, play, push, next, etc...
While I'm giving a method to a particular button id to play poem it is not working...
This below code for play a poem in another view where I build player.
`
- (IBAction)btnpoemclicked:(id)sender {
btnPressed = [NSString stringWithFormat:#"%li", (long)[sender tag]];
NSLog(#"selected poem -%#",btnPressed);
PlayerController *pl=[[PlayerController alloc] init];
pl.btnPressed=btnPressed;
[pl setBtnPressed:pl.btnPressed];
[self performSegueWithIdentifier:#"player" sender:sender];
}
You should pass values in prepareForSegue when using Segues.
Update your code like
- (IBAction)btnPoemClicked:(id)sender {
btnPressed = [NSString stringWithFormat:#"%li", (long)[sender tag]];
[self performSegueWithIdentifier:#"player" sender:self];
}
And then In prepareForSegue pass the values to destinationViewController like this
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"player"]) {
PlayerController *playerController = (PlayerController *)segue.destinationViewController;
playerController.btnPressed = btnPressed;
}
}
Implement your button action like,
- (IBAction)buttonPoemClicked:(id)sender {
[self performSegueWithIdentifier:#"Player" sender:sender];
}
Pass your value by implementing prepareForSegue,
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"Player"]) {
PlayerController *playerController = (PlayerController *)segue.destinationViewController;
playerController.pressedButtonTag = [NSString stringWithFormat:#"%li", (long)[sender tag]];
}
}
Additional Note : Try to make your variable/class names little more understandable or try to follow apple recommended naming conventions.
I have a segue that is being called progmatically by performSegueWithIdentifier: but it will not trigger. However, when I create the segue with a button press the segue works without a problem. I have also tried changing the name of my segue in code, and it produces a no segue with identifier error.
Here is my code (Note: the segue is called in two different places to check if it would work somewhere else in the code.)
#import "SignUpViewController.h"
#import "ProgressHUD.h"
#interface SignUpViewController ()
#end
#implementation SignUpViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self performSegueWithIdentifier:#"profilePic" sender:self];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (IBAction)createAnAccount:(id)sender {
[ProgressHUD show:#"Please Wait"];
if ([self.passwrod.text isEqualToString:self.confirmPassword.text]){
// Register User
PFUser *user = [PFUser user];
user.username = self.username.text;
user.password = self.passwrod.text;
user.email = self.eMail.text;
// other fields can be set if you want to save more information
NSString *name = [self.firstName.text stringByAppendingString:#" "];
NSString *fullName = [name stringByAppendingString:self.lastName.text];
user[#"name"] = fullName;
user[#"posts"] = #0;
user[#"score"] = #5;
user[#"followers"] = #0;
user[#"following"] = #0;
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
// Hooray! Let them use the app now.
[self performSegueWithIdentifier:#"profilePic" sender:self];
[ProgressHUD showSuccess:nil];
NSLog(#"Perform Segue");
} else {
NSString *errorString = [error userInfo][#"error"];
// Show the errorString somewhere and let the user try again.
[ProgressHUD showError:errorString];
}
}];
} else {
// Alert User
[ProgressHUD showError:#"Please check your passwords as they do not match."];
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"Preparing for segue");
NSLog(#"Segue: %#", segue.identifier);
}
#end
Update for clarification: The prepareForSegue method is being called and logging.
Thanks for your help!
You shouldn't call performSegueWithIdentifier in viewDidLoad. In fact, I'm pretty sure you'll see a warning or error in the console if you do. Move the call to viewDidAppear instead.
The solution that worked for me was to delete all the segues to the view controller in question and then re-added them. This porbably won't work for everyone, but it is worth a shot.
I've built a custom UITabBarController with Storyboards/Segues and UIViewController containment. Here is a link to it: https://github.com/mhaddl/MHCustomTabBarController
The UIViewControllers which will be presented by the Container are stored in a NSMutableDictionary (keys are the segues' identifiers). Everything is working fine until the point is reached where I come back to a earlier presented ViewController. At this moment "dealloc" gets called on this ViewController before it is presented.
How can I prevent "dealloc" from getting called so it can be used to unsubscribe from Notifications, and nil delegates.
MHCustomTabBarController:
#implementation MHCustomTabBarController {
NSMutableDictionary *_viewControllersByIdentifier;
}
- (void)viewDidLoad {
[super viewDidLoad];
_viewControllersByIdentifier = [NSMutableDictionary dictionary];
}
-(void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.childViewControllers.count < 1) {
[self performSegueWithIdentifier:#"viewController1" sender:[self.buttons objectAtIndex:0]];
}
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
self.destinationViewController.view.frame = self.container.bounds;
}
#pragma mark - Segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (![segue isKindOfClass:[MHTabBarSegue class]]) {
[super prepareForSegue:segue sender:sender];
return;
}
self.oldViewController = self.destinationViewController;
//if view controller isn't already contained in the viewControllers-Dictionary
if (![_viewControllersByIdentifier objectForKey:segue.identifier]) {
[_viewControllersByIdentifier setObject:segue.destinationViewController forKey:segue.identifier];
}
for (UIButton *aButton in self.buttons) {
[aButton setSelected:NO];
}
UIButton *button = (UIButton *)sender;
[button setSelected:YES];
self.destinationIdentifier = segue.identifier;
self.destinationViewController = [_viewControllersByIdentifier objectForKey:self.destinationIdentifier];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([self.destinationIdentifier isEqual:identifier]) {
//Dont perform segue, if visible ViewController is already the destination ViewController
return NO;
}
return YES;
}
#pragma mark - Memory Warning
- (void)didReceiveMemoryWarning {
[[_viewControllersByIdentifier allKeys] enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
if (![self.destinationIdentifier isEqualToString:key]) {
[_viewControllersByIdentifier removeObjectForKey:key];
}
}];
}
#end
MHTabBarSegue:
#implementation MHTabBarSegue
- (void) perform {
MHCustomTabBarController *tabBarViewController = (MHCustomTabBarController *)self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) tabBarViewController.destinationViewController;
//remove old viewController
if (tabBarViewController.oldViewController) {
[tabBarViewController.oldViewController willMoveToParentViewController:nil];
[tabBarViewController.oldViewController.view removeFromSuperview];
[tabBarViewController.oldViewController removeFromParentViewController];
}
destinationViewController.view.frame = tabBarViewController.container.bounds;
[tabBarViewController addChildViewController:destinationViewController];
[tabBarViewController.container addSubview:destinationViewController.view];
[destinationViewController didMoveToParentViewController:tabBarViewController];
}
#end
"At this moment "dealloc" gets called on this ViewController before it is presented." -- no, not really. Dealloc is being called on a controller that never gets on screen, not the one you came from initially or are going back to. The way your segue is set up, and the fact that you keep a reference to your controllers in the dictionary, means that they never get deallocated. Segues (other than unwinds) ALWAYS instantiate new view controllers, so what's happening is that a new instance of, say VC1 is created when you click on the first tab (and a segue is triggered), but you never do anything with that controller (which would be self.destinationViewController in the custom segue class) so it's deallocated as soon as the perform method exits.
Depending on where you setup any delegates or notification observers, this might not be a problem -- this controller that's created, and then immediately deallocated never has its viewDidLoad method called, so if you do those things in viewDidLoad, they won't ever happen for this transient view controller.
If you don't want this to happen, then you need to make your transitions in code without using segues.
I am trying to make my first iOS app after doing a few tutorials. I am making a simple headline/news reader that pulls New Orleans Saints headlines and stories as json from an ESPN API, displays the headlines in a table view, and then displays the text of the story in a detail view when the user taps a headline.
The app compiles and runs without errors or warnings. The prepareForSegue method executes when the user taps a headline in the top tableview. I can step thru this code in the debugger. If I step thru the prepareForSegue method, it calls the detailViewController's setStory method, which in turn calls the configureview method. But then it takes me to main.m and the app closes, without showing the detail view.
I am a noob. What am I missing? Why don't I see the detail view? Maybe I need an IBAction to load the IBOutlet?
saintsMasterViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"]) {
saintsDetailViewController *detailViewController = [segue destinationViewController];
saintsNewsStory* displayThisStory =[self.dataController objectInListAtIndex:[self.tableView indexPathForSelectedRow].row];
[detailViewController setStory:displayThisStory];
}
}
saintsDetailViewController.m
- (void)setStory:(saintsNewsStory *)newStory
{
// if (_story != newStory) {
_story = newStory;
// Update the view.
// [self configureView];
// }
}
- (void)configureView
{
self.storyText.text = self.story.storyText;
}
- (void)viewDidLoad //the app never gets here. if i put a breakpoint here it never gets here
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
saintsDetailViewController.h
#class saintsNewsStory;
#interface saintsDetailViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextView *storyText;
#property (retain, nonatomic) saintsNewsStory *story;
#end
The console shows this error:
SaintsHeadlineReader[9608:11303] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key detailDescriptionLabel.'
* First throw call stack:
(0x1c90012 0x10cde7e 0x1d18fb1 0xb7a711 0xafbec8 0xafb9b7 0xb26428 0x2320cc 0x10e1663 0x1c8b45a 0x230bcf 0xf5e37 0xf6418 0xf6648 0xf6882 0x102235 0x3013d2 0xff4f3 0xff777 0xff7b7 0x46afe2 0x45cad9 0x45cb54 0xc4899 0xc4b3d 0xacbe83 0x1c4f376 0x1c4ee06 0x1c36a82 0x1c35f44 0x1c35e1b 0x1bea7e3 0x1bea668 0x1565c 0x204d 0x1f75 0x1)
libc++abi.dylib: terminate called throwing an exception
It seems like textview has a detail label and maybe I needed an outlet for that?
Change this line :
saintsDetailViewController *detailViewController = [segue destinationViewController];
To this:
saintsDetailViewController *detailViewController = (saintsDetailViewController *)[segue destinationViewController];
EDIT:
You should show your view first and then call the configureView method. Try to call configureView in viewDidLoad of that ViewController.
- (void)setStory:(saintsNewsStory *)newStory
{
// if (_story != newStory) {
_story = newStory;
// Update the view.
// [self configureView];
// }
}
-(void) viewDidLoad {
[self configureView];
}
- (void)configureView
{
self.storyText.text = self.story.storyText; //the app gets to here but does not show the view. why?
}
Test my theory. Put NSLog(#"%d", [self.yourTableView indexPathForSelectedRow].row); in your current prepareForSegue method immediately before or after the line you currently use the indexPathForSelectedRow, and see what it is returning in your console. Probably 0.
Instead of connecting the segue from the UITableViewCell Prototype to the DetailViewController, connect it from the UITableViewController to the DetailViewController and then use the methods below:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
SaintsDetailViewController *detailViewController = [[SaintsDetailViewController alloc] init];
saintsNewsStory* displayThisStory =[self.dataController objectAtIndex:indexPath.row];
[detailViewController setStory:displayThisStory];
[self performSegueWithIdentifier:#"showDetail" sender:indexPath];
}
and your prepareForSegue: method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"showDetail"]) {
saintsDetailViewController *detailViewController = [segue destinationViewController];
}
}