Parent view -> Subview -> button -> subview method - iphone

I have a parentview with a ui view on it. This loads a subviewcontroller on viewdidload. The subviewcontroller has a button on it that is linked up on touch up inside to a subviewcontroller method called clicked.
This causes a bad access error to fire and crashes the app. My question is, is this setup possible or do I have to have the method in the parent view? The subview will be loaded in 8 different parentviews so I would like to keep the method in the subview.
What would be the correct way of doing this?

The good approach for this kind of setup is to have a protocol including the click message, a delegate property of type id in the view containing the button and a method in the same view that fires the delegated message like so [self.delegate clicked]. The TouchUpInside event is linked to that method in IB and the parent view set itself as delegate of the subview and conform itself to the protocol. It can sounds a bit tricky but its definitely the proper way to do.
the Protocol
#protocol viewWithButtonDelegate
-(void)buttonClicked:(UIButton*)button inView:(UIView*)view;
#end
in the subview interface
#interface viewWithButton {
...
id<viewWithButtonDelegate> delegate;
}
...
#property (nonatomic, retain) id<viewWithButtonDelegate> delegate
-(void)buttonClicked:(id)sender;
...
#end
in the subview implementation
-(void)buttonClicked:(id)sender {
if([sender class] == [UIButton class]) {
[self.delegate buttonClicked:(UIButton*)sender inView:self]
}
}
the controller interface is declared like this
#interface myController<viewWithButtonDelegate>
and finally in the controller implementation :
-(void)buttonClicked:(UIButton*)button inView:(UIView*)view {
//do something in response to button clicked
}
hope this helps...

Related

Xcode - update ViewController label text from different view

I have two view Controllers in my project ViewController, SettingsView. Here I am trying to update the ViewController's label, when i click on the SettingsView's back button. NSLog is working fine, but the label is not updating...
Please help me....
SettingsView.m
-(IBAction)backToMain:(id) sender {
//calling update function from ViewController
ViewController * vc = [[ViewController alloc]init];
[vc updateLabel];
[vc release];
//close the SettingsView
[self dismissModalViewControllerAnimated:YES];
}
ViewController.m
- (void)updateLabel
{
NSLog(#"Iam inside updateLabel");
self.myLabel.text = #"test";
}
Could you please tell me whats wrong with my code? Thank you!
You have to implement protocols for that. Follow this:
1) In SettingView.h define protocol like this
#protocol ViewControllerDelegate
-(void) updateLabel;
#end
2) Define property in .h class and synthesis in .m class..
#property (nonatomic, retain) id <ViewControllerDelegate> viewControllerDelegate;
3) In SettingsView.m IBAction
-(IBAction)backToMain:(id) sender
{
[viewControllerDelegate updateLabel];
}
4) In ViewController.h adopt protocol like this
#interface ViewController<ViewControllerDelegate>
5) In viewController.m include this line in viewDidLoad
settingView.viewControllerDelegate=self
Your label is not updating because , you are trying to call updateLabel method with a new instance.
You should call updateLabel of the original instance of viewcontroller from which you have presented your modal view.
you can use a delegate mechansim or NSNotification to do the same.
Delegate mechnaism would be clean. NSNotification is quick and dirty.
You are not exactly calling the correct vc. This is because you are creating a new instance of that class and calling the updateLabel of that instance.
You have a few options.
Either implement it as a delegate callBack (delegate messagePassing, or delegate notification - however you want to call it) to notify that class instance to call the updateLabel method.
Use the original instance VC as a dependency injection into the class that you are on right now, and use that instance to call the updateLabel
Use NSNotifications / NSUserDefaults to communicate between viewControllers and setup a notification system for your actions. This is quite easy, but not really great in the long run.
I would RECOMMEND option 1 (or) option 2.
Simply declare like this in SettingsView class:
UILabel *lblInSettings;// and synthesize it
Now assign like below when you presenting Settings viewController:
settingsVC.lblInSettings=self.myLabel;
Then whatever you update in lblInSettings it will be present in MainView obviously....
no need for any delegate methods or updating methods.
Means if you assign at the time of dismissing like
lblInSettings.text=#"My new value";
then self.myLabel also will be updated.
Let me know if you have any queries?

Pushing a UIViewController from a UIView

I need to push a UIView into my UINavigation controller. I am doing it by
[self.view addSubview:showContactFlow];
And on a button click in UIView I need to push another UIViewController over the UIView. From the UIView I am not able to access self.navigationcontroller How can I do this?
Edit:
I have set the UIView as the view of a new UIViewController I am pushing into, the before mentioned UIViewController . Now I would like to know, how to handle the UIView button event inside its UIViewController, in which's view it is set.
Add a UINavigationController ivar to the UIView and assign it to the main view controller's. Then you should be able to access it from the UIView.
Edit:
Your UIView subclass:
// CustomView.h
#interface CustomView: UIView {
// ...
// your variables
// ...
UINavigationController *navController;
}
#property (nonatomic, assign) UINavigationController *navController; // assign, because this class is not the owner of the controller
// custom methods
#end
// CustomView.m
#implementation Customview
// synthesize other properties
#synthesize navController;
// implementation of custom methods
// don't release the navigation controller in the dealloc method, your class doesn't own it
#end
Then before the [self.view addSubview:showContactFlow]; line just add [showContactFlow setNavController:[self navigationController]]; and then you should be able to access your hierarchy's navigation controller from your UIView and use it to push other UIViewControllers.
You should try to work with an MVC approach. So your controller has access to all that stuff and can keep pushing and popping views, so the view doesn't need to know too much about the controller.
Otherwise, and for this case you can solve it fast by using delegation. So:
showContactFlow.delegate = self;
[self.view addSubview:showContactFlow];
So later in the UIView, you can just say:
[self.delegate addSubview:self];
This is gonna work, but it's not likely to be the best approach you should use.
On button click, you can present a view controller like,
-(void)buttonFunction{
ThirdVC *third= [[ThirdVC alloc]initWithNibNme];......
[self presentViewController:third animated:NO];
}
Using Core animation you can make NavigationController's pushviewController like animation on writing code in ThirdVC's viewWillAppear: method.
where do you add the UIButton is it in showContactFlow view or in the ViewController's view??
In regard to the modalViewControllers issue the correct method is
[self presentModalViewController:viewController animated:YES];
the standard animation in upwards

Parent View Controller and Model View Controller dismissal detection not working

I have a UIViewController called ShowListViewController that uses a Modal View Controller to push another view onto the stack:
AddShowViewController *addShowViewController = [[AddShowViewController alloc] init];
[addShowViewController setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentModalViewController:addShowViewController animated:YES];
I would then like to call my method populateTableData of the ShowListViewController class when the addShowViewController disappears.
I would think that the answer found here would work, but it doesn't. My method populateTableData is not detected as an optional method to use.
Essentially my questions is: How do I detect when a Modal View Controller disappears so as to call a method within the class that pushed it on the stack?
This may not be a best solution, but can do what you want at this time.
In your showlistcontroller add an instance variable like
BOOL pushedView;
#implementation ShowListViewController
and before you do the modal presentation set its values as YES like
pushedView = YES;
[self.navigationController presentModalViewController:popView animated:YES];
in the viewWillAppear of ShowListViewController you can detect whether it is appearing because pop getting dismissed or not like
if (pushedView) {
NSLog(#"Do things you would like to on pop dismissal");
pushedView = NO;
}
I think you would like something like this.
You make a delegate inside ur modalVC like this:
#protocol ModalViewDelegate <NSObject>
- (void)didDismissModalView;
#end
and implement it in your MainVC like this:
#interface MainViewController : UIViewController <ModalViewDelegate>
{
Then u will make a delegate property in your modalVC like this:
#interface ModalShizzle : UIViewController
{
id<ModalViewDelegate> dismissDelegate;
}
You set the dismissDelegate of your ModalVC to your MainVC and then you make the delegate method. Before you dismiss it however you will call the ModalVC to do one last thing. (which is populate your table). You will call for the data inside your MainVC and then do whatever you feel like it, just before you dismissed your modalVC.
-(void)didDismissModalView
{
//call ModalVC data here...
//then do something with that data. set it to a property inside this MainVC or call a method with it.
//method/data from modalVC is called here and now u can safely dismiss modalVC
[self dismissModalViewControllerAnimated:YES];
}
Hope it helps ;)
OK so it appears that in Apple's template for Utility App's they ignore what the docs for [UIViewController][1] say and actually go out of their way to call dismissModalViewControllerAnimated: from the UIViewController that pushed the modal view onto screen.
The basic idea in your case will be
Define a protocol for AddShowViewControllerDelegate
Make ShowListViewController implement this protocol
Call a method on the delegate to ask it to dimiss the modal view controller
For a full example just create a new project with Utility template and look at the source for FlipsideViewController and MainViewController
Here is an example adapted for your needs:
AddShowViewController.h
#class AddShowViewController;
#protocol AddShowViewControllerDelegate
- (void)addShowViewControllerDidFinish:(AddShowViewController *)controller;
#end
#interface AddShowViewController : UIViewController
#property (nonatomic, assign) id <AddShowViewControllerDelegate> delegate;
- (IBAction)done:(id)sender;
#end
AddShowViewController.m
- (IBAction)done:(id)sender
{
[self.delegate addShowViewControllerDidFinish:self];
}
ShowListViewController.h
#interface ShowListViewController : UIViewController <AddShowViewControllerDelegate>
{
...
}
ShowListViewController.m
- (void)addShowViewControllerDidFinish:(AddShowViewController *)controller
{
[self dismissModalViewControllerAnimated:YES];
[self populateTableData];
}

I want to change the view controller when a button it's pressed iphone

I have a table view controller with custom cells. In those cells i added a button for each one of the cells. What i would like it's that when I press that button it display a new view with more information about that cell, different of the view that i get from didSelectRowAtIndexPath.
I know it's now a hard question. I've seen that the button has some action on interfacebuilder (touch down maybe) but how do i link it to what code. where should i declare the code that handle that event?
I've posted in other forums with no answer, hope this would work.
Thanks.
Gian
I would probably subclass UIButton to have an instance of NSIndexPath. That way, each individual UIButton in a UITableViewCell can "know" where it is in the table view, so when you press the button, you can call some method that takes an NSIndexPath and pushes a new view, similar to what you do in -didSelectRowAtIndexPath: but with your other view instead (maybe give it a descriptive method name like -didPressButtonAtIndexPath:).
Instead of using the Interface Builder to do this, you should add a method to the UIButton subclass itself that in turn calls a method on your view controller. Then, for each UIButton, you can use the UIControl method -addTarget:action:forControlEvents:. Have the UIButton call its own method, which calls the controller's method. Your solution might look something like:
// MyButton.h
#interface MyButton : UIButton {
NSIndexPath *myIndexPath;
MyViewController *viewController;
}
- (void)didPressButton;
#end
// MyViewController.h
#interface MyViewController { }
- (void)didPressButtonAtIndexPath:(NSIndexPath *)indexPath;
#end
Then, when you build your cells, for each button you add call:
[button addTarget:button
action:#selector(didPressButton)
forControlEvents:UIControlEventTouchDown];
And finally, implement -didPressButton to look like:
- (void)didPressButton {
[controller didPressButtonAtIndexPath:myIndexPath];
}

iphone sdk - calling a method of view's superview's viewcontroller

hello how can i call in the current view, a method implemented in the viewcontroller of the current view's superview?
can you help me please.
thanx
Typically this is done through delegates.
Have your view interface define a protocol and a reference to some delegate. Then have your parent viewcontroller implement this protocol.
Then the parent would do this:
someView.fooDelegate = self;
then the view would do something like this:
if(self.fooDelegate != nil) {
if([fooDelegate respondsToSelector:...]) {
[fooDelegate performSelector:...];
}
}
This is not compiled, but I think you get the gist.
You can add a function -(void)initWithView:(EchiquierDisplayView *)aSuperview or something like that, define a reference in your
#interface pieceDraggableImageView : UIImageView {
CGPoint startLocation;
CGPoint startLocationInView;
EchiquierDisplayView *theSuperview;
}
#property (nonatomic, retain) EchiquierDisplayView *theSuperview;
-(void)correctDestinationPosition;
-(void)initWithView:(EchiquierDisplayView *)aSuperview;
...
-(void)askSuperview;
#end
#implementation pieceDraggableImageView
...
-(void)initWithView:(EchiquierDisplayView *)aSuperview
{
theSuperview = aSuperview;
}
...
-(void) correctDestinationPosition
{
[theSuperview correctIt];
}
Now be sure to implement the function correctIt in your superview.
Hopefully i understood your question right...
UIViews have no knowledge of their view controllers. You will need to create a custom UIView subclass that maintains a reference to one (or potentially more than one) view controller, although doing so introduces further coupling between UIView and UIViewController.
You should consider implementing the method in the superview's or view's class rather than implementing it in a view controller.
Here is another way:
SuperviewsViewController *controller = self.superview.nextResponder;
if (controller && [controller isKindOfClass:[SuperviewsViewController class]])
{
[controller method];
}
It should work in most cases.
Apple's UIResponder Reference