iPhone: passing data between views - objective-c

I'm trying to create a simple application to understand how to pass data between views. I have two views. The first one passes some strings to the second view with this method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"colorSegue"]) {
[segue.destinationViewController setFirstColor:self.favoriteColorTextField.text];
[segue.destinationViewController setSecondColor:self.secondColor];
[segue.destinationViewController setDelegate:self];
}
And the second view receives the data with this method.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (self.firstColor != nil && ![self.firstColor isEqualToString:#""]) {
self.favoriteColorLabel.text = [[NSString alloc] initWithFormat:#"Favorite color: %#", self.firstColor];
}
if (self.secondColor != nil && ![self.secondColor isEqualToString:#""]){
self.secondFavoriteColorLabel.text = [[NSString alloc] initWithFormat:#"Second favorite color: %#", self.secondColor];
}
}
Till now everything works fine, the second view visualizes the first string (but not the second one, because it's still empty). Then the second view passes back a string to the first one through delegation:
- (void)viewWillDisappear:(BOOL)animated
{
[self.delegate setSecondFavoriteColor:self.secondFavoriteColorTextField.text];
}
And the first view visualizes the string like this:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (self.secondColor != nil && ![self.secondColor isEqualToString:#""]) {
self.secondFavoriteColorLabel.text = [[NSString alloc] initWithFormat:#"Second favorite color: %#", self.secondColor];
}
}
Here everything works, the first view visualizes both strings.
But now I'm having a problem. If i try to pass again the strings to the second view, only the first one is passed correctly. The second view always receives secondColor with the nil value, even after its value has been set through delegation.
The variables firstColor and secondColor are declared and synthetized exactly in the same way. Can you please help me find the error?

I think you need to trasform segue.destinationViewController to SecondViewController class
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"colorSegue"]) {
SecondViewController *svc = [segue destinationViewController];
[svc setFirstColor:self.favoriteColorTextField.text];
[svc setSecondColor:self.secondColor];
[svc setDelegate:self];
}
OR
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"colorSegue"]) {
[(SecondViewController *)segue.destinationViewController setFirstColor:self.favoriteColorTextField.text];
[(SecondViewController *)segue.destinationViewController setSecondColor:self.secondColor];
[(SecondViewController *)segue.destinationViewController setDelegate:self];
}

If you want to pass data between more than 1 views then you have these options.
You can use a singleton
Use your AppDelegate.
Here is a good tutorial about singleton:
http://www.galloway.me.uk/tutorials/singleton-classes/
If you want to use AppDelegate to do it like this:
AppDelegate.h
#property (nonatomic, retain) NSString *something;
AppDelegate.m
#synthesize something;
FirstViewController.m
#import "AppDelegate.h";
(void) setSomething {
AppDelegate *AppDelegate = [[UIApplication sharedApplication] delegate];
AppDelegate.something = #"something";
}
SecondviewController.m
#import "AppDelegate.h";
(void) getSomething {
AppDelegate *AppDelegate = [[UIApplication sharedApplication] delegate];
label.text = AppDelegate.something;
}

Related

Objective-C: How can i access another ViewController and change UI in it?

I'm trying to navigate to another view and change its UI.
I manage to access to the controller method "hideElements",
but all the elements are "NIL" and it doesnt change anything.
(When i access the ViewController.m regular with no navigation then the properties are not NIL)
Why is that and How can i solve it?
ViewController.m:
- (void)hideElements:(NSString *)identifier{
[_agreeButton setHidden:TRUE];
[_noThankButton setHidden:TRUE];
[self cleanButtons];
}
ViewController2.h
#import "ViewController.h"
#property (nonatomic) ViewController *myViewController;
ViewController2.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSString *identifire = [sender text];
ViewController* vc = (ViewController*)segue.destinationViewController;
[vc hideElements:identifire];
}
ScreenShot:
It seems that the problem is the view controller's view (and subviews including outlets) haven't been created by the time you are calling hideElements. So you can do a couple of things
1)
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSString *identifire = [sender text];
ViewController* vc = (ViewController*)segue.destinationViewController;
vc.view;
[vc hideElements:identifire];
}
2)
ViewController2.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSString *identifire = [sender text];
ViewController* vc = (ViewController*)segue.destinationViewController;
vc.shouldHideElements = YES;
}
ViewController.m
- (void)viewDidLoad {
if (self.shouldHideElements) {
[self hideElements];
}
}

Passing indexPath from UITableView

In my application I have oneUITableViewController (Uctovatrida0TableViewController) and two otherUIViewContoller (DetailViewController and examples).
Now I have a problem because I do not know how to identify index (from indexPath.row) in my firstUIViewController to then correctly referred to another UIViewController.
The code in my Uctovatrida0TableViewController.m (works well):
-(void) showDetailsForIndexPath:(NSIndexPath*)indexPath
{
[self.searchBar resignFirstResponder];
DetailViewController* vc = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailsViewController"];
Ucty* ucet;
if(isFiltered)
{
ucet = [_filteredTableData objectAtIndex:indexPath.row];
}
else
{
ucet = [self.alldataArray objectAtIndex:indexPath.row];
}
vc.uctyItem = ucet;
[self.navigationController pushViewController:vc animated:true];
}
Code in my class DetailViewConroller where I need indexPath from Uctovatrida0TableViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"Priklad"])
{
Priklad *vc = [segue destinationViewController];
Ucty* ucet;
//of course this does not work
ucet = [self.alldataArray objectAtIndex:NSIndexPath.row];
vc.uctyItem = ucet;
[self.navigationController pushViewController:vc animated:true];
}
}
Code in my class Priklad.m:
- (void)setDetailItem:(id)newDetailItem
{
if (self.uctyItem != newDetailItem)
{
self.uctyItem = newDetailItem;
[self configureView];
}
}
- (void)configureView
{
if (self.uctyItem ) {
self.prikladLabel.text = self.uctyItem.priklad;
self.uctykprikladu.text = self.uctyItem.uctykprikladu;
}
}
From your code, it seems DetailViewConroller has a property uctyItem. So in your DetaiViewController's prepareForSegue:sender: method, you can just call vc.uctyItem = self.uctyItem;.
Add a property to DetailViewConroller that you can set before pushing to it.
#property (assign, nonatomic) int index;
If you want to get selected cell indexPath then there is a method for that is : selectedIndexPath = [tableView indexPathForSelectedRow];

using prepareForSegue to get a tag / id from a label

I have a set of UILabel's and would like to put the id of the selected item into the tag. Like so:
UILabel *miII = [[UILabel alloc] initWithFrame:CGRectMake(530, 0, 25, 25)];
miII.tag=item.id;
I have the following where I am able to set the itemId property of the destinationViewController. The problem I am having is how do I access the tag from the UILabel? Or is there a better way to do this? I have included my experiences in comments and am not using a UITableView.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"prepareForSegue: %#", segue.identifier);
ItemDetailViewController *myVC = [segue destinationViewController];
//[myVC setItemId:12]; // <-- hard-coding this works
[myVC setItemId:sender.view.tag]; // this doesn't work
}
- (void)tapRecognized:(UIGestureRecognizer *)sender
{
NSLog(#"that tap was recognized with %d", sender.view.tag); // <-- this works
[self performSegueWithIdentifier: #"ItemSegue" sender: self];
}
thx in advance
The sol'n was fairly simple but a bit cryptic based upon other q's and a's. I did the following:
#interface ListViewController ()
{
#private
int _itemId;
}
.....
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// NSLog(#"prepareForSegue: %#", segue.identifier);
// NSLog(#"prepareForSegue: %#", sender);
ItemDetailViewController *myVC = [segue destinationViewController];
[myVC setItemId:_itemId]; // obviously need property for itemId on ItemDetailViewController
}
- (void)tapRecognized:(id)sender
{
NSLog(#"that tap was recognized with %d", [(UIGestureRecognizer *)sender view].tag);
_itemId=[(UIGestureRecognizer *)sender view].tag;
in ItemDetailiViewController.h ...
#interface ItemDetailViewController : UIViewController
#property (nonatomic) int itemId;

passing data through segues

I'm trying to use one web view and have three different buttons to take you to this web view. Each one will take you to a different website. I've created a new webview class and in this class I have a method to set which url it will go to.
In the original view controller I'm trying to send a different number through to this method but it says there is no known instance method.
The code is below:
method to set url
-(void)setUrlNo:(int)urlNo
{
if (urlNo == 0) {
url = #"http://www.twitter.com/ac3112";
} else if (urlNo == 1) {
url = #"http://www.facebook.com/ac3112";
} else if (urlNo == 2) {
url = #"http://www.adamcarlin.co.uk";
}
}
prepare for segue method
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Twitter"])
{
[segue.destinationViewController setUrlNo:1];
}else if ([segue.identifier isEqualToString:#"FB"]) {
[segue.destinationViewController setUrlNo:2];
} else {
[segue.destinationViewController setUrlNo:3];
}
}
segue.destinationViewController returns an object of type id. You'll need to cast it to your view controller type to send the message without warning:
[(YourViewController *)segue.destinationViewController setUrlNo:1];
to open a different website and send url through segue you could use this method based on a master view controller. You create a push segue from each table cell or button and drag to UIViewController and then put the segue value from the below in there to make sure you're getting the right site/value.
FollowViewController.h
#interface FollowViewController : UIViewController
#end
FollowViewController.m segue method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
FollowDetailViewController *webController = [[FollowDetailViewController alloc] init];
if ([[segue identifier] isEqualToString:#"Facebook"]) { // Facebook
NSString *urlstr=#"https://www.facebook.com/";
webController = [segue destinationViewController];
webController.urlstr = urlstr;
webController.title = #"Facebook"; // this sets the title of the next page
}
else if ([[segue identifier] isEqualToString:#"Instagram"]) { // Instagram
NSString *urlstr=#"http://instagram.com/";
webController = [segue destinationViewController];
webController.urlstr = urlstr;
webController.title = #"Instagram"; // this sets the title of the next page
}
else if ([[segue identifier] isEqualToString:#"Twitter"]) { // Twitter
NSString *urlstr=#"https://twitter.com/";
webController = [segue destinationViewController];
webController.urlstr = urlstr;
webController.title = #"Twitter"; // this sets the title of the next page
}
else if ([[segue identifier] isEqualToString:#"YouTube"]) { // YouTube
NSString *urlstr=#"http://www.youtube.com/";
webController = [segue destinationViewController];
webController.urlstr = urlstr;
webController.title = #"YouTube"; // this sets the title of the next page
}
self.title = #"Follow"; // This sets the title of the back button, and the title of this page
}
FollowDetailViewController.h
#interface FollowDetailViewController : UIViewController {
NSString *urlstr;
}
#property (strong, nonatomic) IBOutlet UIWebView *WebView;
#property (strong, nonatomic) NSString *urlstr;
#end
FollowDetailViewController.m
#implementation FollowDetailViewController
#synthesize WebView;
#synthesize urlstr;
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:urlstr];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[self.WebView loadRequest:requestObj];
}
#end
good luck!

How to go to the next cell (Detail View) in a UITableView?

So, i have a UITableView split in 3 sections. I want to be able, once i opened up the second row in the first section (i.e.), to swipe left to go to the next cell, and to swipe right to go the previous cell.
I wrote the code for the swipe:
SecondDetailView.m
- (void)viewDidLoad
{
UISwipeGestureRecognizer *swipeRecognizerLeft = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipeDetectedLeft:)];
swipeRecognizerLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeRecognizerLeft];
[swipeRecognizerLeft release];
UISwipeGestureRecognizer *swipeRecognizerRight = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipeDetectedRight:)];
swipeRecognizerRight.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swipeRecognizerRight];
[swipeRecognizerRight release];
}
- (void)swipeDetectedRight:(UIGestureRecognizer *)sender {
NSLog(#"Right Swipe");
}
- (void)swipeDetectedLeft:(UIGestureRecognizer *)sender {
NSLog(#"Left Swipe");
}
How can i do that? Is it right to put the code into the Detail View?
I have a very simple solution for your issue.
You need to declare an NSMutableArray *arr; in your .h file and assign your array to this array when you are moving to detail page.
And also you need to declare an NSString *currentPos; variable.
- (void)swipeDetectedRight:(UIGestureRecognizer *)sender {
currentPos--;
NSMutableDictionary *dic=[arr objectAtIndex:currentPos];
}
- (void)swipeDetectedLeft:(UIGestureRecognizer *)sender {
currentPos++;
NSMutableDictionary *dic=[arr objectAtIndex:currentPos];
}
In this way you can get your next and prev index values of array.
Hope this help for you.
Shivam
In my example I use NSString as my data that will be displayed in detail view controller. Feel free to change that to whatever suits your needs. Okay so here we go:
First declare a protocol in DetailViewController like so:
#class DetailViewController;
#protocol DetailViewControllerDelegate <NSObject>
- (void)swipeToNextCell:(DetailViewController *)sender;
- (void)swipeToPreviousCell:(DetailViewController *)sender;
#end
#interface DetailViewController : UIViewController
#property(weak, nonatomic) id<DetailViewControllerDelegate> delegate;
#property(copy, nonatomic) NSString *data;
#property(weak, nonatomic) IBOutlet UILabel *label;
#end
The next thing is to add UISwipeGestureRecognizers in DetailViewController to check for gestures:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UISwipeGestureRecognizer *leftGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeDetectedLeft:)];
leftGesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:leftGesture];
UISwipeGestureRecognizer *rightGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeDetectedRight:)];
rightGesture.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:rightGesture];
}
Implement viewWillAppear to display your data when you push your DetailViewController:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.label.text = self.data;
}
Don't forget to implement methods that will be called by GestureRecognizers:
- (void)swipeDetectedRight:(UISwipeGestureRecognizer *)sender
{
NSLog(#"Right Swipe");
[self.delegate swipeToNextCell:self];
self.label.text = self.data;
}
- (void)swipeDetectedLeft:(UISwipeGestureRecognizer *)sender
{
NSLog(#"Left Swipe");
[self.delegate swipeToPreviousCell:self];
self.label.text = self.data;
}
And thats all you need in your Detail View. Now go to the TableViewController. Your TableViewController should implement the DetailViewControllerDelegate protocol:
#interface CustomTableViewController : UITableViewController <DetailViewControllerDelegate>
#property(strong, nonatomic) DetailViewController *detailViewController;
#property(assign, nonatomic) NSInteger currentRow;
#end
Here is my getter for detailViewController #property:
- (DetailViewController *)detailViewController
{
if (_detailViewController == nil)
{
_detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
_detailViewController.delegate = self;
}
return _detailViewController;
}
Here is how I manage row selection:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController *viewController = self.detailViewController;
viewController.data = [NSString stringWithFormat:#"Cell: %d", indexPath.row];
viewController.title = #"Detail";
self.currentRow = indexPath.row;
[self.navigationController pushViewController:viewController animated:YES];
}
The last thing you have to do is to implement protocol's methods:
- (void)swipeToNextCell:(DetailViewController *)sender
{
// Get data for next row
sender.data = [NSString stringWithFormat:#"Cell: %d", ++self.currentRow];
}
- (void)swipeToPreviousCell:(DetailViewController *)sender
{
// Get data for next row
sender.data = [NSString stringWithFormat:#"Cell: %d", --self.currentRow];
}
I tested it on simulator and worked fine. It is very simple as my data model is quite simple - it is just NSString. There is no checking whether there is any row in section so you have to figure that out yourself. But the whole delegation pattern should be the same.
Good luck!
Declare a protocol in the detail view controller and set the parent (which should be the table view controller) as the delegate. Then, in the swipe methods call the delegate and implement the necessary code for changing the selected row.