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
Related
This question already has answers here:
how to reload tableview of another uiviewcontroller in current viewcontroller
(4 answers)
Closed 7 years ago.
I have two view controllers. First one has a table view and using the second view controller I'm calling a method on first view controller. In that method I'm trying to adding an item to the objects array and refresh the table view.
[postsArr insertObject:post atIndex:postsArr.count];
dispatch_async(dispatch_get_main_queue(), ^{
[self.newsTableView reloadData];
});
I'm calling this method in 2nd view controller,
dispatch_async(dispatch_get_main_queue(), ^{
[self.navigationController popViewControllerAnimated:YES];
[self.appContext.creator createWithText:userText completion:^(UserData *userData,NSError *error) {
if (error == nil){
if (userData != nil) {
[self.parent addNew:((UserData*)[userData.list objectAtIndex:0]) withImage:nil];
}
}else{
[self showAlertWith:#"Error" with:#"Error occurred!"];
}
}];
});
How may I refresh the table view from another view controller?
Add this on the top of the interface of your second viewcontroller
#protocol SecondViewControllerDelegate <NSObject>
- (void)addNewItem:(id)item;
#end
#interface SecondViewController : UIViewController
#property (nonatomic, weak) id <SecondViewControllerDelegate> delegate;
#end
The point in your firstViewController from where you are instantiating your secondViewController for navigation add secondviewController.delegate as self.
self.secondViewController.delegate = self;
From the point where you get response in your secondViewController and you want to addItem in your firstViewController call delegate method from here and pass that item to firstViewController in that delegate method.
dispatch_async(dispatch_get_main_queue(), ^{
[self.navigationController popViewControllerAnimated:YES];
[self.appContext.creator createWithText:userText completion:^(UserData *userData,NSError *error) {
if (error == nil){
if (userData != nil) {
[self.delegate addNewItem:((UserData*)[userData.list objectAtIndex:0]) withImage:nil];
}
}else{
[self showAlertWith:#"Error" with:#"Error occurred!"];
}
}];
});
Add the implementation of addNewItem in your firstViewController
- (void)addNewItem:(id)item{
[postsArr insertObject:post atIndex:postsArr.count];
dispatch_async(dispatch_get_main_queue(), ^{
[self.newsTableView reloadData];
});
}
You can use NSNotificationCenter
Fire notification when you need to reload the table
Just follow the link to know how implement notifications
Send and receive messages through NSNotificationCenter in Objective-C?
There Are Many methods through you can achieve your Goal
NSNotification
Delegates
KeyValueObserver
But I found most reliable is
#property (copy) void (^valueTypeChangedBlock) (NSarray arrayTypeObject);
Add This Property In .h file
And In .m File Add This
self.valueTypeChangedBlock = ^(NSarray NewArr) {
postsArr = NewArr
[self.newsTableView reloadData];
};
Where Ever U want to change the array from which table view Reload
Add the new array here
self.valueTypeChangedBlock (self.NewArray);
I'm checking if toViewController, which is the 2nd Tab in my tabBar, is of the class MatchCenterViewController, but the else statement is running instead, which tells me that it's not of that class.
I'm positive that the UIViewController in that tab is connected to MatchCenterViewController, so what else could cause this if statement to not work?
NSLog(#"numberOfMatches is 1");
UIViewController *toViewController = [self.tabBarController viewControllers][1];
NSLog(#"toViewController: %#", toViewController);
if ([toViewController isKindOfClass:[MatchCenterViewController class]]) {
NSLog(#"2nd matchcenter if statement works");
MatchCenterViewController *matchViewController = (MatchCenterViewController *)toViewController;
matchViewController.didAddNewItem = YES;
NSLog(#"alright they're set, time to switch");
}
else {
NSLog(#"toViewController is not MatchCenterViewController");
}
[self.tabBarController setSelectedIndex:1];
You can add NSLog(#"toViewController is of class: %#", NSStringFromClass([toViewController class]); and see the actual view controller class.
Or if didAddNewItem is a property that only MatchCenterViewController has, you can try this way:
if ([toViewController respondsToSelector:#selector(setDidAddNewItem:)]) {
// this is MatchCenterViewController
} else {
// this is not MatchCenterViewController
}
I've just implemented a commenting feature in my app. Ideally when someone leaves a comment, I'd like all notified people be able to swipe the push notification and open the app on that post.
I assume you want to open the concerned page directly. There are many ways to go about this, and it depends on how your app is laid out.
If you want to open an inner page upon app launch, you can programmatically trigger the segues that the user would otherwise need to make manually. (this ensures the back/home buttons work as opposed to loading the desired page directly).
Here's an excerpt from one of my own code, your use case may not be the same, but this is all i can do unless you give us more details.
- (BOOL) navigateToRespectiveSectionforPushNot:(NSDictionary*)pushNot
{
id rootVC = self.window.rootViewController;
NSLog(#"ROOT CLASS : %#", [rootVC class]);
if ([rootVC isKindOfClass:[SWRevealViewController class]])
{
NSLog(#"Root Class looking good... mission Navigate!!");
SWRevealViewController *homeVC = (SWRevealViewController*) rootVC;
NSString *category = [[pushNot objectForKey:pushPayloadKeyaps] objectForKey:pushPayloadKeyCategory];
NSString *subCat = [[pushNot objectForKey:pushPayloadKeyaps] objectForKey:pushPayloadKeySubCategory];
NSLog(#"category : %# , subcat : %#",category,subCat);
//The code for the page to which i'm supposed to navigate to is contained in the push notification payload
if ([category isEqualToString:pushCategoryItemChat])
{
[homeVC.rearViewController performSegueWithIdentifier:#"chatPush" sender:nil];
UINavigationController *nc = (UINavigationController*)homeVC.frontViewController;
NSLog(#"FrontView Class : %#",[nc.viewControllers[0] class]);
UITableViewController *tvc = (UITableViewController*)nc.viewControllers[0];
NSDictionary *send = #{chatPushTargetUserId:subCat,chatPushTargetUserName:#"",chatPushTargetUserImage:#""};
[tvc performSegueWithIdentifier:#"seguePushDemoVC" sender:send];
return YES;
}
//communityPush historyPush
else if ([category isEqualToString:pushCategoryItemCommunity])
{
if ([subCat isEqualToString:pushSubCatItemNewRequest])
{
[homeVC.rearViewController performSegueWithIdentifier:#"communityPush" sender:nil];
return YES;
}
else if ([subCat isEqualToString:pushSubCatItemAccepted])
{
[homeVC.rearViewController performSegueWithIdentifier:#"communityPush" sender:nil];
return YES;
}
}
else if ([category isEqualToString:pushCategoryItemHistory])
{
[homeVC.rearViewController performSegueWithIdentifier:#"historyPush" sender:nil];
return YES;
}
}
else
{
UIAlertView *whoa = [[UIAlertView alloc] initWithTitle:#"WHOA!!" message:#" That wasn't supposed to happen. You are not even logged in. Call 911..." delegate:nil cancelButtonTitle:#"mmKay.." otherButtonTitles:nil, nil];
[whoa show];
}
return NO;
}
I hope the code is self explanatory. cheers
I have a problem with the integration of game center in my app' which use iOS 6 SDK.
In fact I use the sample code from Apple, but it looks like incomplete :
I have tried this code :
-(void) authenticateLocalPlayer {
GKLocalPlayer* localPlayer =
[GKLocalPlayer localPlayer];
localPlayer.authenticateHandler =
^(UIViewController *loginVC,
NSError *error) {
[self setLastError:error];
if ([GKLocalPlayer localPlayer].authenticated)
{
// authentication successful
[self enableGameCenterForPlayer:[GKLocalPlayer localPlayer]];
}
else if (loginVC)
{
// player not logged in yet, present the vc
[self pauseGame];
[self presentLoginVC:loginVC];
}
else
{
// authentication failed, provide graceful fallback
[self disableGameCenter];
}
};
}
But the problem is that enableGameCenterForPlayer, pauseGame, presentLoginVC, disableGameCenter are not implemented methods, and it returns :
Instance method '-enableGameCenterForPlayer:' not found (return type defaults to 'id')
How can I fix this problem ?
Thanks
I use the method [self presentLoginVC:VC] to pass my UITabViewController or UINavigationController the viewController because the block below is not on the main thread.
localPlayer.authenticateHandler = ^(UIViewController *loginVC, NSError *error) {
When you are in a block you should be sure not to change UI elements because you really don't know when it will complete or where you will be in your app. There are probably many ways to do this, but this is my solution.
Below is my UITabBarController 'category' .m file (additions of methods for a class without subclassing) I create the method presentLoginVC and just have it call 'showGameCenterViewController' through my UITabBarController:
#import "UITabBarController+GameKitAdditions.h"
#implementation UITabBarController (GameKitAdditions)
-(void) showGameCenterViewController: (UIViewController *)VC {
[self presentViewController:VC animated:NO completion:nil];
}
-(void)dismissGameCenterViewController:(UIViewController *)VC {
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
As to the other functions:
-(void) enableGameCenterForPlayer:(GKLocalPlayer *) localPlayer;
-(void) disableGameCenter;
-(void) pauseGame;
They could be as simple as just setting a BOOL called enableGameCenter to YES or NO. To get around errors you can just add these prototypes to your .h file and then write the functions just to output something to NSLog() or something.
I have an UIPageViewController and a class which conforms to UIPageViewControllerDataSource and manages the UIViewControllers which UIPageViewController displays. It all runs well when turning the pages is initiated by the user. But now I want to do that programmatically.
I've found that there is a very similar question here on SO which answers the question for a static amount of ViewControllers: Is it possible to Turn page programmatically in UIPageViewController?
But I'm unable to adapt the answers to the use of UIPageViewControllerDataSource. I was thinking that there must be something like:
- (void)setCurrentViewController:(UIViewController *)viewController
I suppose I'm just overlooking something here.
The Method mentioned in the answer ( setViewControllers:direction:animated:completion: ) sets the current view controller and is exactly what you need.
With the first param you define the view controller(s), you want to display. Use one if you don't have a spine, if you book is left/right use two.
Old question but the following implementation calls delegate and dataSource methods for turning to previous or next page (But doesn't check if delegate or dataSource is set):
- (IBAction)navigateReverse:(id)sender
{
UIViewController *controller = [self.dataSource pageViewController:self viewControllerBeforeViewController:self.designViewControllers[self.currentIndex]];
if (!controller)
{
return;
}
NSArray *newViewControllers = #[controller];
NSArray *previousViewControllers = self.viewControllers;
__weak __typeof(self) weakSelf = self;
[self.delegate pageViewController:self willTransitionToViewControllers:newViewControllers];
[self setViewControllers:newViewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL finished) {
[weakSelf.delegate pageViewController:weakSelf didFinishAnimating:finished previousViewControllers:previousViewControllers transitionCompleted:finished];
}];
}
- (IBAction)navigateForward:(id)sender
{
UIViewController *controller = [self.dataSource pageViewController:self viewControllerAfterViewController:self.designViewControllers[self.currentIndex]];
if (!controller)
{
return;
}
NSArray *newViewControllers = #[controller];
NSArray *previousViewControllers = self.viewControllers;
__weak __typeof(self) weakSelf = self;
[self.delegate pageViewController:self willTransitionToViewControllers:newViewControllers];
[self setViewControllers:newViewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL finished) {
[weakSelf.delegate pageViewController:weakSelf didFinishAnimating:finished previousViewControllers:previousViewControllers transitionCompleted:finished];
}];
}
I've created a easy project with that functionality:
https://github.com/delarcomarta/AMPageViewController
Hope this can help you.