My question is based on the following question, but my case is reversed.
Example for login screen modally based on storyboard
I would like to have a few tabs private for users only so required login, my storyboard is below:
Below is my code in custom tabBarController
#interface PNTabBarController ()
#end
#implementation PNTabBarController
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
NSUInteger indexOfTab = [tabBar.items indexOfObject:item];
NSLog(#"%lu", (unsigned long)indexOfTab);
if (indexOfTab == 2 || indexOfTab == 3) {
PFUser *currentUser = [PFUser currentUser];
NSLog(#"%#", currentUser.username);
if(currentUser == nil){
PNLoginViewController *obj = [[PNLoginViewController alloc]init];
[self presentViewController:obj animated:YES completion:NULL];
}
else{
NSLog(#"%#", currentUser.username);
}
}
}
But what happens is a dark screen when I click the 2 or 3 tab. What should be the correct way to achieve this?
Related
I have set up my class with UITabBarDelegate and implemented its method didSelectItem to detect when a certain tabBar item is pressed. Works great. In each tabBar item I have one containerView that can show a "you have to login"-page if the user is not logged in, and another containerView that present viewControllers that are embedded in a navigationController.
I would like to keep track of the viewController that is presented in the current tab item, and/or the root viewController of that tab.
I have tried a number of different approaches, but most of them return nil or I can't get it to work. I think the whole container situation makes it harder to handle.
It looks something like this:
#interface MyTabBarController () <UITabBarDelegate>
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
NSUInteger indexOfTab = [[tabBar items] indexOfObject:item];
switch (indexOfTab) {
case 0: {
NSLog(#"🐳PRESSIIING %lu", (unsigned long)[[tabBar items] indexOfObject:item]);
break;
}
case 1: {
NSLog(#"🐳PRESSIIING %lu", (unsigned long)[[tabBar items] indexOfObject:item]);
break;
}
case 2: {
NSLog(#"🐳PRESSIIING %lu", (unsigned long)[[tabBar items] indexOfObject:item]);
//These return nil
NSLog(#"🐳AAAAAA %#", ((UINavigationController*)_appD.window.rootViewController).visibleViewController);
NSLog(#"🐳AAAAAA %#", ((UITabBarController*)_appD.window.rootViewController).selectedViewController);
NSLog(#"🐳AAAAAA %#", self.navigationController.topViewController);
NSLog(#"🐳AAAAAA %#", self.navigationController.visibleViewController);
//This returns with a value, but can't get it to work with conditionals, that is, when I'm in root, the else is triggered
NSLog(#"🐳AAAAAA %#", self.tabBar.window.rootViewController);
if(!self.tabBar.window.rootViewController) {
NSLog(#"🐳🐳🐳THIS IS NOT ROOT🐳🐳🐳");
}else {
NSLog(#"🐳🐳🐳this is ROOT🐳🐳🐳");
}
// This returns nil
((UINavigationController*)_appD.window.rootViewController).visibleViewController;
((UITabBarController*)_appD.window.rootViewController).selectedViewController;
//Doesn't work
if([self.navigationController.viewControllers[0] isKindOfClass:[ExperiencesListViewController class]]) {
NSLog(#"🐳IS KIND OF CLASS LIST");
}
if([self.navigationController.viewControllers[0].childViewControllers isKindOfClass:[ExperiencesContainerViewController class]]) {
NSLog(#"🐳IS KIND OF CLASS CONTAINER");
}
break;
}
case 3: {
NSLog(#"🐳PRESSIIING %lu", (unsigned long)[[tabBar items] indexOfObject:item]);
break;
}
case 4: {
NSLog(#"🐳PRESSIIING %lu", (unsigned long)[[tabBar items] indexOfObject:item]);
break;
}
default:
break;
}
}
So, what else can I try? Seems like I have to use `self.tabBar.window.rootViewController` in some way, no?
***EDIT***
Oh, and I have tried the `tabBarController` delegate but that doesn't trigger. Also, the `tabBar` is constructed programmatically if that helps.
Sorry to have not read your question correctly. Here's what I suggest you do.
All of these view controllers that you're interested in keeping track of: you should have them send a custom notification from within their -viewDidAppear: (or -viewWillAppear:) method. Then let your ApolloTabBarController object register for that notification. When it gets the notification, you could then store a reference to the view controller. That reference will always point to the active view controller.
In your individual view controllers, do something like the following:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:#"XYZViewControllerDidBecomeActiveNotification"
object:self];
}
Of course, you would want to use some kind of constant for the notification name.
In your ApolloTabBarController class, register for XYZViewControllerDidBecomeActiveNotification and implement something like:
- (void)viewControllerDidBecomeActive:(NSNotification *)notification
{
self.activeViewController = [notification object];
}
I hope that helps!
When you're setting up each view controller for each of the tabs, set the tag property of the UITabBarItem to correspond to the index of the view controller in the tab bar's viewControllers array.
UIViewController* myFirstVC = [[UIViewController alloc] init];
UIViewController* mySecondVC = [[UIViewController alloc] init];
// "self" is your ApolloTabBarController.
[self setViewControllers:#[myFirstVC, mySecondVC]];
myFirstVC.tabBarItem =
[[UITabBarItem alloc] initWithTitle:#"First" image:nil tag:0];
mySecondVC.tabBarItem =
[[UITabBarItem alloc] initWithTitle:#"Second" image:nil tag:1];
Then, you'll be able to grab a reference to view controller.
// In your example, your ApolloTabBarController acts as its own delegate.
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
UIViewController* activeVC =
[[self viewControllers] objectAtIndex:[item tag]];
}
I need to enable Peek And Pop functionality on a UITableViewCell in Objective C by using Force Touch. And also need to show some actions under peek view like default mail app. I am new to iOS and please help me to get there.
Peek And Pop Effect on a TableViewCell and Collection View Cell With Actions
1)You should address your caller viewController class as UIViewControllerPreviewing Delegate
#interface MyTableViewController ()
2)Create a #property for storing the information.
#property (nonatomic, strong) id previewingContext;
3)Call the forceTouchIntialize method in ViewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
[self forceTochIntialize];
}
4)Check Force Touch Available or Not
-(void)forceTouchIntialize{
if ([self isForceTouchAvailable]) {
self.previewingContext = [self registerForPreviewingWithDelegate:self sourceView:self.view];
}
}
- (BOOL)isForceTouchAvailable {
BOOL isForceTouchAvailable = NO;
if ([self.traitCollection respondsToSelector:#selector(forceTouchCapability)]) {
isForceTouchAvailable = self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable;
}
return isForceTouchAvailable;
}
5)Specify the view controller which content we want to preview(FOR PEEK)
- (UIViewController *)previewingContext:(id<UIViewControllerPreviewing> )previewingContext viewControllerForLocation:(CGPoint)location{
CGPoint cellPostion = [yourTableView convertPoint:location fromView:self.view];
NSIndexPath *path = yourTableView indexPathForRowAtPoint:cellPostion];
if (path) {
UITableViewCell *tableCell = [yourTableView
cellForRowAtIndexPath:path];
//Pushing to a nib File
PushViewController *previewController = [[PushViewController alloc] initWithNibName:#"PushViewController" bundle:nil];
//To Pass data to Preview ViewController
// id temp = [yourDataArray objectAtIndex:path.row];
// PushViewController.anyObject=temp;
previewingContext.sourceRect = [self.view convertRect:tableCell.frame fromView: yourTableView
]; return previewController;
}
return nil;
}
6)POP in a Deep Press (FOR POP)
-(void)previewingContext:(id )previewingContext commitViewController: (UIViewController *)viewControllerToCommit {
[self.navigationController showViewController:viewControllerToCommit sender:nil];
}
7)When the controller get peek and you swipe up, If you want to add buttons like delete,archive add this method into your previewViewContrller(In this example “ PushViewController")
- (NSArray<id> *)previewActionItems {
UIPreviewAction *previewAction1 = [UIPreviewAction actionWithTitle:#"delete" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction *action, UIViewController *previewViewController){
}];
UIPreviewAction *previewAction2 = [UIPreviewAction actionWithTitle:#"archive" style:UIPreviewActionStyleDefault handler:^(UIPreviewAction *action, UIViewController *previewViewController){
}];
return #[previewAction1,previewAction2];
}
I am new to iPad development. I know how to use images for Page view controller. My problem is i have 3 charts which i have done in three view controllers.. How can i combine all the view controllers in a single page view controller. I have for now kept three view controllers.
I have tried lot of tutorials but none explains me how to use three view controllers
I have done like this now but this is wrong
-(IBAction)handleSwipeLeft:(UISwipeGestureRecognizer*)sender {
LastLearningSessionViewController *last=[[LastLearningSessionViewController alloc]init];
[self presentViewController:last animated:YES completion:nil];
}
From what I can see it appears you may have done this slightly wrong.
First you need to create a controller for the UIPageViewController, that is a datasource and delegate.
Please note all code has been written directly to the answer and has not been tested.
MyUIPageViewController.h
#interface
MyUIPageViewController : UIViewController <UIPageViewControllerDataSource, UIPageViewControllerDelegate>
{
NSNumber *currentIndex //Using NSNumber to handle 32bit/64bit easier.
}
#property (nonatomic, strong) UIPageViewController *pageViewController
#property (nonatomic, strong) NSArray *controllersArray //Used to help navigate between controllers
#end
MyUIPageViewController.m
#import MyUIPageViewController.h
#implementation MyUIPageViewController
- (instancetype)initWithNibName:(NSString *)nibName
bundle:(NSBundle *)nibBundle
{
if(self = [super initWithNibName:nibName bundle:nibBundle])
{
//Create ChartViewController1 (UIViewController *ChartViewController1 = [[ChartViewController1Class alloc] init];)
//Create ChartViewController2
//Create ChartViewController3
//Now we have created all 3 chartViewControllers, create our controllers Array with the controller objects.
self.controllersArray = [[NSArray alloc] initWithObjects:ChartViewController1, ChartViewController2, ChartViewController3];
//Currently setting to 0. A proper way of handling with Multi-tasking is to store the index value from before, but not dealing with that right now.
currentIndex = [NSNumber numberWithInt:0];
//Create our PageViewController. Currently set to PageCurl and all pages will go from left to right.
//These options can be changed, if so desired (Scroll Effect like iBooks Textbooks and a page change from bottom to top like a flip book.
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
//Set ourselves as the datasource and delegate to handle the pages etc.
self.pageViewController.datasource = self;
self.pageViewController.delegate = self;
//We need to set the viewControllers for the PageViewController, because this is the initial load, we will not animate the change.
[self.pageViewController setViewControllers:self.controllersArray direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:^ (BOOL finished) {
//No animation is being done so no need to worry.
}];
//Set our view to be the pagecontroller's view, so we can see it all.
self.view = self.pageViewController.view;
}
return self;
}
//DataSource Methods:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController
{
//As this method looks for the previous view controller. If our current index is 0, there is no previous VC. But using the objectAtIndex method on the array would throw a outOfRange exception
if([self.currentIndex intValue] <= 0)
{
return nil;
}
else
{
return [self.controllersArray objectAtIndex:([self.currentIndex intValue] - 1)];
}
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController
{
//As this method looks for the next view controller. If our current index is the maximum value the array count and be (2), there isn't a new VC to push. But using the objectAtIndex method on the array would throw a outOfRange exception
if([self.currentIndex intValue] >= self.controllersArray.count)
{
return nil;
}
else
{
return [self.controllersArray objectAtIndex:([self.currentIndex intValue] + 1)];
}
}
//Delegate Methods
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
if(transitionCompleted)
{
//We will update our currentIndex, only if the transition has happened.
switch (previousViewControllers)
{
case 0:
//Something went wrong :S
break;
case 1:
//We are either in Vertical Orientation of the first viewController is only being shown.
if([pageViewController.viewControllers contains:[self.controllersArray objectAtIndex:([currentIndex intValue]+ 1)]])
{
currentIndex = [NSNumber numberWithInt:([currentIndex intValue] + 1)];
}
else
{
if([currentIndex intValue] == 0)
{
//Saftey Net.
}
else
{
currentIndex = [NSNumber numberWithInt:([currentIndex intValue] - 1)];
}
}
break;
case 2:
//We are in horizontal Orientation.
//With 3 View Controllers the only ViewController that will be in both arrays is the ViewController at index 1. We just need to see if the pageViewControllers viewcontrollers array contains the ViewController at index 0 or index 1.
if([pageViewController.viewControllers contains:[self.controllersArray objectAtIndex:([currentIndex intValue]+ 1)]])
{
currentIndex = [NSNumber numberWithInt:([currentIndex intValue] + 1)];
}
else
{
if([currentIndex intValue] == 0)
{
//Saftey Net.
}
else
{
currentIndex = [NSNumber numberWithInt:([currentIndex intValue] - 1)];
}
}
break;
default:
//Should never reach here.
break;
}
}
}
#end
It is also handy to look at the reference documentation:
Class Reference - Apple Docs
For my iPhone app I want to implement the option to upload files to Soundcloud by making use of the CocoaSoundCloudAPI. In the instructions "How to use the SoundCloud API directly" it is explained how to modally present a loginViewController:
- (void)login {
[SCSoundCloud requestAccessWithPreparedAuthorizationURLHandler:
^(NSURL *preparedURL){
SCLoginViewController *loginViewController;
loginViewController =
[SCLoginViewController loginViewControllerWithPreparedURL:preparedURL
completionHandler:^(NSError *error){
if (SC_CANCELED(error)) {
NSLog(#"Canceled!");
} else if (error) {
NSLog(#"Ooops, something went wrong: %#", [error localizedDescription]);
} else {
NSLog(#"Done!");
}
}];
[self presentModalViewController:loginViewController
animated:YES];
}];
}
Now I replaced
[self presentModalViewController:loginViewController
animated:YES];
with
[self presentViewController:loginViewController
animated:YES
completion:nil];
because the first method is deprecated in iOS 7.
But the problem is that the Soundcloud loginViewController overlaps the status bar when presented in this fashion. And since I don't want to change the Soundcloud API I do not have the option to customize the loginViewController accordingly e.g. in its - viewDidLoad method (as suggested in many other posts on Stackoverflow).
Unfortunately there is a toolbar with a button on top the loginViewController. How can I configure my loginViewController from inside my own (presenting) view controller so that it won't overlap with the status bar when presented?
As mentioned in my comment to the original question I did not find a neat solution for this problem. However I managed to implement a workaround that does the job:
The basic idea is to add the SCLoginViewController as a child view controller of another custom view controller that is not part of the Soundcloud framework and that you can customize to your needs. This is my new login method that presents the login view controller:
- (BOOL)loginToSoundcloud {
BOOL __block success = NO;
[SCSoundCloud requestAccessWithPreparedAuthorizationURLHandler:^(NSURL *preparedURL){
SCLoginViewController *loginViewController;
loginViewController =
[SCLoginViewController loginViewControllerWithPreparedURL:preparedURL
completionHandler:^(NSError *error){
if (SC_CANCELED(error)) {
NSLog(#"Canceled!");
} else if (error) {
NSLog(#"Ooops, something went wrong: %#", [error localizedDescription]);
} else {
NSLog(#"Done!");
success = YES;
}
}];
/* BEGIN workaround for iOS7 bug:
when modally presenting a view controller it overlaps the status bar */
CBContainerVCToFixStatusBarOverlap *containerVC = [[CBContainerVCToFixStatusBarOverlap alloc] init];
[containerVC addChildViewController:loginViewController];
containerVC.view.backgroundColor = [UIColor clearColor];
if ([CBAppDelegate iOSVersionIs7OrHigher]) {
loginViewController.view.frame =
CGRectMake(loginViewController.view.frame.origin.x,
loginViewController.view.frame.origin.y + 20,
containerVC.view.frame.size.width,
containerVC.view.frame.size.height - 20);
} else {
loginViewController.view.frame =
CGRectMake(loginViewController.view.frame.origin.x,
loginViewController.view.frame.origin.y,
containerVC.view.frame.size.width,
containerVC.view.frame.size.height);
}
[containerVC.view addSubview:loginViewController.view];
/* END workaround for iOS7 bug */
[self presentViewController:containerVC
animated:YES
completion:nil];
}];
return success;
}
To check for the iOS version I implemented the following method in my CBAppDelegate:
+ (BOOL)iOSVersionIs7OrHigher {
return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1;
}
CBContainerVCToFixStatusBarOverlap is a simple view controller class with no additional methods and only one declared property. This is the content of CBContainerVCToFixStatusBarOverlap.h:
#interface CBContainerVCToFixStatusBarOverlap : UIViewController
#property (strong, nonatomic) IBOutlet UIView *containerView;
#end
I am Using Outh to add Twitter to my app.
Below are two UIViewControllers for login and posting tweets respectively.
1.SettingViewcontroller
2.DetailViewcontroller
This code for login to twitter with SettingViewcontroller.
- (void)switchAction1:(UISwitch*)sender
{
if (sender.on){
if(_engine)
return;
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate:self]
_engine.consumerKey = #"###";
_engine.consumerSecret = #"###";
UIViewController *controller =[SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine: _engine delegate: self];
DetailViewcontroller *detobj=[[DetailViewcontroller alloc]init];
detobj.detail_enginne=_engine;
[detobj release];
[self presentModalViewController: controller animated: YES];
}
if(!sender.on)
{
NSLog(#"Logout section");
}
}
And In DetailViewcontroller Iam doing like this for posting.
if(buttonIndex == 1)
{
if(detail_enginne!=nil)
{
NSLog(#"engine availble");
NSString *str=#"hai twitterrrrrrrrrrr";
[detail_enginne sendUpdate:str];
}
else{
NSLog(#"Engine not availabele");
}
}
Here Iam unable to send tweets from DetailViewcontroller.
How to achive this?
in DetailViewcontroller.m
#import "SettingViewcontroller.h"
replace
[_enginnne sendUpdate:str];
to
[[[self parentViewController] _enginnne] sendUpdate:str];