In my app I have an action sheet and one of its buttons opens the TWTweetComposeViewController modally. On iPhone simulator the cancel button on the tweet composer works fine and dismisses the view. However, on iPad simulator the cancel button does not work and the tweet composer view remains on the screen. It is even weirder because after pressing the cancel button, the keyboard retracts and the underlying views become active. It behaves as if the view has been dismissed but its is still there.
The code I used when the user pressed the action button is:
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *buttonTitle = [actionSheet buttonTitleAtIndex:buttonIndex];
if ([buttonTitle isEqualToString:#"Open in Safari"]){
[[UIApplication sharedApplication] openURL:[self.webView.request URL]];
}else if ([buttonTitle isEqualToString:#"Twitter"]){
if ([TWTweetComposeViewController canSendTweet]){
TWTweetComposeViewController *tweetSheet = [[TWTweetComposeViewController alloc] init];
[tweetSheet addURL:[self.webView.request URL]];
tweetSheet.completionHandler = ^(TWTweetComposeViewControllerResult result){
if (result == TWTweetComposeViewControllerResultCancelled){
[self dismissModalViewControllerAnimated:YES];
}
};
[self presentModalViewController:tweetSheet animated:YES];
}else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Twitter error" message:#"You can't send a tweet right now, make sure your device has an internet connection and you have at least one Twitter account setup" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}
}
Do you have any idea on how to solve this problem or is it a bug of the simulator?
P.S.: My app is a tabbar app and this code is called from one of of the view controller of the tab bar.
I'm having this same problem on the actual device. It turns out this is a bug in Apple's SDK for TWTweetComposeViewController.
See the bug report here on OpenRadar: http://openradar.appspot.com/radar?id=1484405.
When a completionHandler block is added to
TWTweetComposeViewController, the completion handler needs to call
-[UIViewController dismissModalViewControllerAnimated:], even though the view for the tweet composer dismisses itself with its cancel or
send buttons. Failure to do so causes touch events to not reach the
view that spawned the tweet composer.
Just thought I'd add how I'm doing things, even though this is not correctly following memory guidelines, it's a workaround:
[compose setCompletionHandler:^(TWTweetComposeViewControllerResult result){
dispatch_async(dispatch_get_main_queue(), ^{
if(self.delegate != nil)
{
if (result == TWTweetComposeViewControllerResultDone)
{
[self.delegate twitterOperation:TETwitterOperationTweet
completedSuccessfully:YES
withResponseString:#"Tweet Successful"];
}
else if(result == TWTweetComposeViewControllerResultCancelled)
{
[self.delegate twitterOperation:TETwitterOperationTweet
completedSuccessfully:NO
withResponseString:#"Tweet Cancelled"];
}
}
// Dismiss per Apple's Twitter example
[self.shownInViewController dismissViewControllerAnimated:YES
completion:nil];
// Yuck. But it's necessary.
[compose release];
});
Related
so I have my app setup with share action button that shares the current URL to Twitter or Facebook however when I click the Facebook button it shows a Twiiter share sheet. The (Tiwtter option works fine)
After I click the FACEBOOK option, when testing on iPhone the standard twiiter share sheet appears.
- (IBAction)social:(id)sender {
UIActionSheet *share = [[UIActionSheet alloc] initWithTitle:#"Pass on the news!" delegate:self cancelButtonTitle:#"OK" destructiveButtonTitle:nil otherButtonTitles:#"Post to Twitter", #"Post to Facebook", nil];
//You must show the action sheet for the user to see it.
[share showInView:self.view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
//Each button title we gave to our action sheet is given a tag starting with 0.
if (actionSheet.tag == 0) {
//Check Twitter accessibility and at least one account is setup.
if([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]) {
SLComposeViewController *tweetSheet =[SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
[tweetSheet addURL:self.newsItemWebView.request.URL]; //This is setting the initial text for our share card.
[tweetSheet setInitialText:#"Check out this article I found using the 'Pass' iPhone app:, "];
//Brings up the little share card with the test we have pre defind.
[self presentViewController:tweetSheet animated:YES completion:nil];
} else {
//This alreat tells the user that they can't use built in socal interegration and why they can't.
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Sorry" message:#"You can't send a tweet right now, make sure you have at least one Twitter account setup and your device is using iOS6 or above!." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
} else if (actionSheet.tag == 1) {
//Check Facebook accessibility and at least one account is setup.
if([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {
SLComposeViewController *facebookSheet =[SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[facebookSheet addURL:self.newsItemWebView.request.URL];
//This is setting the initial text for our share card.
[facebookSheet setInitialText:#"Check out this article I found using the 'Pass' iPhone app:"];
//Brings up the little share card with the test we have pre defind.
[self presentViewController:facebookSheet animated:YES completion:nil];
} else {
//This alreat tells the user that they can't use built in socal interegration and why they can't.
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Sorry" message:#"You can't post a Facebook post right now, make sure you have at least one Facebook account setup and your device is using iOS6 or above!." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}
}
You are using actionSheet.tag which will be the same for the entiere actionsheet.
If you want to know which button is pressed you should use buttonIndex in your if statements.
Even better is to use UIActivityViewController.
NSArray *items = #[self.newsItemWebView.request.URL];
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
activityViewController.completionHandler = ^(NSString *activityType, BOOL completed) {
// do any thing you want when the user finishes the share activity.
// Like present a message that that activity has completed or not.
};
[self presentViewController:activityViewController animated:YES completion:nil];
I'm working on a twitter app and i want to immediately display tweet after sending. I tried to use reloadData, but it didn't work. Can anyone help me? Thank you!
Here's my code:
- (IBAction)postNewTweet:(id)sender {
if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
{
SLComposeViewController *tweetSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
[tweetSheet setInitialText:#""];
[self presentViewController:tweetSheet animated:YES completion:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tweetTableView reloadData];
});
}
else
{
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#"Sorry"
message:#"You can't send a tweet right now, make sure your device has an internet connection and you have at least one Twitter account setup"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
}
Blaze, you are reloading the table view right after presenting the compose sheet, so it reload the table right there - and you are passing the completion block as nil, so nothing will happen when user finishes tweeting.
Also, you need to be sure that you are reloading not only the table, but the array or any other object that is the source of tweetTableView.
For example:
When calling the tweet compose sheet:
[self presentViewController:tweetSheet animated:YES completion:^(SLComposeViewControllerResult result)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTweets];
});
}];
And then, you reload the tweets source and only then the table
-(void)reloadTweets
{
tweetsArray = .... //load the tweets from the internet, and only then reload the table
[self.tweetTableView reloadData];
}
I have a UIAlertView that asks a user whether they want to copy a bunch of contacts. After the user clicks "Continue," I want the AlertView to disappear to be replaced by a UIProgressView that shows the rate of loading.
Everything functions properly except that the alert remains on the XIB after the user clicks Continue, and it doesn't disappear until the entire process has run. As soon as it disappears, the UIProgressView bar appears but by then it has reached 100%.
How do I clear the UIAlertView off the screen immediately and show the progress bar growing instead.
Here's the code. Thanks for help.
-(IBAction)importAllAddressBook:(id)sender {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Import ALL Contacts" message:#"Do you want to import all your Address Book contacts? (Please note that only those with a first and last name will be transfered)" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Continue", nil];
[alert show];
[alert release];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
NSLog(#"NO STOP");
return;
}else {
NSLog(#"YES CONTINUE");
importAllContactsProgress.hidden = NO; // show progress bar at 0
[self importAllMethod];
}
}
// method to import all Address Book
-(void) importAllMethod {
importAllContactsProgress.progress = 0.0;
load names etc etc
You'll want to perform the perform the import on a background thread:
[self performSelectorInBackground:#selector(importAllMethod)];
Your import method is blocking the main thread and preventing any UI actions from occurring.
I'm having a similar issue to Anthony Chan's question, and after trying every suggested solution, I'm still stuck.
Somehow, only after interacting with my UIAlertView, I'm unable to dismiss the keyboard in another view of my app. It's as though the Alert is breaking my UITextField's ability to resignFirstResponder. Below I instantiate my UIAlertView, which then calls its didDismissWIthButtonIndex method. Then, I call the showInfo method, which loads another UIViewController.
UIAlertView *emailFailAlert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"error message text."
delegate:self
cancelButtonTitle:#"Not now"
otherButtonTitles:#"Settings", nil];
[emailFailAlert setTag:2];
[emailFailAlert show];
[emailFailAlert release];
Once the 'Settings' option is pressed, I'm calling this method:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if ([alertView tag] == 2) {
if (buttonIndex == 1){
[self showInfo:nil];
}
}
}
My showInfo method loads the other ViewController, via the code below:
- (IBAction)showInfo:(id)sender {
FlipsideViewController *fscontroller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideView" bundle:nil];
fscontroller.delegate = self;
fscontroller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:fscontroller animated:YES];
[fscontroller release];
}
Upon clicking any textField in this Flipside VC, I'm unable to dismiss the keyboard as I normally can with - (BOOL)textFieldShouldReturn:(UITextField *)textField, and [textField resignFirstResponder]. I've omitted this code bc this question is getting long, but I'm happy to post if necessary.
The interesting part is that if I comment out the [self showInfo:nil] call made when the button is clicked and call it by clicking a test button (outside the alertView didDismissWithButtonIndex: method), everything works fine. Any idea what's happening here?
Thanks in advance!
When an alert, with more than one dismissal option, is called above a keyboard - the keyboard becomes un-dismissible with resignFirstResponder on the active textfield;
You will need to dismiss the keyboard before showing the alert.
Assuming your UITextField is called myTextField;
[myTextField resignFirstResponder]; //That's the only line I added
UIAlertView *emailFailAlert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"error message text."
delegate:self
cancelButtonTitle:#"Not now"
otherButtonTitles:#"Settings", nil];
[emailFailAlert setTag:2];
[emailFailAlert show];
[emailFailAlert release];
I hope this helps anyone who had to deal with this oddly obscure issue.
You should not call alertView:didDismissWithButtonIndex: directly. This delegate method will be executed automatically in all cases after the alert has disappeared. Otherwise the code will be run twice!
I used to show a splash screen which in background load some data from web, I also check that if the location of the user is changed from one city to another city I want to show in alert to the user with the message that you are now in "CityName" would you like to see data from this city?
I have tabbed application and I have presented the splash screen as follow in the app delegate class.
SplashViewController *controller = [[SplashViewController alloc] initWithNibName:nil bundle:nil];
tabBarController.view.frame = [[UIScreen mainScreen] bounds];
controller.tabBarController = self.tabBarController;
[application setStatusBarStyle:UIStatusBarStyleBlackOpaque];
[window addSubview:controller.view ];
//[window addSubview:tabBarController.view ];
[self.tabBarController presentModalViewController:controller animated:YES];
[window makeKeyAndVisible];
[controller release];
Now when I show the alert screen it crash the application with "EXC_BAD_ACCESS" message and the stack trace show that _buttonClick is released in UIAlertView class.
Please advise what should I do, I also tried with UIActionSheet but the same problem with this thing too.
I think there is some problem with the model thing with the current view (SplashView).
Thanks in advance.
Are you trying to display your UIAlertView inside of your SplashViewController viewDidAppear? If not, I would try that first. I would also make sure you have your UIAlertView clickedButtonAtIndex method setup properly to try and trap what is going on.
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Your Location Has Changed"
message:#"Your location has changed since you last opened opened THEAPP. Would you like to refresh your data?" delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
alert.tag = 1;
[alert show];
[alert autorelease];
Then for the clickedButtonAtIndex method:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
//--NSLog(#"You clicked button #%i",buttonIndex);
if (alertView.tag == 1){
if (buttonIndex == 0) {
//--NSLog(#"CANCEL");
} else if (buttonIndex == 1) {
//--NSLog(#"OK");
}
}
}
Doing all of this on a splash screen should be fine as long as you take into account the HIG's requirements for using the users location. Hope this helps!
I resolve this issue, the problem was that my Splash View was a modeled view and invoked by
[self.tabBarController presentModalViewController:controller animated:YES];
what I did that I shifted the data downloading to another view controller and there I can show alerts, and can handle that