How to implement didSelectViewController - objective-c

I want to catch the event when someone switches between tabs. I have the following two function in my appdelegate file:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController * uitbc = [storyboard instantiateViewControllerWithIdentifier:#"tabbarcontroller"];
uitbc.delegate = self;
[self.window addSubview:uitbc.view];
return YES;
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSLog(#"switching");
}
But the NSLog(#"switching"); never fires. The xcode issues a warning for the line uitbc.delegate = self; saying "Passing appdelegate const__strong to parameter of incompatible type id".
What am I doing wrong? I'm just following the accepted answer found here, except i'm instantiating my tabbarcontroller form story board:
how to get the event that switch tab menu on iphone
Update
Based on skram's suggestion, I wrote this for my appdelegate but the NSLOG(Switching) still doesn't fire:
#interface johnAppDelegate : UIResponder <UITabBarControllerDelegate>
I also updated my didFinishLauchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
tabBarController = self.window.rootViewController.tabBarController;
tabBarController.delegate = self;
[window addSubview:tabBarController.view];
}
Good thing is that nothing crashes. I also no longer et the warning about incompatible types. But still, didSelectViewController doesn't fire.

in my appdelegate.h file, I changed the line
#interface wscAppDelegate : UIResponder <UIApplicationDelegate>
to
#interface wscAppDelegate : UIResponder <UIApplicationDelegate,UITabBarControllerDelegate>
Then in my CustomTabBarController in the viewDidLoad function i added these lines:
wscAppDelegate *appDelegate = (wscAppDelegate *)[[UIApplication sharedApplication] delegate];
self.delegate = appDelegate;
Then in appdelegate.m file, I added this function
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSLog(#"hooray this works");
}

I also struggled with this a lot but my UITabBarNavigationController is pretty far from AppDelegate to set it there, and the only way to actually make it work was to do it not in the viewDidLoad of the UITabBarController subclass itself, but in its:
-(void)viewDidLayoutSubviews {
[ super viewDidLayoutSubviews ];
self.delegate = self;
}
And that solved it. Alternatively you can do it in any of the tab bar controlled viewcontrollers, but that just feels wrong.

You need your AppDelegate to conform to UITabBarControllerDelegate protocol. See below..
#interface YourAppDelegate : UIResponder <UITabBarControllerDelegate>

Related

Objective-C: Tabbaritem tapped->Method Called-> But WebView not refreshed

Trying to achieve
When I tap on the tabbaritem say #2, it will called the method and reload the web view.
Issue
When I tap on the tabbaritem, the method is called but web view did not reload.
Did not load the web view
Question
If I called the method on the VC itself. I can manage to reload the web view. Only if I called it when the tabbaritem is tapped, it doesn't reload the web view.
Code
MyTabBarController.m
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
NSLog(#"controller class: %#", NSStringFromClass([viewController class]));
NSLog(#"controller title: %#", viewController.title);
if (viewController == [tabBarController.viewControllers objectAtIndex:2])
{
[(UINavigationController *)viewController popToRootViewControllerAnimated:YES];
tabBarController.delegate = self;
[[[Classes alloc] init] LoadClasses];
}else if (viewController == [tabBarController.viewControllers objectAtIndex:3]){
[(UINavigationController *)viewController popToRootViewControllerAnimated:YES];
tabBarController.moreNavigationController.delegate = self;
[[[Gym alloc] init] handleRefreshGym:nil];
}else{
[(UINavigationController *)viewController popToRootViewControllerAnimated:NO];
}
}
Classes.m
- (void)LoadClasses {
sURL = #"www.share-fitness.com/apps/class.asp?memCode=SF100012&dtpClass=13/09/2018&lang=EN&lat=37.785835&long=-122.406418&ver=1&plat=IOS"
NSLog(#"The URL To be loaded %#", sURL);
NSURL *url = [NSURL URLWithString:sURL];
sRefresh = sURL;
[[NSURLCache sharedURLCache] removeAllCachedResponses];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[webView loadRequest:urlRequest];
[webView setDelegate:(id<UIWebViewDelegate>)self];
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[webView.scrollView addSubview:refreshControl];
}
As I mentioned in my other reply Objective-C: How to properly set didSelectViewController method for TabBarController, so I can refresh the VC everytime it is tapped, I don't think it's good User Experience to be refreshing the view from the server every time the tab bar is selected (this will get very annoying for users to wait every time for the server to refresh the data)
That being said, the issue with the code you posted is that you're initializing a new instance of your classes in the TabBarControllerDelegate method so the method will be called on this new instance instead of on the one that's displaying/exists in your TabBarController's view controllers. Specifically these two lines are initializing the new instances:
[[[Classes alloc] init] LoadClasses];
[[[Gym alloc] init] handleRefreshGym:nil];
Instead you should be finding the instance that already exists, and calling the method on them.
I would recommend creating a ParentViewController with a public method along the lines of - (void)doStuffWhenTabBarControllerSelects; (just example naming to be clear what's it doing to you) then have each of the view controllers you'd like to have do something when they're selected be child classes of this parent (and have their own implementation of - (void)doStuffWhenTabBarControllerSelects;). This way in the TabBarController's delegate method, you can just find the appropriate instance of ParentViewController (associated with the view controller being selected) and call the - (void)doStuffWhenTabBarControllerSelects; method on it.
Here's an example of what I mean:
ParentViewController.h:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface ParentViewController : UIViewController
- (void)doStuffWhenTabBarControllerSelects;
#end
NS_ASSUME_NONNULL_END
ParentViewController.m:
#import "ParentViewController.h"
#interface ParentViewController ()
#end
#implementation ParentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)doStuffWhenTabBarControllerSelects {
NSLog(#"Fallback implementation if this method isn't implemented by the child class");
}
#end
FirstViewController.h:
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#interface FirstViewController : ParentViewController
#end
FirstViewController.m:
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)doStuffWhenTabBarControllerSelects {
NSLog(#"I'm doing stuff on the %# when the tab bar controller delegate calls back to selection", NSStringFromClass([self class]));
}
#end
SecondViewController.h:
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#interface SecondViewController : ParentViewController
#end
SecondViewController.m:
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)doStuffWhenTabBarControllerSelects {
NSLog(#"I'm doing stuff on the %# when the tab bar controller delegate calls back to selection", NSStringFromClass([self class]));
}
#end
MyTabBarController.h:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface MyTabBarController : UITabBarController <UITabBarControllerDelegate>
#end
NS_ASSUME_NONNULL_END
MyTabBarController.m:
#import "MyTabBarController.h"
#import "ParentViewController.h"
#implementation MyTabBarController
- (void)viewDidLoad {
[super viewDidLoad];
self.delegate = self;
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
// since your view controllers are embedded in nav controllers, let's make sure we're getting a nav controller
if ([viewController isKindOfClass:[UINavigationController class]]) {
// we're expecting a nav controller so cast it to a nav here
UINavigationController *navController = (UINavigationController *)viewController;
// now grab the first view controller from that nav controller
UIViewController *firstViewControllerInNav = navController.viewControllers.firstObject;
// check to make sure it's what we're expecting (ParentViewController)
if ([firstViewControllerInNav isKindOfClass:[ParentViewController class]]) {
// cast it to our parent view controller class
ParentViewController *viewControllerToCallMethodOnAfterSelection = (ParentViewController *)firstViewControllerInNav;
[viewControllerToCallMethodOnAfterSelection doStuffWhenTabBarControllerSelects];
}
}
}
#end
Then when you select between the two tabs you'll this is the output:
I'm doing stuff on the FirstViewController when the tab bar controller delegate calls back to selection
I'm doing stuff on the SecondViewController when the tab bar controller delegate calls back to selection
I'd recommend doing some additional research/reading of the documentation:
There's a good amount of beginner information here: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/DefiningClasses/DefiningClasses.html#//apple_ref/doc/uid/TP40011210-CH3-SW1
UITabBarController: https://developer.apple.com/documentation/uikit/uitabbarcontroller?language=objc
UITabBarControllerDelegate:
https://developer.apple.com/documentation/uikit/uitabbarcontrollerdelegate?language=objc
One other helpful hint is that within Xcode you can hold down on the option key and click on something to show a quicklook into the explanation/documentation
You can also right click on something and "Jump To Definition". The majority of Apple's implementations will will have additional information in the header.
Here's the example of what's in the header of UITabBarController:
/*!
UITabBarController manages a button bar and transition view, for an application with multiple top-level modes.
To use in your application, add its view to the view hierarchy, then add top-level view controllers in order.
Most clients will not need to subclass UITabBarController.
If more than five view controllers are added to a tab bar controller, only the first four will display.
The rest will be accessible under an automatically generated More item.
UITabBarController is rotatable if all of its view controllers are rotatable.
*/
NS_CLASS_AVAILABLE_IOS(2_0) #interface UITabBarController : UIViewController <UITabBarDelegate, NSCoding>
As well as under the Help Menu there's "Developer Documentation" (CMD + SHIFT + 0) which has a multitude of useful information.

How to fix "property implementation must have its declaration in interface 'Appdelegate'"

//
// AppDelegate.m
// Foody
//
// Created by Iceeiei on 5/2/15.
// Copyright (c) 2015 pondandfriend. All rights reserved.
//
#import "AppDelegate.h"
#import "ViewController.h"
#import "RecipeList.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[ViewController alloc]initWithNibName:#"ViewController" bundle:nil];
RecipeList *recipeList = [[RecipeList alloc] initWithNibName:#"RecipeList" bundle:nil];
self.window.rootViewController = recipeList;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
#end
Error 1:
property implementation must have its declaration in interface 'Appdelegate'
Error 2:
Property'viewController' not found on object of type 'AppDelegate'
Is there any way to fix this?
It looks like you haven't added the #property notation for your ViewController in the .h. Add the following to AppDelegate.h:
#propery (nonatomic) ViewController *viewController;

Applications are expected to have a root view controller at the end of application launch wait_fences: failed to receive reply: 10004003

when i launch my application in iphone emulator, it goes without any problem. If i launch on device it will crash as still as i open it. But when i launch emulator it says that problem:
Applications are expected to have a root view controller at the end of application launch
wait_fences: failed to receive reply: 10004003
What can i do?
This is my application delegate:
.h :
#import <UIKit/UIKit.h>
#class TrovaChiaviViewController;
#interface TrovaChiaviAppDelegate : NSObject <UIApplicationDelegate>
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet TrovaChiaviViewController *viewController;
#end
and this is .m
#import "TrovaChiaviAppDelegate.h"
#import "TrovaChiaviViewController.h"
#implementation TrovaChiaviAppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[application release];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
}
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
#end
I don't see where you are setting your viewController property, add
self.viewController = [[[TrovaChiaviViewController alloc] initWithNibName:#"TrovaChiaviViewController" bundle:nil] autorelease];
before
self.window.rootViewController = self.viewController;
I just had this issue when I was changing the application from iPhone only to iPad. The error message "Applications are expected to have a root view controller at the end of application launch" was happening because in the application-info plist, there was an entry specifying a nib file for MainWindow when I was no longer using a nib.
Although this is an older thread, I thought I'd contribute. Sometimes I select a template when creating new projects. There were two cases when I created empty projects. In both of these cases, I encountered the same problem.
Refer to this link.
http://www.mumuen.com/2011/09/applications-are-expected-to-have-root.html
In my case, the key was that I had failed to allocate space for both my window and root controller.

iOS Memory Management, UIWindow addSubview:, and tab controller

When using the following code, do you need to do anything to release the tab controller's view? While working on an app, I've analyzed the project and it reports that there is a potential link on the line using addSubview:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:[tabController view]];
[window makeKeyAndVisible];
return YES;
}
MyAppDelegate.h
#interface MyAppDelegate : AppDelegate {
UIWindow *window;
IBOutlet UITabBarController *tabController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabController;
#end
MyAppDelegate.m
#implementation MyAppDelegate
#synthesize tabController, window;
- (void)dealloc {
[tabController release];
[window release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:[tabController view]];
[window makeKeyAndVisible];
return YES;
}
#end
Do I need to release tabController.view before I release the tabController? Or inside the application:didFinishLaunchingWithOptions: method? This question seems to say that I just need to release the controller in dealloc.
Thanks!
Technically, you should release the tabController at some point, but in reality it does not matter. The window exists for the life of the program, and when the program is terminated all memory is purged, so there will be no leaks for objects retained by the window.
This will raise an alert when you run Build/Build and Analyze, but it can safely be ignored. The same applies to a navController added at this level or any other retained subview of the main window.
u have sythesized so call it with self
- (void)dealloc {
[self.tabController release];
self.tabController=nil;
[self.window release];
self.window = nil;
}

Pushing and Popping ViewControllers using a Navigation Controller: Implementation

Like many others I started to code an experiment today where I would have two view controllers and be able to switch between them. I got this to work using a navigation controller, but I have a question about the implementation.
In my TwoViewsAppDelegate, I define the navigation controller and the rootViewController.
#interface TwoViewsAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UINavigationController *navigationController;
RootViewController *rootViewController;
}
and set them up as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
rootViewController = [[RootViewController alloc] init];
navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
[window setRootViewController:navigationController];
[self.window makeKeyAndVisible];
return YES;
}
Then in my rootViewController, I define the level2ViewController that I
am going to switch to, and a button that I'm going to press to make the
switch happen:
#interface RootViewController : UIViewController {
UIButton *theButton;
Level2ViewController *level2ViewController;
}
Here's the response to the button being pressed in RootViewController.m:
-(void)level1ButtonPressed:(id)sender
{
if (level2ViewController == nil)
{
level2ViewController = [[Level2ViewController alloc] init];
}
[self.navigationController pushViewController:level2ViewController animated:YES];
}
The problem is that if there was going to be a level3ViewController,
it would have to be defined as a member of level2ViewController, etc.
for however many view controllers i wanted to push onto the stack.
It would be nice to be able to define all the view controllers in one
place, preferably the app Delegate. Is this possible?
To solve this, you can create a callback-type method which uses the delegate of the class that'll be sending the requests for the view controllers. Best explained through code...
RootViewController.h
#import "RootInterfaceView.h"
// all the other VC imports here too
#interface RootViewController : UIViewController <RootInterfaceViewDelegate>
{
RootInterfaceView *interface;
}
RootViewController.m
-(void)rootInterfaceView: (RootInterfaceView*)rootInterfaceView didSelectItem:(NSUInteger)itemTag
{
switch (itemTag)
// then create the matching view controller
}
RootInterfaceView.h
// imports here if required
#protocol RootInterfaceViewDelegate;
#interface RootInterfaceView : UIView <RootInterfaceItemViewDelegate>
{
id <RootInterfaceViewDelegate> delegate;
}
#property (nonatomic, assign) id delegate;
#end
#protocol RootInterfaceViewDelegate <NSObject>
#optional
-(void)rootInterfaceView: (RootInterfaceView*)rootInterfaceView didSelectItem:(NSUInteger)itemTag;
#end
RootInterfaceView.m
// remember to synthesize the delegate
-(void)rootInterfaceItemSelected: (RootInterfaceItemView*)rootInterfaceItemView
{
NSUInteger theTag = rootInterfaceItemView.tag;
if ([self.delegate respondsToSelector:#selector(rootInterfaceView:didSelectItem:)])
[self.delegate rootInterfaceView:self didSelectItem:theTag];
}
Alternatively, if the only options from level 2 were either back to root/pop one VC or to push controller 3, then it'd be fine for level 2 to be importing 3 to allow for it's creation.