Update IBOutlet on Main View from Sub View - ios

I have a main view which has a UISlider on it.
From the main view I add a subview using:
gameView *myViewController = [[gameView alloc] initWithNibName:#"gameView" bundle:nil];
[self.view addSubview:myViewController.view];
The subview is created on top of the main view.
When I remove the sub view using:
[self.view removeFromSuperview];
the main view underneath becomes visible.
I want to be able to update the value of the UISlider on the main view, from the sub view, before I call [self.view removeFromSuperview]
Is it possible?
Basically the question can be generalized to how to update an IBOutlet on the main view from the sub view.
Help is greatly appreciated.
Many thanks!

Yes, it's possible.
And there's a few ways to do this. Here's how I would do it:
First, make your parent view controller's UISlider a property that can be accessed by other objects.
Secondly, give your gameView object an instance variable that you'll link to the parent view (let's call it id savedParent;)
Then, before you do removeFromSuperview, you can simply do something like:
ParentViewController * parentVC = (ParentViewController *) savedParent;
if(parentVC)
{
// some float value of whatever you want to set the slider value to
parentVC.slider.value = 0.5f;
}
Also, why are you instantiating a whole View Controller object (gameView) if you simply want to add a subview? When you do your removeFromSubview call, the view gets removed but your gameView view controller isn't released (and might even be getting lost & leaked in memory, leading to a crash). If you want to do a subview, subclass UIView. If you want to push a new view controller, push the whole controller (and not just the view it contains).

Here is another way:
I'm not sure what the slider is representing, but you need to create an object that represents this
#interface MyGameThing : NSObject
#property (assign) CGFloat myValue;
#end
#implementation MyGameThing {
CGFloat *_value;
}
#synthesize myValue = _myValue;
#end
You then need to pass that object to both of your view controllers (or make it a singleton).
Then, on ParentViewController, in the viewWillAppear, just set the slider to the new value.
Daniel.
(p.s. don't just add view controllers views to the superview, use presentModalViewController / dismissModalViewController or a navigation controller).

Related

setting Delegates on destination viewController

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

Memory not released after removing subviews (ARC)

I am trying to solve "Received Memory Warning" issue.
My app has 2 view controllers and when you click a button on first view controller,
detail view controller appears.
The detail view controller has a view inherited of UIView called 'topView' and the view has many subviews.
The subviews are also inherited of UIView and each subview has 2 UILabels.
My question is when you go back to first view controller by clicking back button,
'topView' is not released, even if I put the following code in viewDidDisappear.
How can I release the memory of topView?
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[[self.topView subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
self.topView= nil;
}
Am I missing some thing?
I think I'm heading to some wrong direction, so please give me advice.
I'm not sure, but here's a thought. When you call this line:
[[self.topView subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
It removes all of the subviews which declare self.topView as a superview. The next line:
self.topView = nil
Doesn't remove the top view itself, but rather nil's your pointer to it. Because views are retained by their superviews, this object will stick in memory until the view controller is removed and its view is released.
Check your variable declarations (weak, strong) to be sure that the topView object hasn't retain somewhere else.
Such as IBOutlet's usually declared with a Weak Key:
#property (weak, nonatomic) IBOutlet UIView * topView;

Clearing memory when removing a viewController

I have this view controller (class1) , which has a UICollectionView in it .
When i am finish with this view, i am going to the next view, but i can see that the memory consumption of this view(class1) is not cleared and being added to the next view (class2).
Both view controllers are made with storyboard, and has a name , and when i finish with view1 (class1) i am going to the next one with :
//in view1 i do when exit
UIViewController *mainV=[self.storyboard instantiateViewControllerWithIdentifier:#"MainView"];
mainV.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:mainV animated:YES completion:^(void)
{
[self.myCache removeAllObjects];//NSCache
[self.GridView removeFromSuperview]; //collection view
[self.view removeFromSuperview];
}];
Seems that memory is still not freed.
Is there another way to move to next view and just clear everything before ?
In that code class1 is presenting class2. But class1 is still there, presenting class2, until it does a "dismissViewController:" method.
If you want to go back to mainView from class1. And in the case of class1 being presented by mainView. Then mainView have to do a dismissViewController:
In other case you are stacking a pile of view controllers, one presenting the next one.
The pattern most used in objetive-c is to send a message from class1 to its presenter. A delegation pattern. The presenter then dismiss the viewController.
Even after grid view is removed from superview, it won't be cleared from memory if it's an iVar of your view controller class or a strong property. Try setting GridView to nil after you call [self.GridView removeFromSuperview].

Overriding drawRect method in a UIView subclass & call it into another UIViewcontroller

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.

UIView: adding UIViewController's view as a subview and its removing

I would like to ask what is the correct way to add and remove UIViewController's view as a child view.
So, having UIViewController initialized I can add its view to view hierarchy as follows:
UIViewController *myViewControler = [[UIViewController alloc] init];
[someAnotherView addSubview:myViewController.view];
Question 1: Should I release myViewController.view after addSubview: call?
If I want to remove myViewController's view from view hierarchy I call [myViewController.view removeFromSuperview];
Question 2: How should I release myViewController instance in this case after its view removedFromSuperview?
You do not need to release the view, the owning view controller will do this for you.
I normally put the declaration of myViewController in the header and then release and nil it when I am done with it (either somewhere in the normal flow or in the dealloc of the containing view controller).

Resources