In my parent viewcontroller, I have a view container, I added a subview to it:
m_cardDetail = [[CardDetailView alloc] init];
[_m_viewContainer addSubview:m_cardDetail];
[m_cardDetail initialize];
But how can I let my parent viewcontroller process a button click (button resides in m_cardDetail subview). I tried setting the button tag property of the button to 1010 and using this code in parent viewcontroller:
UIButton *aButtonView = (UIButton *)[m_cardDetail viewWithTag:1010];
[aButtonView addTarget:self action:#selector(aButtonTapped:) forControlEvent:UIControlEventTouchUpInside];
and also added this in viewcontroller:
- (void) aButtonTapped:(UIButton *) sender {
//Do something
}
but im getting the Unrecognized selector error
You should treat another view controller's views as private, even one that is a child view controller. As #Ostanik suggests in his answer (voted), setting up a protocol and a delegate is a good way to make a connection between a parent and child.
Note that an embed segue is a very clean way to set up the parent/child link. You simply create a container view in IB and control-drag from the container view to the scene of the view controller that you want to be a child, and Xcode does the rest. When the parent view controller is loaded the embed segue is invoked and you can set up the delegate in your prepareForSegue method.
Related
This is puzzling me.
The context
The original tutorial I'm following.
Where the segue is added to the Main View via a custom segue:
- (void) perform {
MainViewController *source = (MainViewController *)self.sourceViewController;
UIViewController *destination = (UIViewController *) self.destinationViewController;
for(UIView *view in source.main.subviews){
[view removeFromSuperview];
}
source.currentViewController = destination;
destination.view.frame = CGRectMake(0, 0, source.main.frame.size.width, source.main.frame.size.height);
[source.main addSubview:destination.view];
}
The TextField is connected as delegate in the child View Controller. All things being equal I get the app crashed without any message.
The workaround
In the Main View Controller, in -(void)prepareForSegue: I've added [segue.destinationViewController setDelegate:self]; in the meantime I've added a property in the child View Controller id<UITextFieldDelegate> delegate and modified the textfield delegate as self.delegate.
This works, but the trouble is that I've to set the delegated methods in Main View Controller which is not quite efficient as I have more View Controllers to add.
The Objective
How do I set each View Controller to be the delegate for itself without crashing?
The immediate cause of your error is that the view controller that your views belong to is being deallocated. The fact that your views are on screen while their view controller is deallocated highlights a fundamental flaw in the approach of taking views off one view controller and adding them to another. View controller containment is the correct way to solve an issue like this.
Changing the currentViewController property to strong will fix the memory management issue you're seeing, but it's just a bandaid. Your currentViewController will still be missing rotation methods, appearance and disappearance methods, layout methods, and so forth. View controller containment ensures these methods get called for the view controller whose views are on screen.
Here is an altered version of your project that illustrates how to use view controller containment. I think that will be a better solution than manually removing and adding subviews of the view controllers themselves. See the Apple docs for more info on custom view controller containers.
At first, let's see crash report. Please, do the following:
1. Add Exception Breakpoint
2. Edit it as in the picture
You should create a custom class for the destinationViewController wich will implement UITextFieldDelegate
#interface DestinationViewController <UITextFieldDelegate>
#end
And from storyboard add the class to UIViewController that has TextField
And make the connections for elements and TextField delegate.
Implement delegate methods.
You will not need the implementation of prepareForSegue: anymore. You will have two different classes with different elements. Only if you need to pass something from source to destination then you use prepareForSegue:
Hope you'll understand
I am developing an app in which a UIViewController (firstViewController) contains some UILabels, a UIButton, and a UIView (subView). The UIView should display the UIViewController (secondViewController) that contains some layers. I am unable to do this.
What should I do to display secondViewController within subView of firstViewController?
You should use UIViewController containment or parent/child view controllers. You can read details here.
The most basic version is:
UIViewController *parentVC = ...
UIViewController *otherVC = ... // it's view will be added as subview
[parentVC addChildViewController:otherVC];
[parentVC.containerView addSubview:otherVC.view]; // containerView is a view where your child view controller should go
[otherVC didMoveToParentViewController:parentVC];
If you only add other view controller's view as a subview, child view controller won't receive all the events. For example, if you use other methods suggested here (just add view as subview and nothing more), you won't get -viewDidAppear: message (and others) sent to your child view controller.
You can do that by adding view of another view controller as sub view in view as bellow
SecondVC *aObjSecondVC = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondVC"];
[self.view addSubview:aObjSecondVC.view]
You can add it using the following line:
[self.subView addSubView:secondViewController.view];
I am a novice ios programmer and this is my first project.I am going to develop this project specifically for ipad. In this project i need to draw several circles & display data on it by parsing xml element.I have done the the circle drawing part by subclassing UIView class and overriding drawRect method. I load the UIView subclass in a UIViewcontroller via loadview method .Now what i need to do is
Touch a circle and switch to another UIViewcontroller.
I am not sure how to switch to another UIViewcontroller because all of drawing and touch detecting code is in my UIView subclass.
A help will be appreciated
You need to use a delegate method to tell the parent view controller that there was a touch, so it can present another view controller.
At the top of your UIView subclass header, add this:
#protocol MyCustomViewDelegate <NSObject>
- (void)customViewCircleTapped;
#end
Then, in your declaration of the view (the existing declaration you have of your custom view subclass):
#interface MyCustomView : UIView
...
#property (weak) id<MyCustomViewDelegate> delegate;
After that, in your view controller, you need to set view.delegate = self, so the view can reference the view controller.
Then, in your view controller header, change your declaration to look like this:
#interface MyViewController : UIViewController <MyCustomViewDelegate>
then implement customViewCircleTapped in the view controller implementation:
- (void)customViewCircleTapped {
... // Open a view controller or something
}
Once you have done that, in the touch detection code in your view, you can add:
[self.delegate customViewCircleTapped];
What this does is gives your custom view the ability to tell its parent view controller that something has happened, by calling this method (you can change it and add arguments if you need to pass data), and then the view controller can open another view controller or perform some action based on this.
View detects touches → Process touches → call customViewCircleTapped delegate method on view controller → view controller opens another view controller
Provide the subclassed view with a delegate notifying the view is touched and in the delegate call in the mainVC do the push job
The way I like to do this is to send a message up the responder chain. This completely decouples the view from it's enclosing views and view controllers. So, when your circle view is tapped, it emits a "circle view was tapped message" to the responder chain--the first object on the responder chain that responds to that message (which don't know/care which one) will have it invoked. It's simple to implement.
You attach a UITapGestureRecognizer to your view with -tapped: as the action.
On your view, your tap action might look like this.
-(IBAction)tapped:(UIGestureRecognizer*)g
{
[ self sendAction:#selector( circleViewTapped: ) withObject:self ] ;
}
The -sendAction: method on UIResponder is added via a category, like this:
#implementation UIResponder (ActionSending)
-(void)sendAction:(SEL)action withObect:(id)object
{
UIResponder * target = self ;
while( self && ![ target respondsToSelector:action ] )
{
target = [ target nextResponder ] ;
}
[ target performSelector:action withObject:object ] ;
}
#end
Your view controller or any parent view or parent view controller that responds to circleViewTapped: will have that method invoked when your circle view is tapped.
A note about organizing your views:
I would make a circle view UIView subclass. Instantiate your subclass for each circle to be displayed. To each of those attach a UITapGestureRecognizer. The target of your gesture recognizer is the -tapped: method, above.
The current answers are overcomplicating the solution. You don't need gesture recognisers or delegate protocols.
Make your circle drawing view a subclass of UIControl instead of UIView. Then attach your view controller as a target to the UIControlEventTouchUpInside event:
[circleView addTarget:self action:#selector(circleTapped:) forControlEvents:UIControlEventTouchUpInside];
This will call the circleTapped: method on your view controller, with the tapped view as the sender.
iOS 6 introduced the Embed Segue, allowing custom container controllers to be used in Storyboards. Is there anyway to duplicate this for iOS 5?
The challenge here is that the child view controller's view is often to be added as a subview of some container view of the parent view controller. Since you can't have segues from random UIView controls, that defies creating segues from a UIView container view to the child's scene. Thus, you just have to write the code yourself.
Fortunately, it's just those four lines of code referenced in Adding a Child Controller from the View Controller Programming Guide. Personally, I'd might even modify that code slightly, having the following method defined in my view controller:
- (void) displayChildController:(UIViewController*)childController
inContainerView:(UIView *)containerView
{
[self addChildViewController:childController]; // 1
childController.view.frame = containerView.bounds; // 2
[containerView addSubview:childController.view];
[childController didMoveToParentViewController:self]; // 3
}
I have, though, done custom segues for changing the active child controller from one scene to the next, but it essentially just a variation of the code listed later in the above referenced document. But that's not an embed segue question, so that's not relevant here
I duplicated the functionality by subclassing UIStoryboardSegue.
In Interface Builder, I create a custom segue and set its' class to my subclass (QCEmbedSegue). In my parent view controller's viewDidLoad, I call performSegueWithIdentifier:sender.
QCEmbedSegue simply overrides perform:
- (void)perform
{
[self.sourceViewController addChildViewController:self.destinationViewController];
[[self.sourceViewController view] addSubview:[self.destinationViewController view]];
[self.destinationViewController didMoveToParentViewController:self.sourceViewController];
}
http://www.quentamia.com/blog/embed-segue-in-ios-5/
Hy
i have two classes uiviewcontroller and uiview. I have one view controller. Inside i have uiview. Inside uiview i have textfield and when i write a text and click done i need to refresh uiviewcontroller.
I tried with this in uiview class:
-(IBAction)textFieldReturn:(id)sender
{
ViewController *vc = [[ViewController alloc] init];
[vc viewDidLoad];
}
i need refresh the same as you click the button and open viewcontroller.
I am guessing you mean that you want to "refresh" the view, not the view controller. To do that simply call [self setNeedsDisplay] from the view, or [self.view setNeedsDisplay] from the view controller. Also make sure that the textfield is a subview of the uiview. Either do that in the nib file or in code by calling [self addSubview: (textfield here)].
Also, if you want to access the view controller from the view you will need to create an IBOutlet, simply allocating a new ViewController object within the view does not mean that the created view controller controls the view. Hopefully that makes sense. I'd recommend going through some ios starter tutorials as well. Just google that there are a lot.