I am attempting to accomplish something and i was looking for a few tips. I started working on a project that consisted of one xib file. Later on i upgraded to xcode 5 and pretty soon for the rest of my interface files i began using my storyboard however i kept my old xib (mainly cause of how i pass in methods to it) and simply called it like :
mapViewController *mapView=[[mapViewController alloc]initWithNibName:#"mapViewController" bundle:nil location:0 option:#"poe"];
Recently I have added new functionality and upon clicking a callout I would like to prepare a segue for a new view to be displayed.
Firstly in the xib interface. I was wondering if I could from my mapView (not in storyboard) prepare and call a view/segue(in story board) .
Or if I can simply drop or copy and paste my mapView into my storyboard and then draw my segue connectors to my new view. As I dont think it is necessarily for my to add the mapView to my "story line" however I still want to call is using the method posted above.
However if none of these are feasible please suggest another method.
Thanks
UPDATE
I have moved my xib to my storyboard interface however i am facing a little problem
When i called my map from the xib it would zoom in to a specific location supplied in the init method. I am passing in the same parameter but i am having a little trouble recreating my old results in my storyboard
if ( ([receivedRainObject isEqualToString:#"Natural"])) {
// mapViewController *mapView=[[mapViewController //alloc]initWithNibName:#"mapViewController" bundle:nil location:0 option:#"na"];
// self.navigationItem setHidesBackButton:NO animated:NO];
// [self.navigationController setNavigationBarHidden:NO animated:YES];
// [self.navigationController pushViewController:mapView animated:NO];
[self performSegueWithIdentifier:#"countryMap" sender:self];
}
Then in preparing the srgue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"countryMap"]) {
mapViewController *transferViewController=segue.destinationViewController;
transferViewController.location=0;
transferViewController.option=#"na";
}
}
Then in view did load of the map
location does evaulate to 0 and the breakpoint shows me that all this code is being stepped thru but the region of the coordinates i supplied isnt what is being shown.( the the span or region)
(however when i call my old xib it works fine)
- (void)viewDidLoad
{
[super viewDidLoad];
{
// Do any additional setup after loading the view from its nib.
if (location==0)
{
CLLocationCoordinate2D location1;
location1.latitude=(double)15.435786;
location1.longitude=(double)-61.318447;
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta=0.50;
span.longitudeDelta=0.40;
region.span=span;
region.center= location1;
[mapView setRegion:region animated:TRUE];
[mapView regionThatFits:region];
}
}
INIT of my mapview
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil location:(int)num option:(NSString *)row
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.location=num;
self.option=row;
}
return self;
}
In your mapView, you should be able to do something like this in the callout method:
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"YourStoryBoardName" bundle:[NSBundle mainBundle]];
YourViewControllerFromStoryBoard *viewController = [sb instantiateViewControllerWithIdentifier:#"YourViewControllerIdentifierInYourStoryBoard"];
Then in your mapView you can override prepareForSegue to do any prep work and then present your viewController.
You can't call a segue between a xib defined controller and a storyboard defined one (or between controllers in two different storyboards either). You can instantiate the controller in the storyboard using the UIStoryboard method, instantiateViewControllerWithIdentifier:, and then push or present it in code. It's not clear from your question why you don't want to move the xib based controller into your "story line". You can create a new controller there, and copy and paste the view from your xib file if you want to go that route.
Related
In my app I've got a UINavigationController.
In some of the pages I got a MKMapView as the back view (allows changes to map view the overlay UIView UI, so I can't make it as a image).
In some combinations, It's possible to have 3 or so MKMapView alloc'd.
The problem is that each MKMapView takes nearly 60MB, so the memory jumps up to 180+ MB only from the maps. not to mention if the user opens more ViewControllers with map, he'll get a OutOfMemoryException.
All the maps have the same content, I tried moving a map from ViewController to another, but according to another post in StackOverflow, moving UIView from ViewController to another is against MVC, not to mention it removes it from the caller ViewController.
Basically since all the maps contains the same content, and I want them the contain all the annotations it'd be best to just move the map but I had hard time with that.
I've got a solution out of the box which is to remove the MKMapView from its superView and on viewWillAppear realloc it, but this doesn't seem like the best idea.
I don't see any problem with passing a pointer to a single map view when you move from controller to controller. Unless you're viewing more than one map at a time, I don't see why there should be more that one instance. Remove the view in viewWillDisappear, and pass a pointer to the map in prepareForSegue:
- (void)viewDidLoad {
[super viewDidLoad];
if (! self.mapView) {
self.mapView = [[MKMapView alloc] initWithFrame:self.view.frame];
}
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.view addSubview:self.mapView];
[self.view sendSubviewToBack:self.mapView];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.mapView removeFromSuperview];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
SecondViewController *secVC = segue.destinationViewController;
secVC.mapView = self.mapView;
}
Subsequent controllers could have the same code in viewWillAppear, viewWillDisappear, and prepareForSegue (except for what controller is the destinationViewController). The map only needs to be instantiated once in the viewDidLoad method of the first controller. An even simpler implementation would be to have all the subsequent view controllers inherit from the first controller. If you do it that way, you won't need to put anything in viewWillAppear or viewWillDisappear. You only need to override prepareForSegue.
I have an application where I have a ViewController in my storyboard file. I have added a button to this viewController that should call another ViewController that is outside of the storyboard and has with it, it's own .xib file. However, when I press this button, the method is being called, but the new screen is not being loaded, and I don't know why.
Here is the relevant code that I have below:
- (IBAction)showTireCount:(id)sender {
NSLog(#"do i get called?");
SecondViewController *secondView = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[self.navigationController pushViewController:secondView animated:YES];
}
I am able to see the text "do i get called" appear in the system output, but unfortunately nothing else occurs. Can anyone see what it is I am doing wrong?
self.navigationController is nil. Did you embed your view controller (the one with the button) in a UINavigationController?
Make sure you are having the below statement inside SecondViewController initialize method
[super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
I'm pushing to a new view controller and passing some data to it. When I run the application I can press the button and push to a new view but the screen is completely black. Any help is appreciated.
- (IBAction)button:(id)sender {
NSString *firstField = self.field.text;
NSString *secondField = self.field2.text;
self.resultsArray = [[NSArray alloc] initWithObjects:firstField, secondField, nil];
NSUInteger randomResult = arc4random_uniform(self.resultsArray.count);
self.label.text = [self.resultsArray objectAtIndex:randomResult];
ImagesViewController *ivc = [[ImagesViewController alloc] init];
ivc.label = self.label.text;
[self.navigationController pushViewController:ivc animated:YES];
}
When you're using a storyboard, and you want to push a view controller in code (rather than with a segue), you need to give the controller an identifier, and create it like this:
ImagesViewController *ivc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyIdentifier"];
ivc.label = self.label.text;
[self.navigationController pushViewController:ivc animated:YES];
The view controller you are pushing is not having any frame dimension set.It is always recommended to call designated init for objects. For view controllers, designated init method is
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
if you have a xib assign it like
ImagesViewController *ivc = [[ImagesViewController alloc] initWithNibName:<your xib> bundle:[NSBundle mainbundle];
if you are using custom view, assign a frame dimension and add it as subview
Xcode 7.3 and Swift 2.2
In my case, I had made changes in the storyboard and made it a TabBarController and accordingly changed the class of the controller from UIViewController to UITabBarController. After some tweaking, this change wasn't favourable and I un did all the changes and got a black screen then because I had forgotten to change the class of the controller. I changed it back to UIViewController and it started working again.
So check if you have made the same mistake. The black screen came because the storyboard had a class(UIView/UITabBar/UITableView Controller) but that wasnt the same in code.
This can also happen if you have somehow got an incorrect connection between one of the subviews in the storyboard to the controller's view. Check the Referencing Outlets are correct in each of your subviews.
I got a good one:
Make sure you are implementing the right Super class, delegate, etc.. in the top part of the viewController you are trying to present. i.e.
I wasn't using/implementing UINavigationController at all
class TMDetailBlogViewController: UINavigationController {
//code goes here
}
After
class TMDetailBlogViewController: UIViewController {
//code goes here
}
Typically, you transition to another view controller by calling:
initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
on your custom UIViewController.
If you're not using a xib file, then what you're doing may be fine. Are you dynamically creating your UI elements within the constructor of your ImagesViewController?
I was trying without using storyboard, and its just that the default screen it uses is in black color. I changed the background color to white and it worked.
Pushed the controller this way-
NextController *nextController = [[NextController alloc]init];
[self.navigationController pushViewController:nextController animated:YES];
In NextController-
(void)viewDidLoad{
[self.view setBackgroundColor:[UIColor whiteColor]];
}
I'm pretty new to iOS dev, and I've been reading a ton on this problem, but still can't figure it out.
I have a button in an action sheet that is supposed to activate and present a slide-up modal that is a from/to date picking screen (it has its own controller DatePickerViewController. The action sheet is triggered by a button in the toolbar of a NavigationViewController's subview ("Map of Shows" view, top left button). Graphic shows the current storyboard relationships:
The code for this sequence looks like this:
// ShowsContainerController.m
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if( buttonIndex == 1 ){
// 1. Activates from actionsheet
[(NavigationViewController *)self.parentViewController showDateSelect];
}
}
// NavigationViewController.m
// 2. fires up the datepicker view
-(void)showDateSelect
{
pickerView = [[DatePickerViewController alloc] init ];
[self presentViewController:pickerView animated:YES completion:nil];
}
// DatePickerViewController.m
// 3. Instantiation of this controller. Definitely fires nslog.
-(void)viewDidLoad
{
NSLog(#"Here");
}
Once "here", the screen just goes black. I assume this is because I'm not doing something right either with the instantiation of the date picker controller, or with the segue to it. All of the views in question are associated with their respective controllers in the storyboard configs. To further confuse the issue, I have a UITableViewController that I created for another screen, and just for shits and giggles, attempted to load that, and it worked fine. I then created another completely separate UIViewController, pointed it back to the controller files that control the current non-working one, and it bombs as well, so I'm thinking the issue are the header and main files for the non-working UIViewController. ed. scratch that last note; I created a completely new header, main file and NIB for the view and it still didn't work. I don't know what the hell the deal is.
Any and all help would be greatly appreciated.
Addendum
- (IBAction)showOptions:(id)sender
{
NSString *showTypes;
if( self.onlyShowPreferredEvents ){
showTypes = #"\u2713 Only shows that I'll like";
} else {
showTypes = #"Only shows that I'll like";
}
_showDisplayOptionsActionSheet = [[UIActionSheet alloc] initWithTitle:#"Event Display Settings" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles: showTypes, #"Date Range", nil];
[_showDisplayOptionsActionSheet showFromTabBar:self.tabBarController.tabBar];
}
Per the comments.
Addendum 2
// All of DatePickerViewController.m
#import "DateRangeViewController.h"
#interface DateRangeViewController ()
#end
#implementation DateRangeViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
// All of DatePickerViewController.h
#import <UIKit/UIKit.h>
#interface DateRangeViewController : UIViewController
#end
I would disconnect the DatePickerViewController from the Nav controller and leave it as a stand alone VC.
Then get rid of showDateSelect and change clickedButtonAtIndex method to the following:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if( buttonIndex == 1 )
{
pickerView = [[DatePickerViewController alloc] init ];
[self.navigationController pushViewController:pickerView animated:YES];
}
}
Here you are pushing the pickerView unto the Navigation controller stack. You will need to pop it to remove it.
you can also experiment with present/dismiss but I am not sure whether it would work on Nav Controller stack.
Update:
I am not sure why you are re-declaring the viewDidLoad in your DatePickerViewController.h file. Take that out.
Also in all view Did/Will Load/Appear methods, you need to start by calling super. So insert [super viewDidLoad] as a first line in your viewDidLoad method of DatePickerViewController.m
Figured it out. The problem is that when you're using a storyboard, like I am, and you're programmatically presenting a view, you need to begin by creating a storyboard object:
// "MainStoryboard_iPhone" is your .storyboard file's name
// [NSBundle mainBundle] returns the main storyboard bundle.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone"
bundle:[NSBundle mainBundle]];
Next, you instantiate the view that you want to present using that storyboard object:
// Give your view an identifier in the storyboard. That's how the storyboard object will find it.
// You should see it in the right panel options when you click on the view.
UIViewController *dateRangeController = [storyboard instantiateViewControllerWithIdentifier:#"dateRange"];
Present.
[self presentViewController:dateRangeController animated:YES completion:nil];
My mistake was in thinking that if you simply associated the view with the viewcontroller file (under the Class property in the config pane), that's all that was needed. But if you're using a storyboard, the UI process is expecting views to be instantiated through it, whether automagically or programmatically (presumably because more than one view can use the same view controller).
I am trying to segue to a ABPersonViewController from a button in the Storyboard.
But if I do that the screen is completely black.
If I use a IBAction for the button and use the following code it works:
ABPersonViewController *person = [[ABPersonViewController alloc]init];
[self.navigationController pushViewController:person animated:YES];
Why is that? am I doing wrong?
EDIT: I found a work around but I don't think this is a proper way to do it. I subclassed ABPersonViewController and overrode the initWithCoder method with the following:
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [self initWithNibName:nil bundle:nil];
return self;
}
Since you are using storyboards you should should name the segue you are using and then this in your IBAction:
[self performSegueWithIdentifier:#"abPersonViewControllerSegue" sender:self];
This way you do not even need to manually call alloc/init. Then in your prepareForSegue you could set any attributes:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"abPersonViewControllerSegue"])
{
[segue.destinationViewController setAttribute:#"whatever you want"];
...
}
}
If this is not what you are looking for please let me know.
You don't set the displayedPerson property ...
#property(nonatomic, readwrite) ABRecordRef displayedPerson
Since you are using Storyboard, why do you still call [[ABPersonViewController alloc]init]?The Storyboard will handle the creation and pushing of the ABPersonViewController(if you specified Push action for the segue). As long as you control dragged from the Button to the ABPersonViewController in story board, you do not need to write a single line of code. If you control dragged from the view controller that contains the button, then you should be calling performSegue to trigger the segue(you need to set the identifier for the segue in this case) that adds the ABPersonViewController to the navigation controller.