I'm still relatively new to iPhone development but I know this warning is usually the result of not declaring a method in my classes header file. This is slightly different - at least I think it is.
I've created a custom tab bar in my applications root view controller which loads in the other view controllers dynamically inside the delegate method - essentially like this:
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
UIViewController *viewController = [viewControllers objectAtIndex:item.tag];
[self.selectedViewController.view removeFromSuperview];
[self.view insertSubview:viewController.view atIndex:0];
self.selectedViewController = viewController;
}
That code works fine and loads in the required views. When the view changes a check is run to see if the setting view is about to be unloaded and if so it calls the save settings method like this:
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
if (self.currentController == 1) {
[self.selectedViewController saveSettings];
}
UIViewController *viewController = [viewControllers objectAtIndex:item.tag];
[self.selectedViewController.view removeFromSuperview];
[self.view insertSubview:viewController.view atIndex:0];
self.selectedViewController = viewController;
}
Again the code functions fine and the instance method of SettingsViewController is called, but because the declaration of the method is in the header of the SettingsViewController and not the RootViewController hence the warning.
If I declare it in the RootViewController as well I get the 'no matching method declaration' warning. I presume redeclaring my function would fix the warning - but surely isn't the 'proper' way to fix this.
If I understand your question correctly, this should work:
if (self.currentController == 1) {
[(SettingsViewController *)self.selectedViewController saveSettings];
}
Related
I was trying to push a viewcontroller B into navigation controller from A and then assigning some properties of B in A.
In this case, the assigning of properties was done and then viewDidLoad of viewcontroller A was executed.
Here, assigning properties in A should be done only after viewDidLoad of A has done.
For example,
[b.navController pushViewController:a animated:YES];
a.status = #"loaded";
Here, status was assigned first and then viewDidLoad of A was executed.
This happens only in iOS 7 whereas in iOS6 it works fine.
Can anyone please let me know where the problem is?
UPDATE: For me in some cases in iOS7, Push view is not working. How cna I debug and fix it?
Just access the viewcontroller.view (set any thing immediately after the alloc) property after the alloc init;
Which will loadview/viewdidload.
See Apple Documentation
In my experience, a UIViewController view is loaded lazily, no matter which iOS version you're working on. If you want to trigger a view load, and therefore its UIViewController viewDidLoad, you should access the view after the UIViewController is allocated. For example:
UIViewController *aViewController = [[CustomViewController alloc] init];
[aViewController view];
Make sure you don't code it as
aViewController.view
since that would generate a compiler warning.
So, in your case it would have to be something like this:
...
CustomViewController *a = [[CustomViewController alloc] init];
[b.navController pushViewController:a animated:YES];
[a view];
a.status = #"loaded";
Let me know if you have further problems with it.
You can know when a View Controller has been pushed onto the stack by implementing the UINavigationControllerDelegate and setting yourself as the delegate self.navigationController.delegate = self; then you will get this callback after every push
navigationController:didShowViewController:animated:
So you can check if the shown viewController is the one your interested in, then set your a.status.
I would suggest you call a delegate method once the view is loaded.
Set the delegate to be controller B.
and after viewDidLoad finishes (in controller A) call the delegate method. You can even pass parameters as you wish to the delegate.
Here's some example code:
Controller B:
a.delegate = self;
[b.navigationController pushViewController:a animated:YES];
Implement the delegate method:
- (void)controllerIsLoaded:(ControllerA *)controllerA status:(NSString *)status
{
a.status = status;
}
Controller A .h file:
#class ControllerA;
#protocol ControllerADelegate <NSObject>
- (void)controllerIsLoaded:(ControllerA *)controllerA status:(NSString *)status;
#end
#interface ControllerA : UIViewController
#property (nonatomic, weak) id <ControllerADelegate> delegate;
Controller A .m file:
- (void)viewDidLoad:(BOOL)animated
{
[super viewDidLoad:animated];
/* your viewDidLoad code here... */
if ([_delegate respondsToSelector:#selector(controllerIsLoaded:status)])
[_delegate controllerIsLoaded:self status:#"Loaded"];
}
Turn off animation for ios7, in my case its work
[b.navController pushViewController:a animated:NO];
a.status = #"loaded";
No documentation provides enough information to know exactly when viewDidLoad would be called.
UIViewController's documentation just says this
This method is called after the view controller has loaded its view hierarchy into memory
I would suggest that you create a custom initializer like this
- (id)initWithStatus:(NSString *)status {
}
But, if you are trying to use this variable to check if the viewController's view has 'loaded' or not, it may not be possible to do that because the pushViewController or presentViewController methods are not guaranteed to be synchronous.
Even in iOS 6, there was no explicit guarantee that the view would be loaded as soon as that method returned.
Please write the code in viewWillAppear method instead of viewDidLoad in next class i.e. where you are pushing the object to
-(void)viewWillAppear:(BOOL)animated
{
}
I'm understand of your question like this:
B *b = [[B alloc] init];
b.status = #"loaded";
[self.navigationController pushViewController:b animated:Yes];
If you want to pass a value from one controller to another means, you have to assign a value before using pushViewController method.
I have two view controllers (FirstViewController and SecondViewController) and a Tab Bar Controller and I'm using Storyboards. In the FirstViewController user can drag and drop an imageview. So every time a user clicks on the second TabBarItem which displays the SecondViewController I would like to check if the user has dropped the image or not every time she clicks the TabBarItem.
So I understand that this can be done with UITabBarDelegate and with its method -(void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item. But I'm doing something wrong because the method isn't called and I believe this is because I can't set the delegate properly. So I want the SecondViewController to be the delegate for TabBarController.
So in my SecondViewController.h I have the following
#interface SecondViewController : UIViewController<UITabBarDelegate>
And in SecondViewController.m I have
-(void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
NSLog(#"%#", item);
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tabBarController.delegate = self;
}
But nothing happens and when setting the delegate I also get a compiler warning: Assigning to 'id' from incompatible type 'SecondViewController *const __strong'
Please be gentle with me, this is my first app and the first time I'm trying to use delegates.
Add the following code to any of the view controllers
UITabBarController *tabBarController = (UITabBarController*)[UIApplication sharedApplication].keyWindow.rootViewController ;
[tabBarController setDelegate:self];
// add any delegates methods to your class
-(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
NSLog(#"%#", tabBarController);
}
-(void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item;
This method is a delegate method for UITabBar, not UITabBarController, so
self.tabBarController.delegate = self;
will not work.
Tab bar controller has its own UITabBar, but changing the delegate of a tab bar managed by a tab bar controller is not allowed, so just try UITabBarControllerDelegate method like this:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
NSLog(#"%#", item);
}
For more detail check info
Thanks
I imported and implemented the following. Hope it helps.
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
if (_mainTab.selectedItem.tag == 1) {
NSLog(#"TAB 1");
}
else if (_mainTab.selectedItem.tag == 2) {
NSLog(#"TAB2");
}
else if (_mainTab.selectedItem.tag == 3)
{
NSLog(#"TAB3");
}
else
{
NSLog(#"TAB NOT WORKING");
}
}
You are using the wrong delegate protocol UITabBarDelegate is usually used for customizing the UITabBar objects. You need to use UITabBarControllerDelegate protocol in order to check if a tab is selected or customize the behavior of tabs.
You should implement UITabBarControllerDelegate protocol instead and use this delegates callback to track selection:
tabBarController:didSelectViewController:
Next thing is, that you should initialize delegate before it will be called. ViewDidLoad will be called after tabbarcontroller will try to talk to delegate.
In order to get rid of the compiler warning your SecondViewController should conform to the UITabBarControllerDelegate protocol instead of the UITabBarDelegate protocol.
#interface SecondViewController : UIViewController<UITabBarControllerDelegate>
I am having an issue with trying to load a viewcontroller onto another viewcontroller as a subview.
what I have is a NavigationController that loads some viewControllers in as views (pop and push etc) that works perfectly. then I have decided to put a tabBar into a viewController which then looks after all of the selection stuff using a switch statement, this switch statement then calls a method inside one of the viewControllers that appears inside the navigationController.
The method inside this viewController then trys to set another viewcontroller as a subview to the viewcontroller thats inside the navgiation controller.
this is my code.
TabBarViewController.m
#import "DetailViewController.h"
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
switch (item.tag) {
case 0:
{
NSLog(#"item 1 selected");
DetailViewController *dVC = [[DetailViewController alloc] init];
[dVC tabBarSelectedAction];
}
break;
default:
break;
}
}
so this catches the selected item on the tab bar... then fires off a msg to the DetailViewController method to load the new subview onto DetailViewController.view
- (void)tabBarSelectedAction
{
ButtonOneViewController *b1VC = [[ButtonOneViewController alloc] initWithNibName:#"ButtonOneViewController" bundle:[NSBundle mainBundle]];
[self.testView addSubview:b1VC.view];
}
and this is where I am trying to load the subview onto the screen.. I think I am doing it right but for some reason its not displaying.. another thing I would like to do is animate this view from the bottom of the screen up..
any help would be hugely appreciated.
When you created your new DetailViewController you didn't make it part of the view hierarchy through a push or present type of method. Adding a subview may or may not be working but you won't see it because the object you're adding it to isn't using the screen.
Your method should probably look like this. Assuming self DetailViewController.
- (void)tabBarSelectedAction {
ButtonOneViewController *b1VC = [[ButtonOneViewController alloc] initWithNibName:#"ButtonOneViewController" bundle:[NSBundle mainBundle]];
[self presentModalViewController:b1VC animated:YES];
}
Even with that, I think your logic is a little screwed up. You allocate and initialize DetailViewController but you never present it anywhere. So how are you expecting to see a modal view in DetailViewController, if you never present it.
EDIT: Taking into consideration your comment of adding it to the UINavigationController, you would change it to look something like this..
[[self navigationController] presentModalViewController:b1VC animated:YES];
EDIT2: Also, you're initializing a class, just to call a method which is already self. Your -didSelectItem: method should look more like this.
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
switch (item.tag) {
case 0:
{
NSLog(#"item 1 selected");
[self tabBarSelectedAction];
}
break;
default:
break;
}
}
I'm trying to call a method in the view controller from the app delegate, but Xcode says No known class method for selector 'myMethodHere'. Here's my code:
AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[..]
[MainViewController myMethodHere];
[..]
return YES;
}
MainViewController.m:
-(void) myMethodHere {
[..]
}
I would try
MainViewController * vc = [[MainViewController alloc]init];
[vc myMethodHere];
[vc release];
Make sure to import your MainViewController in your app delegate .m file
make sure you add "myMethodHere" to your MainViewController .h file
You are trying to call a class method when you want to call an instance method. If the view controller is the root view controller, then you should be able to call it thus:
UIWindow *window = [UIApplication sharedApplication].keyWindow;
MainViewController *rootViewController = window.rootViewController;
[rootViewController myMethodHere];
If it's not the root view controller then you'll have to find some other way of getting hold of the instance and then calling the method as in the last line above.
If you want to access to a view controller on a story board, you may use this block of code from the AppDelegate:
MainViewController *rootViewController = (MainViewController*)self.window.rootViewController;
[rootViewController aMethod];
Remember to add the import.
In Swift, you can write it like this
UIApplication.sharedApplication().keyWindow?.rootViewController?.yourMethodName()
Try to write
-(void) myMethodHere;
in MainViewController.h
I'd like to use a modal UITableView at startup to ask users for password, etc. if they are not already configured. However, the command to call the uitableview doesn't seem to work inside viewDidLoad.
startup code:
- (void)viewDidLoad {
rootViewController = [[SettingsController alloc]
initWithStyle:UITableViewStyleGrouped];
navigationController = [[UINavigationController alloc]
initWithRootViewController:rootViewController];
// place where code doesn't work
//[self presentModalViewController:navigationController animated:YES];
}
However, the same code works fine when called later by a button:
- (IBAction)settingsPressed:(id)sender{
[self presentModalViewController:navigationController animated:YES];
}
Related question: how do I sense (at the upper level) when the UITableView has used the command to quit?
[self.parentViewController dismissModalViewControllerAnimated:YES];
You can place the presentModalViewController:animated: call elsewhere in code - it should work in the viewWillAppear method of the view controller, or in the applicationDidFinishLaunching method in the app delegate (this is where I place my on-launch modal controllers).
As for knowing when the view controller disappears, you can define a method on the parent view controller and override the implementation of dismissModalViewControllerAnimated on the child controller to call the method. Something like this:
// Parent view controller, of class ParentController
- (void)modalViewControllerWasDismissed {
NSLog(#"dismissed!");
}
// Modal (child) view controller
- (void)dismissModalViewControllerAnimated:(BOOL)animated {
ParentController *parent = (ParentController *)(self.parentViewController);
[parent modalViewControllerWasDismissed];
[super dismissModalViewControllerAnimated:animated];
}
I had quite the same problem. I know the topic is old but maybe my solution could help someone else...
You just have to move your modal definition in a method:
// ModalViewController initialization
- (void) presentStartUpModal
{
ModalStartupViewController *startUpModal = [[ModalStartupViewController alloc] initWithNibName:#"StartUpModalView" bundle:nil];
startUpModal.delegate = self;
[self presentModalViewController:startUpModal animated:YES];
[startUpModal release];
}
Next, in viewDidLoad, call your modal definition method in a performSelector:withObject:afterDelay: with 0 as delay value. Like this:
- (void)viewDidLoad
{
[super viewDidLoad];
//[self presentStartUpModal]; // <== This line don't seems to work but the next one is fine.
[self performSelector:#selector(presentStartUpModal) withObject:nil afterDelay:0.0];
}
I still don't understand why the 'standard' way doesn't work.
If you are going to do it like that then you are going to have to declare your own protocol to be able to tell when the UITableView dismissed the parentViewController, so you declare a protocol that has a method like
-(void)MyTableViewDidDismiss
then in your parent class you can implement this protocol and after you dismissModalView in tableView you can call MyTableViewDidDismiss on the delegate (whihc is the parent view controller).