Reloading UITextView from another UIViewController - objective-c

I have 2 view controllers MainDetailViewController (MD) and MainEditViewController (ME)
There is a textView in MD with some text already there when view loads. Then I call ME like this
MainEditViewController *editVC = [[MainEditViewController alloc] init];
editVC.theTextView.text = self.theTextView.text;
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:editVC];
[self.navigationController presentViewController:navCon animated:YES completion:nil];
I am passing the TextView's text value to ME like above and calling it with presentViewController method.
In ME I edit the text and click on save button which should update the text value in MD's textView
MainDetailViewController *mainDetailVC = [[MainDetailViewController alloc] init];
mainDetailVC.theTextView.text = self.theTextView.text;
[self dismissViewControllerAnimated:YES completion:nil];
This is not reflecting change in MD
What am I doing wrong?

The easiest way to do this is by using block.
Add new block typedef to your MainDetailViewController.h file just after imports but before #interface:
typedef void (^ReturnBlock)(NSString *arg);
In MainEditViewController.h file between #interface and #end add public property:
#property(copy) ReturnBlock returnBlock;
This is your block which will be called when you dismiss your ME view controller.
Next move to MainEditViewController.m file and replace code in save button from:
MainDetailViewController *mainDetailVC = [[MainDetailViewController alloc] init];
mainDetailVC.theTextView.text = self.theTextView.text;
[self dismissViewControllerAnimated:YES completion:nil];
to:
if (self.returnBlock)
self.returnBlock(self.theTextView.text);
[self.navigationController popViewControllerAnimated:YES];
You should use popViewControllerAnimated: method instead of dismissViewControllerAnimated.
The last change left to do is in your MainDetailViewController.m file when you create editVC, add after:
editVC.theTextView.text = self.theTextView.text;
this:
editVC.returnBlock = ^(NSString *returnText) {
NSLog(#"Returned text: %#", returnText);
self.theTextView.text = returnText;
};
Happy coding.

Views are not loaded until you present the view, so you will need to create a property to hold that string value and in viewDidLoad, you should set the textfield with the string value.

You need to use a protocol to retrieve data from B to A.
You can have a look here
and here.

Related

How to put score in label on second screen after NSTimer?

I have a file (.m) wich is counting the clicks on a button
counter=counter +1;
count.text = [NSString stringWithFormat:#"%i",counter];
After a NSTimer a new screen appears (a new .m / .h / .xib file) but i want the score (clicks on button) in a label on the new screen.
On the first screen header file i'm doing:
IBOutlet UILabel *count;
but after the NStimer countdown i want to display to score on the new screen (screen 2)
Does anyone know how i can do this?
Need some more information? Please ask me!
In your randomMainVoid: action set the property of the label before you push the second view.
I don't know the name of your secondViewController and what you named the label in the secondViewController, so I can't give you exact code here.
Can you tell me what those names are and I will post the code here for you.
You should always name your ViewControllers with a Capital letter like Score, not score. But in either case:
Score *scoreView = [[Score alloc]initWithNibName:#"Score" bundle:nil];
[scoreView.count setText:count.text]; //Replace this with the text you want to pass
[self presentModalViewController:scoreView animated:YES];
Or if your using a NavigationController you could push the view onto the stack:
[self.navigationController pushViewController:scoreView animated:YES];
So, how are you initializing your timer? I assume you are using something like...
timerWithTimeInterval:target:selector:userInfo:repeats:
This method takes a selector, which is called when the timer expires/fires. In this method you should be displaying your view and you can pass the count to the new view you are creating. When your view loads you can set the count to a label on the view.
Just give your second view controller an initializer like:
- (id)initWithScoreText:(NSString*)scoreText {
if ((self = [super initWithNibName:#"MyNibName" bundle:nil])) {
scoreLabelText = [scoreText copy];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
//set the text of the label
scoreLabel.text = scoreLabelText;
}
- (void) dealloc {
//release the string since we copied it
[scoreLabelText release];
[super dealloc];
}
Then in your first view controller, you would do something like:
SecondViewController* newController = [[[SecondViewController alloc] initWithScoreText:count.text] autorelease];
[self.navigationController pushViewController:newController animated:YES];
Edit: updated based on feedback from Paul.s.

Passed array is not passed by popViewControllerAnimated... why?

Dont be put off by the huge question... (its mostly code).
OK, I have a navigation Controller which contains a view controller (Called AddClaim) containing a tableView.
if a cell is selected, this is called:
EditClaimDetails *detailViewController = [[[EditClaimDetails alloc] init] autorelease];
// Pass the selected object to the new view controller.
detailViewController.selectedIndexPath = indexPath;
detailViewController.newClaimArrayDetails2 = newClaimArrayDetails;
[self.navigationController pushViewController:detailViewController animated:YES ];
This works nicely and a new view controller is shown containing a tableView (It is an exclusive list).
In ViewDidLoad of the EditClaimDetails this code exists: (claimTypeHoldingArray is a mutable array declared in the header file)
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleBordered target:self action:#selector(pressedBack)];
self.navigationItem.leftBarButtonItem = backButton;
claimTypeHoldingArray = [[NSMutableArray alloc] initWithArray:newClaimArrayDetails2];
Basically the result of this is as expected: A back button is shown - when pressed - it calls a selector popping the view controller to AddClaim, claimTypeHoldingArray contains the newClaimsArray given in AddClaim.
This is part of the code in didSelectRowAtIndexPath: (claimTypeArray is the array which holds the textLabels of the cells)
[claimTypeHoldingArray replaceObjectAtIndex:0 withObject:[claimTypeArray objectAtIndex:indexPath.row]];
What this does is that the first object of claimTypeHoldingArray is replaced with what text was on the TextLabel of the cell. so far so good. (tested with nslog)
This is the code for when the back button is pressed:
-(IBAction)pressedBack {
AddClaim *sender = [[[AddClaim alloc] init] autorelease];
sender.newClaimArrayDetails = claimTypeHoldingArray;
[self.navigationController popViewControllerAnimated:YES];
This is where the trouble starts...
This action (according to me) should replace newClaimArrayDetails with claimTypeHoldingArray. (it does so) But when the view controller is popped and the screen moves back to add claim this array is not changed!
What have I done wrong?! btw, all properties are set.
this is the test i do in viewDidAppear:
NSLog(#"%#",[newClaimArrayDetails objectAtIndex:0]);
This answer is the same scale as the question, hope its's not too large ;)
So, in your pressedBack button method you're trying to update the initial AddClaim view controller object with the claimTypeHoldingArray.
You're halfway right - you're definitely updating an AddClaim object, just not the one that's inside your navigation controller. you're creating a new one and updating that instead!
-(IBAction)pressedBack {
// This line creates a new AddClaim view controller
AddClaim *sender = [[[AddClaim alloc] init] autorelease];
// This line updates your _new_ AddClaim view controller
sender.newClaimArrayDetails = claimTypeHoldingArray;
// This line displays the _old_ AddClaim object
[self.navigationController popViewControllerAnimated:YES];
You need to pass into your EditClaimDetails view controller the AddClaim view controller that created it.
In your cell is selected method add something like
detailViewController.addClaimViewController = self;
(where addClaimViewCOntroller is a property on your EditClaimDetails object like
#property (nonatomic, retain) Addclaim *addClaimViewController;
Then, your pressedBack method becomes
-(IBAction)pressedBack {
// This line updates your _old_ AddClaim view controller
addClaimViewController.newClaimArrayDetails = claimTypeHoldingArray;
// This line displays the _old_ AddClaim object
[self.navigationController popViewControllerAnimated:YES];
Hope that helps.
Check your array property definition in AddClaim, is it by any chance (nonatomic, copy)? If yes the it would hold a private copy of your array, so that you original array couldn't change.

Declaring UITouchGestures?

I'm working on using a UISwipeGestureRecognizerDirectionRight method to change views within an application and i have used the following code in the main file of the View Controller but it seems as though i need to define the gesture after the asterisk and declare it as this build error states:
"swipeGesture undeclared"
-(void)createGestureRecognizers {
UISwipeGestureRecognizerDirectionRight * swipeGesture = [[UISwipeGestureRecognizerDirectionRight alloc]
initWithTarget:self
action:#selector (handleSwipeGestureRight:)];
[self.theView addGestureRecognizer:swipeGesture];
[swipeGesture release];
}
-(IBAction)handleSwipeGestureRight {
NC2ViewController *second2 =[[NC2ViewController alloc] initWithNibName:#"NC2ViewController" bundle:nil];
second2.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:second2 animated:YES];
[second2 release];
}
So my question is how do i declare the "swipeGesture" after the asterisk in the header file or have i done something wrong?
Thank You
UISwipeGestureRecognizerDirectionRight is an enum value for the four possible directions. It is not a class you instantiate to recognize gestures. Use UISwipeGestureRecognizer instead:
UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector (handleSwipeGestureRight:)];
//Set the direction you want to detect by setting
//the recognizer's direction property...
//(the default is Right so don't really need it in this case)
swipeGesture.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swipeGesture];
[swipeGesture release];
Also, the handler method should be:
-(IBAction)handleSwipeGestureRight:(UISwipeGestureRecognizer *)swipeGesture {
because in the selector for the action, you put a colon in the method name meaning you want it to pass the sender object as the first parameter. (You could also remove the colon from the selector instead if you don't need the sender in the handler.)
Finally, void is more appropriate than IBAction in the handler since it won't be called from an object in a xib. However, since IBAction and void are the same thing, it won't cause a problem.

Adding additional UITabbarItem to UITabbarController

I am wondering if there is a way to add an additional UITabBarItem to my exisiting UITabBarController. It doesn't need to be in runtime.
All I want to do is when hitting this button I want to presentModalViewController: over my actually visible ViewController, which should either be the TabBarController or its controllers.
Hopefully this is clear enough, if not, feel free to ask.
As a result of my research you cannot add a UITabBarItem to a UITabBar that is managed by a UITabBarController.
Since you maybe have added your UITabBarItem by adding a list of view controller, this is also the way of your choice to add further custom UITabBarItems, as i will show now:
Pre-Conditions:
As i mentioned before, you maybe have added your UITabBarItems by adding a list of view controller:
tabbarController = [[UITabBarController alloc] init]; // tabbarController has to be defined in your header file
FirstViewController *vc1 = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:[NSBundle mainBundle]];
vc1.tabBarItem.title = #"First View Controller"; // Let the controller manage the UITabBarItem
SecondViewController *vc2 = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:[NSBundle mainBundle]];
vc2.tabBarItem.title = #"Second View Controller";
[tabbarController setViewControllers:[NSArray arrayWithObjects: vc1, vc2, nil]];
tabbarController.delegate = self; // do not forget to delegate events to our appdelegate
Adding custom UITabBarItems:
Now since you know how to add UITabBarItems through adding view controller, you can also use the same way to add custom UITabBarItems:
UIViewController *tmpController = [[UIViewController alloc] init];
tmpController.tabBarItem.title = #"Custom TabBar Item";
// You could also add your custom image:
// tmpController.tabBarItem.image = [UIImage alloc];
// Define a custom tag (integers or enums only), so you can identify when it gets tapped:
tmpController.tabBarItem.tag = 1;
Modify the line above:
[tabbarController setViewControllers:[NSArray arrayWithObjects: vc1, vc2, tmpController, nil]];
[tmpController release]; // do not forget to release the tmpController after adding to the list
All fine, now you have your custom button in your TabBar.
What about handling events of this custom UITabBarItem?
Its easy, look:
Add the UITabBarControllerDelegate to your AppDelegate class (or the class which is holding the tabbarController).
#interface YourAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> { }
Fit the protocol definition by adding this function:
- (void)tabBarController:(UITabBarController *)theTabBarController didSelectViewController:(UIViewController *)viewController {
NSUInteger indexOfTab = [theTabBarController.viewControllers indexOfObject:viewController];
UITabBarItem *item = [theTabBarController.tabBar.items objectAtIndex:indexOfTab];
NSLog(#"Tab index = %u (%u), itemtag: %d", indexOfTab, item.tag);
switch (item.tag) {
case 1:
// Do your stuff
break;
default:
break;
}
}
Now you have all you need to create and handle custom UITabBarItems.
Hope this helps.
Have fun....
Access the tabBar - property of your UITabBarController (reference), grab the elements array with the items - property (reference), add a new UITabBarItem to this array and use the tabBar's setItems:animated: - method to update your tab bar. Add an action to this tab bar to display the modal view controller.

When "programmatically" creating UINavigationController and UITabBarController, how do I address their functions (like viewWillAppear?)

I am creating my Nav and TabBar in code at launch via:
IN : myAppDelegate.m
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// set up a local nav controller which we will reuse for each view controller
UINavigationController *localNavigationController;
// create tab bar controller and array to hold the view controllers
tabBarController = [[UITabBarController alloc] init];
NSMutableArray *localControllersArray = [[NSMutableArray alloc] initWithCapacity:4];
// setup the first view controller (Root view controller)
RootViewController *myViewController;
myViewController = [[RootViewController alloc] initWithTabBar];
// create the nav controller and add the root view controller as its first view
localNavigationController = [[UINavigationController alloc] initWithRootViewController:myViewController];
// add the new nav controller (with the root view controller inside it)
// to the array of controllers
[localControllersArray addObject:localNavigationController];
// release since we are done with this for now
[localNavigationController release];
[myViewController release];
// setup the first view controller just like the first
ResortsListViewController *resortsListViewController;
resortsListViewController = [[ResortsListViewController alloc] initWithNibName:#"ResortsListView" bundle:nil];
resortsListViewController.title = #"Category1";
resortsListViewController.tabBarItem.image = [UIImage imageNamed:#"image1.png"];
resortsListViewController.navigationItem.title=#"Category1";
localNavigationController = [[UINavigationController alloc] initWithRootViewController:resortsListViewController];
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
// setup the second view controller just like the first
ResortsListViewController *resortsListViewController;
resortsListViewController = [[ResortsListViewController alloc] initWithNibName:#"ResortsListView" bundle:nil];
resortsListViewController.title = #"Category2";
resortsListViewController.tabBarItem.image = [UIImage imageNamed:#"image2.png"];
resortsListViewController.navigationItem.title=#"Category2";
localNavigationController = [[UINavigationController alloc] initWithRootViewController:resortsListViewController];
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
// setup the third view controller just like the first
ResortsListViewController *resortsListViewController;
resortsListViewController = [[ResortsListViewController alloc] initWithNibName:#"ResortsListView" bundle:nil];
resortsListViewController.title = #"Category3";
resortsListViewController.tabBarItem.image = [UIImage imageNamed:#"image3.png"];
resortsListViewController.navigationItem.title=#"Category3";
localNavigationController = [[UINavigationController alloc] initWithRootViewController:resortsListViewController];
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
[resortsListViewController release];
// load up our tab bar controller with the view controllers
tabBarController.viewControllers = localControllersArray;
// release the array because the tab bar controller now has it
[localControllersArray release];
// add the tabBarController as a subview in the window
[window addSubview:tabBarController.view];
// need this last line to display the window (and tab bar controller)
[window makeKeyAndVisible];
}
As you see, I am re-using ResortsListViewController for different category displays (resorts with Beaches, resorts with Pools, resorts with espresso bars) ... now, without harassing me (grin) about the silliness of my categories (cos this is a test app) I need need to do several things:
I need to be able to know which tab click caused the ResortsListViewController to be displayed. I was hoping to use TAG but "initWithRootViewController" does not have the "tag" control. So, if i use an imagefilename that is the category name, I can use that filename to distinguish categories...or even navigationItem name. I need to know if there is a way for ResortsListViewController to know which tabbar item click caused it's display. I thought to look for a "action" that I could assign to the tabbar item, but that is not the way tabbarcontroller works.
When clicking from one tab to another, the view does indeed change, the title of ResortsListViewController changes, etc...but the TABLEVIEW it holds does not clear and display any new data. Searching the web I have found a possible solution:
http://discussions.apple.com/thread.jspa?threadID=1529769&tstart=0
basically saying:
In order for UINavigationControllers
to send
"viewWill/Did/Appear/Disappear"
messages, it needs to have received
"viewWill/Did/Appear/Disappear" from
its container.
What is the container for my UINavigationControllers in this situation? myAppDelegate is defined in the .h file as:
NSObject <UIApplicationDelegate, CLLocationManagerDelegate>
and does not have a:
- (void)viewWillAppear:(BOOL)animated {
}
section. When I add one it says "NSObject may not respond to -viewWillAppear" in the debugger.
Any help out there?
1) This is fairly simple. You need to set the delegate property for the UITabBarController. You would set this to the controller object that owns your tabbarcontroller (or your app delegate if that is your setup). Whichever object is set as the delegate would then receive:
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
AND
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
to manage tab selections. Place one of these methods in the implementation of whatever object you set as the delegate (you would want it to be the object that owns all of the controllers).
2) If you have a UIViewController that appears as a tab selection (such as ResortsListViewController) then you would need to put the viewWillAppear method in the controller implementation yourself:
#implementation ResortsListViewController
- (id)initWithNibName:(NSString *)name bundle:(NSBundle *)bundle {
...
}
... etc. ....
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[table reloadData];
}
...
#end
Let me know if I misunderstood the second part of your question.
Okay, here goes: This is the correct answer to the question, however, it did not end up being this hard. All I had to do was the following:
Create a property in ResortViewController of type int with variable name whichChoice (for instance). Then address it in the setup of the TabBarController a la:
// setup the first view controller just like the first
ResortsListViewController *resortsListViewController;
resortsListViewController = [[ResortsListViewController alloc] initWithNibName:#"ResortsListView" bundle:nil];
// START HERE IS THE CHANGE
resortsListViewController.whichChoice = 1;
// END HERE IS THE CHANGE
resortsListViewController.title = #"Category1";
resortsListViewController.tabBarItem.image = [UIImage imageNamed:#"image1.png"];
resortsListViewController.navigationItem.title=#"Category1";
localNavigationController = [[UINavigationController alloc] initWithRootViewController:resortsListViewController];
[localControllersArray addObject:localNavigationController];
[localNavigationController release];
To find out which tab was clicked when my resortsListViewController takes over, I simply query the class variable: whichChoice to get the answer.
Things like this are so simple you skip over them. I thought you had to pass the variable in an action and specify where it would go with the target like you do in other objects -- but when you set things up ahead of time you do not have to do that. Now, having said that, dynamically setting "whichChoice" is not so easy without a lot more thought...but setting it to a variable KNOWN at setup is fine.
For answer #2, I simply put a variable style of IBOutlet in my class, hooked it up to the table, and then followed your instructions, because without the variable in the IBOutlet and hooking it up to the table, there is no reference to the table view. For some reason hooking up the table simply to the VIEW "Referencing Outlet" and calling [self.tableview reloadData] did not do the job.
But for the most part, your answers were right and led me in the right direction. As an aside, I really hate that you have to right-click and drag from here-to-there in IB if you have an IB element you built. You should be able to hook it up in code using it's ObjectId (or something). That would be more in line with programmers. I know IB is made to allow programming to be easier for designers, but gee wilikers..it is hard to wrap my mind around! I end up dropping IB and creating elements in code most of time time...which I do not know if is as fast to execute. I tend to think not...but have no proof to the contrary.