How can I use an Embed Segue in iOS 5? - ios

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/

Related

Proper way to dismiss multiple UIViews?

I have a UIViewController (HomeView) that shows my UIView called GameView via a Segue.
[self performSegueWithIdentifier: #"segue_playgame" sender: self];
The GameView calls a UIView (PauseView) when the use presses a button. This pause view is shown via just adding the PauseView to the UIView.
UIView *pv = [[PauseView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:pv];
To remove the PauseView I call
[pv removeFromSuperview];
Is there a way to call an "End Game" method in the PauseView that will remove both the PauseView and the GameView taking the user back the HomeView (UIViewController)?
And side note, is there a better way to handle showing views and removing them? Or is how I am doing it pretty much standard?
What you are missing here is a UINavigationController. From the official documentation:
The UINavigationController class implements a specialized view
controller that manages the navigation of hierarchical content. This
navigation interface makes it possible to present your data
efficiently and makes it easier for the user to navigate that content.
You generally use this class as-is but you may also subclass to
customize the class behavior.
With your views managed by the UINavigationController stack you can use:
- (NSArray<__kindofUIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated
To pop back to the root view controller which in your case is your home view controller.

Access contained UIView component from Parent ViewController

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.

Create instance of a UIView - iOS

I am designing an iOS app with 10 main UIViewControllers in it. Each representing a different section of the app. Its basically for a company and shows info about the company.
One of the things I am doing on bottom section of the app (in all the different view controllers) is displaying a UIView which contains a map. This map shows a certain location.
Now it works, but the problem I have is that I have 10 copies of the same code and 10 copies of the same UIView.
Is there anyway I could make a small view controller with one class attached to it that would handle the map and then just create an instance of the view controller in all my 10 view controllers in my app?
I hope my question makes sense. Basically I want to know how I can go about reusing ONE UIView in all 10 of my ViewControllers. So I can just call it or something and it appears.
Update - this is basically what I am trying to achieve
Thanks, Dan.
View controllers can contain other view controllers. You can either use a container view in a storyboard or setup the relationship programmatically (see: Creating Custom Container View Controllers).
The storyboard container view is easiest, but the programmatic solution isn't too bad.
- (void)displayContentController:(UIViewController *)content
{
[self addChildViewController:content];
content.view.frame = [self frameForContentController];
// NOTE: You could also add it to any subview of self.view.
[self.view addSubview:content.view];
[content didMoveToParentViewController:self];
}
- (CGRect)frameForContentController
{
return CGRectMake(…);
}
- (void)viewDidLoad
{
…
MyMapViewController *mapViewController = …;
[self displayContentController:mapViewController];
…
}
- (void)dismissContentController:(UIViewController *)content
{
[content willMoveToParentViewController:nil];
[content.view removeFromSuperview];
[content removeFromParentViewController];
}
Final Note: Have each parent view create its own instance of the map view controller. Resist the temptation to reuse an instance of the map view controller between parents.
Update to address questions
So lets say I had 2 of the same view controllers open at once and they both were displaying the same imported viewcontroller then it wouldn't work right?
You can't do this. An instance of a view controller can only have 1 parent view controller. Create separate instances for each use.
So if I create different instances, I can reuse the same view lets say 5 times in one view?
Yes, if you create different instances, you can put as many as you need on a view.
Let me be clear, an instance is a distinct memory location created using a constructor.
MyMapViewController *mapViewController1 = [[MyMapViewController alloc] initWithNibName:#"MyMapViewController" bundle:nil];
MyMapViewController *mapViewController2 = [[MyMapViewController alloc] initWithNibName:#"MyMapViewController" bundle:nil];
or
MyMapViewController *mapViewController1 = [self.storyboard instantiateViewControllerWithIdentifier:#"MapViewController"];
MyMapViewController *mapViewController2 = [self.storyboard instantiateViewControllerWithIdentifier:#"MapViewController"];
Updated to demonstrate dismissing a container view controller.
Here is a method for a child view controller, so it can use to dismiss itself.
- (void)dismissFromParentViewController
{
[self willMoveToParentViewController:nil];
[self.view removeFromSuperview];
[self removeFromParentViewController];
}
Please try below method:
Create "map controller" super class with inherit to UIViewController and define your need common method and variables.
Inherit your 10 child class into "map controller" super class. And connect common IBOutlets and IBActions to super class.
You can access common methods and variable to super class from child class(10 view controller child class).
Please refer below code
#interface mapController : UIViewController
{
NSString *mapControllerVariables;
}
-(IBAction)mapControllerActions:(id)sender;
#end
#interface yourChileView : mapController
{
}
#end

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

ViewDidLoad not called on custom objects in Storyboard

I finally made the switch to Storyboards and i am having issues loading custom controllers which was pretty easy to do when using interface builder.
I have ViewControllerOne with two components: A UIView and UITableView as the subview.
I want the UITableView to be controlled by a custom tableview controller. If this was Interface builder i would have dropped a tableview controller onto the XIB, linked to the custom controller and made the connections and it would have been done.
Using storyboard, i don’t believe its possible to drop a UIViewController/UITableViewController onto a scene which already has a view controller, i relied on Objects to achieve this.
So i added a Object onto the scene and linked it to my custom tableview controller. I set up delegate/date source for my UITableView to point to the custom controller. I finally connected the UITableViews outlet to the custom controller.
When i compile this, the custom controllers delegate (for the table view) gets called but the viewDidLoad is never called.
The only way i can invoke viewDidLoad is if i move the UITableView out of ViewControllerOne. My understanding was that even though there is one view controller for a scene i can still manipulate the subviews using custom controllers.
Am i misunderstanding something or is there is a solution for this ?
Some screenshots
There is a bit of magic in that. Call self.view from awakeFromNib and flow will back to the rails
- (void)awakeFromNib
{
[super awakeFromNib];
// here comes the magic - call self.view and view will load as expected
NSLog(#"awakeFromNib %#", self.view)
}
you can call it from initWithNibName:bundle:
- (id)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
NSLog(#"awakeFromNib %#", self.view);
}
return self;
}
the point is to call self.view because apparently something is done inside.
If I have understood your question correctly:
1 Open the storyboard and navigate to the table view controller that you would like to be of your custom type.
2 Click on the identity inspector in the right hand side panel.
3 Set the class to what it should be.

Resources