UIAlertView showing up only after it's dismissed - objective-c

I've been trying to figure this out for 2 days now, and before anyone posts another stackoverflow question, I've read them all and none of them cover my problem exactly:
I have a CoreData app that updates dynamically. Now during the update I want an UIAlertView to pop up saying that an update is being downloaded.
So here's the important code:
AppDelegate:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[myUpdater checkForUpdatesInContext:self.managedObjectContext];
}
_
Updater Class:
- (void)checkForUpdatesInContext:(NSManagedObjectContext *)myManagedObjectContext
{
[self loadUpdateTime];
NSLog(#"Update start");
NSDate *now = [NSDate dateWithTimeIntervalSinceNow:[[NSTimeZone localTimeZone] secondsFromGMT]];
if ([now timeIntervalSinceDate:updateTime] < UPDATE_TIME_INTERVAL)
{
return;
}
[self showAlertViewWithTitle:#"Update"];
... //updating process
[self.alertView dismissWithClickedButtonIndex:0 animated:YES];
NSLog (#"Update done");
}
- (void) showAlertViewWithTitle:(NSString *)title
{
self.alertView = [[UIAlertView alloc] initWithTitle:title message:#"Daten werden aktualisiert..." delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
... //design the alertView
[self.alertView show];
NSLog (#"AlertView shows");
}
So here is what happens when I run this:
Launch image shows
NSLog "Update starts" fires
NSLog "AlertView shows" fires
Screen dims but no AlertView is shown
Update is running
NSLog "Update done" fires
Launch image goes away and TabBarController shows up
UIAlertView shows up and is dismissed right away and the dimmed screen returns to normal
What I would like to have happen:
Launch image
TabBarController shows up
Screen dims and UIAlertView shows
Update is running
UIAlertView gets dismissed and dimmed screen returns to normal
I know it's something with the UI Thread and the main Thread and stuff.. But I tried every combination it seems but still not the expected result. Please help :)
EDIT:
HighlightsViewController Class:
- (void)viewDidLoad
{
[super viewDidLoad];
self.updater = [[Updater alloc] init];
[updater checkForUpdatesInContext:self.managedObjectContext];
... // other setup stuff nothing worth mentioning
}
Is this the right place to call [super viewDidLoad]? Because it still doesn't work like this, still the update is being done while the Launch Image is showing on the screen. :-(( I'm about to give this one up..

Here you go, in this prototype things work exactly how you want them to.
Header:
#import <UIKit/UIKit.h>
#interface AlertViewProtoViewController : UIViewController
{
}
- (void) showAlertViewWithTitle:(NSString *)title;
- (void) checkForUpdatesInContext;
- (void) update;
- (void)someMethod;
- (void)someOtherMethod;
#end
#import "AlertViewProtoViewController.h"
Class:
#implementation AlertViewProtoViewController
UIAlertView *alertView;
bool updateDone;
UILabel *test;
bool timershizzle;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor yellowColor];
UILabel *test = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
test.backgroundColor = [UIColor blueColor];
[self.view addSubview:test];
[self performSelector:#selector(checkForUpdatesInContext) withObject:nil afterDelay:0.0];
}
- (void)update
{
//NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //commented for auto ref counting
NSLog(#"update start");
//your update stuff
NSLog(#"update end");
updateDone = YES;
//[pool release];
}
- (void)checkForUpdatesInContext//:(NSManagedObjectContext *)myManagedObjectContext
{
//[self loadUpdateTime];
NSLog(#"Update start");
NSDate *now = [NSDate dateWithTimeIntervalSinceNow:[[NSTimeZone localTimeZone] secondsFromGMT]];
// if ([now timeIntervalSinceDate:updateTime] < UPDATE_TIME_INTERVAL)
// {
// return;
// }
[self showAlertViewWithTitle:#"Update"];
//[self setManagedObjectContext:myManagedObjectContext];
[self performSelector:#selector(someMethod) withObject:nil afterDelay:0.0];
[self performSelector:#selector(someOtherMethod) withObject:nil afterDelay:0.0];
}
-(void)someOtherMethod
{
while (!updateDone) {
// NSLog(#"waiting...");
}
[alertView dismissWithClickedButtonIndex:0 animated:YES];
NSLog (#"Update done");
self.view.backgroundColor = [UIColor greenColor];
}
-(void)someMethod
{
[self performSelectorInBackground:#selector(update) withObject:nil];
}
- (void) showAlertViewWithTitle:(NSString *)title
{
alertView = [[UIAlertView alloc] initWithTitle:title message:#"Daten werden aktualisiert..." delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
alertView.frame = CGRectMake(100, 100, 200, 200);
alertView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:alertView];
[self.view setNeedsDisplay];
NSLog (#"AlertView shows");
}
#end
You should adjust were needed for your own purposes but it works.

You are starting a background thread and then dismissing the alert immediately. I would suggest that you might use an NSNotification, posted from the background task, and received in whichever controller starts the alert, triggering a method that dismissed the alert.
I find the UIAlertView interface unsuitable for this type of user notice, and prefer to use a semi-transparent overlay view with a UIActivityIndicatorView, plus an informing message for the user.

You are doing a:
- (void)applicationDidBecomeActive:(UIApplication *)application
Isn't it so that the alertview you want to show needs a view to be loaded which isn't active yet at this point? See: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAlertView_Class/UIAlertView/UIAlertView.html
Similar question? UIAlertView starts to show, screen dims, but it doesn't pop up until it's too late!

Related

Code seem do play as the order as I want

- (void)viewDidLoad {
[super viewDidLoad];
[self pingSplash];
UIViewController *next = [[self storyboard] instantiateViewControllerWithIdentifier:#"ViewController"];
[self.navigationController pushViewController:next animated:YES];
}
I mean to finish pinSplash then pushViewController, but it directly goto ViewController page, even without finishing pingSplash, what is a good way to do that kind of job?
For the pingSplash part:
- (void) pingSplash
{
SKSplashIcon *pingSplashIcon = [[SKSplashIcon alloc] initWithImage:[UIImage imageNamed:#"ping.png"] animationType:SKIconAnimationTypePing];
_splashView = [[SKSplashView alloc] initWithSplashIcon:pingSplashIcon backgroundColor:[UIColor grayColor] animationType:SKSplashAnimationTypeBounce];
_splashView.animationDuration = 5.0f;
[self.view addSubview:_splashView];
[_splashView startAnimation];
}
The pingSplash method returns as soon as it starts the animation which is why you end up pushing the view controller too soon.
There are a few ways to solve this. One way would be to pass a block to the pingSplash method that is run after an appropriate delay.
Here's one way:
Update the pingSplash method to take a completion handler block that is run after the 5 second delay.
- (void) pingSplash:(void (^)(void))completion
{
SKSplashIcon *pingSplashIcon = [[SKSplashIcon alloc] initWithImage:[UIImage imageNamed:#"ping.png"] animationType:SKIconAnimationTypePing];
_splashView = [[SKSplashView alloc] initWithSplashIcon:pingSplashIcon backgroundColor:[UIColor grayColor] animationType:SKSplashAnimationTypeBounce];
_splashView.animationDuration = 5.0f;
[self.view addSubview:_splashView];
[_splashView startAnimation];
if (completion) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_splashView.animationDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
completion();
});
}
}
Then update your viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
[self pingSplash:^{
UIViewController *next = [[self storyboard] instantiateViewControllerWithIdentifier:#"ViewController"];
[self.navigationController pushViewController:next animated:YES];
}];
}

iOS 7 UITextView's native links don't cancel touch

I think I've encountered a bug in UITextView in iOS 7, but I haven't been able to find any reference to it or discussion, so trying to confirm here that I'm not missing something:
The Bug
I have a UITextView setup as follows:
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(20, 100, 280, 140)];
textView.editable = NO;
textView.dataDetectorTypes = UIDataDetectorTypeLink;
textView.backgroundColor = [UIColor lightGrayColor];
textView.delegate = self;
textView.text = [NSString stringWithFormat:#"This is a UITextView in a UIViewController. It has editable %d, selectable %d, dataDectorTypes %d (UIDataDetectorTypeLink), and a link! http://www.google.com.\n\nIf you click the link, it works normally. If you press, then drag your finger away to cancel the touch, the highlight goes away but the action still fires. Shouldn't the action not fire?", textView.editable, textView.selectable, textView.dataDetectorTypes];
[self.view addSubview:textView];
The textview text contains a link, which if you click works as expected: it calls back to the delegate, which just pops up an alert:
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"You clicked the link!" message:#"You clicked the link. Maybe you tried to move away to cancel the touch, but you clicked it anyway." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
return NO;
}
The problem is if you press the link, then try to drag away to cancel the touch: the link's highlight state goes away but the delegate fires as soon as your finger leaves the screen.
I haven't been able to find a way to tell the textview not to fire when it should have cancelled, or to detect that it should have cancelled.
I'm convinced this is a bug because the visual press/cancel state and the action-firing don't match.
Work-around (horrible horrible invasive work-around)
I got around this by subclassing UIApplication to track all screen touches. By holding on to the last touch location, I can, in the delegate, map the location to the link to determine whether or not it should be considered valid:
MyCustomApplication.m
#implementation MyCustomApplication
static CGPoint lastTouchVar;
- (void)sendEvent:(UIEvent *)event {
if (event.type == UIEventTypeTouches) {
lastTouchVar = [((UITouch*)event.allTouches.anyObject) locationInView:[[[UIApplication sharedApplication] delegate] window]];
}
[super sendEvent:event];
}
+ (CGPoint)lastTouch {
return lastTouchVar;
}
#end
(use it in main.m)
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, #"MyCustomApplication", NSStringFromClass([MyAppDelegate class]));
}
}
And map it...
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
CGPoint lastTouchWRTTextView = [[[[UIApplication sharedApplication] delegate] window] convertPoint:[MyCustomApplication lastTouch] toView:textView];
lastTouchWRTTextView.x -= textView.textContainerInset.left;
lastTouchWRTTextView.y -= textView.textContainerInset.top;
if (CGRectContainsPoint(CGRectMake(0, 0, textView.frame.size.width, textView.frame.size.height), lastTouchWRTTextView)) {
int characterIndexForPoint = [textView.textContainer.layoutManager characterIndexForPoint:lastTouchWRTTextView inTextContainer:textView.textContainer fractionOfDistanceBetweenInsertionPoints:nil];
if (NSLocationInRange(characterIndexForPoint, characterRange)) {
// It's a real tap! Do something!
}
}
return NO;
}

Tab bar Controller and Facebook Connect Issues Xcode 4.3 IOS 5.1

I am trying to connect to the facebook and take the users name and picture, so far i can connect and fetch user name . i have a working code can be downloaded from here (single view application)
But if i put a tab bar controller to the code, it can not receive a response from facebook.
I add a tabbar controller programatically like below code
in app.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Regular Code
/*self.viewController = [[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil] autorelease];
self.window.rootViewController = self.viewController;
*/
//Tab bar controller code
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
UIViewController *viewController1 = [[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil] autorelease];
self.rootController = [[[UITabBarController alloc] init] autorelease];
self.rootController.viewControllers = [NSArray arrayWithObjects:viewController1, nil];
self.window.rootViewController = self.rootController;
[self.window makeKeyAndVisible];
return YES;
}
// This method will be used for iOS versions greater than 4.2.
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [[_viewController facebook] handleOpenURL:url];
}
If i put a breaking point at line handleOpenurl In NSUrl there is this= fbAPPID://authorize/#access_token=BABABSbbsabsbabb
I guess access_token means i have succesfully loged in . So tab bar doesnt block me to login put blokcs my facebook methods in Viewcontroller.m
Without tab bar all methods in Viewcontroller.m works great when i add tab bar and breakpoints none of the facebook methods below works. I dont get any errors though.
-(void)request:(FBRequest *)request didLoad:(id)result
-(void)fbDidLogin
ViewController.m
#import "ViewController.h"
#import "FBConnect.h"
#import "Facebook.h"
#implementation ViewController
#synthesize btnLogin;
#synthesize btnPublish;
#synthesize lblUser;
#synthesize actView;
#synthesize facebook;
#synthesize permissions;
#synthesize isConnected;
#synthesize imageView;
// The alert view that will be shown while the game will upload to facebook.
UIAlertView *msgAlert;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
-(void)checkForPreviouslySavedAccessTokenInfo{
// Initially set the isConnected value to NO.
isConnected = NO;
// Check if there is a previous access token key in the user defaults file.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"] &&
[defaults objectForKey:#"FBExpirationDateKey"]) {
facebook.accessToken = [defaults objectForKey:#"FBAccessTokenKey"];
facebook.expirationDate = [defaults objectForKey:#"FBExpirationDateKey"];
// Check if the facebook session is valid.
// If it’s not valid clear any authorization and mark the status as not connected.
if (![facebook isSessionValid]) {
[facebook authorize:nil];
isConnected = NO;
}
else {
isConnected = YES;
}
}
}
-(void)setLoginButtonImage{
UIImage *imgNormal;
UIImage *imgHighlighted;
UIImageView *tempImage;
// Check if the user is connected or not.
if (!isConnected) {
// In case the user is not connected (logged in) show the appropriate
// images for both normal and highlighted states.
imgNormal = [UIImage imageNamed:#"LoginNormal.png"];
imgHighlighted = [UIImage imageNamed:#"LoginPressed.png"];
}
else {
imgNormal = [UIImage imageNamed:#"LogoutNormal.png"];
imgHighlighted = [UIImage imageNamed:#"LogoutPressed.png"];
}
// Get the screen width to use it to center the login/logout button.
// We’ll use a temporary image view to get the appopriate width and height.
float screenWidth = [UIScreen mainScreen].bounds.size.width;
tempImage = [[UIImageView alloc] initWithImage:imgNormal];
[btnLogin setFrame:CGRectMake(screenWidth / 2 - tempImage.frame.size.width / 2, btnLogin.frame.origin.y, tempImage.frame.size.width, tempImage.frame.size.height)];
// Set the button’s images.
[btnLogin setBackgroundImage:imgNormal forState:UIControlStateNormal];
[btnLogin setBackgroundImage:imgHighlighted forState:UIControlStateHighlighted];
// Release the temporary image view.
[tempImage release];
}
-(void)showActivityView{
// Show an alert with a message without the buttons.
msgAlert = [[UIAlertView alloc] initWithTitle:#"My test app" message:#"Please wait..." delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
[msgAlert show];
// Show the activity view indicator.
actView = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 40.0, 40.0)];
[actView setCenter:CGPointMake(160.0, 350.0)];
[actView setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleGray];
[self.view addSubview:actView];
[actView startAnimating];
}
-(void)stopShowingActivity{
[actView stopAnimating];
[msgAlert dismissWithClickedButtonIndex:0 animated:YES];
}
-(void)saveAccessTokenKeyInfo{
// Save the access token key info into the user defaults.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Set the permissions.
// Without specifying permissions the access to Facebook is imposibble.
permissions = [[NSArray arrayWithObjects:#"read_stream", #"publish_stream", nil] retain];
// Set the Facebook object we declared. We’ll use the declared object from the application
// delegate.
facebook = [[Facebook alloc] initWithAppId:#"331327710279153" andDelegate:self];
// Check if there is a stored access token.
[self checkForPreviouslySavedAccessTokenInfo];
// Depending on the access token existence set the appropriate image to the login button.
[self setLoginButtonImage];
// Specify the lblUser label's message depending on the isConnected value.
// If the access token not found and the user is not connected then prompt him/her to login.
if (!isConnected) {
[lblUser setText:#"Tap on the Login to connect to Facebook"];
}
else {
// Get the user's name from the Facebook account.
[facebook requestWithGraphPath:#"me" andDelegate:self];
}
// Initially hide the publish button.
[btnPublish setHidden:YES];
}
- (void)viewDidUnload
{
[self setBtnLogin:nil];
[self setLblUser:nil];
[self setBtnPublish:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
-(void)request:(FBRequest *)request didReceiveResponse:(NSURLResponse *)response {
// Keep this just for testing purposes.
NSLog(#"received response");
}
-(void)request:(FBRequest *)request didLoad:(id)result{
// With this method we’ll get any Facebook response in the form of an array.
// In this example the method will be used twice. Once to get the user’s name to
// when showing the welcome message and next to get the ID of the published post.
// Inside the result array there the data is stored as a NSDictionary.
if ([result isKindOfClass:[NSArray class]]) {
// The first object in the result is the data dictionary.
result = [result objectAtIndex:0];
}
// Check it the “first_name” is contained into the returned data.
if ([result objectForKey:#"first_name"]) {
// If the current result contains the "first_name" key then it's the user's data that have been returned.
// Change the lblUser label's text.
[lblUser setText:[NSString stringWithFormat:#"Welcome %#!", [result objectForKey:#"first_name"]]];
// Show the publish button.
[btnPublish setHidden:NO];
}
else if ([result objectForKey:#"id"]) {
// Stop showing the activity view.
[self stopShowingActivity];
// If the result contains the "id" key then the data have been posted and the id of the published post have been returned.
UIAlertView *al = [[UIAlertView alloc] initWithTitle:#"My test app" message:#"Your message has been posted on your wall!" delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[al show];
[al release];
}
}
-(void)request:(FBRequest *)request didFailWithError:(NSError *)error{
NSLog(#"%#", [error localizedDescription]);
// Stop the activity just in case there is a failure and the activity view is animating.
if ([actView isAnimating]) {
[self stopShowingActivity];
}
}
-(void)fbDidLogin{
// Save the access token key info.
[self saveAccessTokenKeyInfo];
// Get the user's info.
[facebook requestWithGraphPath:#"me" andDelegate:self];
}
-(void)fbDidNotLogin:(BOOL)cancelled{
// Keep this for testing purposes.
//NSLog(#"Did not login");
UIAlertView *al = [[UIAlertView alloc] initWithTitle:#"My test app" message:#"Login cancelled." delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[al show];
}
-(void)fbDidLogout{
// Keep this for testing purposes.
//NSLog(#"Logged out");
// Hide the publish button.
[btnPublish setHidden:YES];
}
- (IBAction)LoginOrLogout {
// If the user is not connected (logged in) then connect.
// Otherwise logout.
if (!isConnected) {
[facebook authorize:permissions];
// Change the lblUser label's message.
[lblUser setText:#"Please wait..."];
}
else {
[facebook logout:self];
[lblUser setText:#"Tap on the Login to connect to Facebook"];
}
isConnected = !isConnected;
[self setLoginButtonImage];
}
- (IBAction)Publish {
// Show the activity indicator.
[self showActivityView];
// Create the parameters dictionary that will keep the data that will be posted.
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"My test app", #"name",
#"http://www.google.com", #"link",
#"FBTestApp app for iPhone!", #"caption",
#"This is a description of my app", #"description",
#"Hello!\n\nThis is a test message\nfrom my test iPhone app!", #"message",
nil];
// Publish.
// This is the most important method that you call. It does the actual job, the message posting.
[facebook requestWithGraphPath:#"me/feed" andParams:params andHttpMethod:#"POST" andDelegate:self];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)dealloc {
[btnLogin release];
[lblUser release];
[btnPublish release];
[actView release];
[facebook release];
[permissions release];
[super dealloc];
}
#end
I am confused how can i make this tab bar controlling working.
I have changed the design
Now Login view is displayed first then it continues to real app which has tab bar controller.
Issue was _viewcontroller facebook , when _viewcontroller has been called it was not returning any thing
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [[_viewController facebook] handleOpenURL:url];
}
So I changed my code(especially _viewcontroller line) to below code
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
UIViewController * viewController1 = [[[VoteMe alloc] initWithNibName:#"VoteMe" bundle:nil] autorelease];
UIViewController *viewController2 = [[[PostController alloc] initWithNibName:#"PostController" bundle:nil] autorelease];
UIViewController *viewController3 = [[[FriendsController alloc] initWithNibName:#"FriendsController" bundle:nil] autorelease];
UIViewController *viewController4 = [[[Responses alloc] initWithNibName:#"Responses" bundle:nil] autorelease];
UIViewController *viewController5 = [[[User alloc] initWithNibName:#"User" bundle:nil] autorelease];
self.rootController = [[[UITabBarController alloc] init] autorelease];
self.rootController.viewControllers = [NSArray arrayWithObjects:viewController1,viewController2,viewController3,viewController4,viewController5, nil];
self.window.rootViewController = self.rootController;
[self.window makeKeyAndVisible];
loginView=[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self.window addSubview:loginView.view];
return YES;
}
// This method will be used for iOS versions greater than 4.2.
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [[loginView facebook] handleOpenURL:url];
}
It works fine

Xcode Unrecognized Selector Error

I've been having a ton of issues with this section of an app that I'm writing, I'm sure people here are getting sick of me so I'm going to try and solve all my questions in this post. I'm working on an app that behaves like a photo gallery, and I'm implementing the option to have the user delete photos from their gallery. To accomplish this, I decided to place an invisible button over each picture. When the user hits an "Edit" button, the hidden delete buttons over each picture become active. I'm using the same IBOutlet over each of the hidden buttons for simplicity, and I've tagged each button appropriately in Interface Builder. When the user taps the button over the picture, an alert view appears asking if they really want to delete it. If they click yes, I call removeObjectAtIndex and delete the image from the UI. But, when I click "Yes" in the alert view, I get an error from Xcode stating:
2012-04-04 11:26:40.484 AppName[608:f803] -[UIButton setImage:]: unrecognized selector sent to instance 0x6a922c0
2012-04-04 11:26:40.485 AppName[608:f803] Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIButton setImage:]: unrecognized selector sent to instance 0x6a922c0'*
I've been trying to figure out what is causing this for a few hours now to no avail. I'm not setting the image of a UIButton anywhere in my code. I did in IB, but I simply set the buttons types to Custom so that they appear invisible. I will post my entire file below, I can't find any issues in my code, so any help is much appreciated! Thanks.
EDIT Here is the current version of the code:
- (IBAction)grabImage {
self.imgPicker = [[UIImagePickerController alloc] init];
self.imgPicker.delegate = self;
self.imgPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
_popover = [[UIPopoverController alloc] initWithContentViewController:imgPicker];
[_popover presentPopoverFromRect:self.imageView.bounds inView:self.imageView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
else {
[self presentModalViewController:imgPicker animated:YES];
}
[self.imgPicker resignFirstResponder];
}
// Sets the image in the UIImageView
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)img editingInfo:(NSDictionary *)editInfo {
if (imageView.image == nil) {
imageView.image = img;
self.array = [NSMutableArray arrayWithObject:[NSData dataWithData:UIImagePNGRepresentation(imageView.image)]];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView2.image == nil) {
imageView2.image = img;
NSLog(#"The image is a %#", imageView);
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView2.image)]];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView3.image == nil) {
imageView3.image = img;
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView3.image)]];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
if (imageView4.image == nil) {
imageView4.image = img;
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView4.image)]];
[picker dismissModalViewControllerAnimated:YES];
[self.popover dismissPopoverAnimated:YES];
return;
}
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Photo Gallery", #"Photo Gallery");
self.tabBarItem.image = [UIImage imageNamed:#"42-photos.png"];
}
return self;
}
////start of saving////
- (void)applicationDidEnterBackground:(UIApplication*)application {
NSLog(#"Image on didenterbackground: %#", imageView);
self.array = [NSMutableArray arrayWithObject:[NSData dataWithData:UIImagePNGRepresentation(imageView.image)]];
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView2.image)]];
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView3.image)]];
[self.array addObject:[NSData dataWithData:UIImagePNGRepresentation(imageView4.image)]];
[self.user setObject:self.array forKey:#"images"];
[user synchronize];
}
- (void)viewDidLoad
{
self.user = [NSUserDefaults standardUserDefaults];
NSLog(#"It is %#", self.user);
self.array = [[self.user objectForKey:#"images"]mutableCopy];
imageView.image = [[UIImage alloc] initWithData:[self.array objectAtIndex:0]];
imageView2.image = [[UIImage alloc] initWithData:[self.array objectAtIndex:1]];
imageView3.image = [[UIImage alloc] initWithData:[self.array objectAtIndex:2]];
imageView4.image = [[UIImage alloc] initWithData:[self.array objectAtIndex:3]];
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:app];
backToGalleryButton.hidden = YES;
tapToDeleteLabel.hidden = YES;
deleteButton1.hidden = YES;
[super viewDidLoad];
}
///// shows the hidden and invisible "delete" button over each photo.
- (IBAction)editButtonPressed:(id)sender {
grabButton.hidden = YES;
editButton.hidden = YES;
backToGalleryButton.hidden = NO;
tapToDeleteLabel.hidden = NO;
deleteButton1.hidden = NO;
}
////
// This is when the user taps on the image to delete it.
- (IBAction)deleteButtonPressed:(id)sender {
NSLog(#"Sender is %#", sender);
UIAlertView *deleteAlertView = [[UIAlertView alloc] initWithTitle:#"Delete"
message:#"Are you sure you want to delete this photo?"
delegate:self
cancelButtonTitle:#"No"
otherButtonTitles:#"Yes", nil];
[deleteAlertView show];
int imageIndex = ((UIButton *)sender).tag;
deleteAlertView.tag = imageIndex;
}
- (void)alertView: (UIAlertView *) alertView
clickedButtonAtIndex: (NSInteger) buttonIndex
{
if (buttonIndex != [alertView cancelButtonIndex]) {
NSLog(#"User Clicked Yes. Deleting index %d of %d", alertView.tag, [array count]);
NSLog(#"The tag is %i", alertView.tag);
[self.array removeObjectAtIndex: alertView.tag];
NSLog(#"After deleting item, array count = %d", [array count]);
NSLog(#"Returned view is :%#, in view: %#", [self.view viewWithTag:alertView.tag], self.view);
((UIImageView *)[self.view viewWithTag:alertView.tag]).image =nil;
}
[self.user setObject:self.array forKey:#"images"];
}
Update: I added breakpoints, and discovered that ((UIImageView *)[self.view viewWithTag:alertView.tag]).image =nil; is the line that is causing the crash, I still can't figure out why though.
I don't see setImage: being called in any of that code you pasted into your question, but I can tell you this: UIButton's setImage method actually requires a button state as a second parameter.
e.g.
[UIButton setImage: forState:]
I've linked Apple's documentation for you.
If your calls to setImage: don't have a second parameter, that would explain the "unrecognized selector error" you're seeing.
This is an extension to #MichaelDautermann , who makes a very good point that a UIButton has a setImage:forState: method, but no setImage method. However, you ARE calling set image. Not on a button, but when you say imageView.image and imageView2.image. This invokes the setter method for the image view. I would set a breakpoint (or use NSLog and the %# item) to ensure that imageView is in fact an imageView and not a button. If it somehow changed from under you, this could be causing the issue. Simply set a break point at those two lines and see if you even make it past them.
Additionally, if Xcode isn't popping you over to which line is actually causing the issue, check your crash logs. Symbolicated, the log will give you the line number. Or, a less direct approach would be to set breakpoints at the ends of the methods you provided in your answer, and see how many you get past. Once you crash, you can narrow down which method is causing you grief, and then start setting break points within the method until you get to the line in question.
UPDATE:
You said in the comments that ((UIImageView *)[self.view viewWithTag:alertView.tag]).image =nil is what you have, but self.view is a UIControl. Changing the cast won't affect the result. Is the UIImageView whose image you are trying to delete a subview of a UIControl? If not, you're never going to get the image view back from viewWithTag. Generally, self.view refers to a view controller's view, so if you're getting a UIControl, my assumption is either your whole view is a UIControl, or you're doing this in the wrong class. Which class are you doing this in, and what is it a subclass of? It appears you are doing this in a view controller, but I just want to be sure. And again, in either case (UIControl or UIImageView), neither class responds to setImage.

Using addSubView or presentModalViewController in a standard method with paramaters?

I'm trying to reuse code, I have some tutorial view controllers / views, which I would like to call from an action sheet. However, the calling views are different. Sometimes the tutorial view(s) would need to be added as a subview and sometimes they would be added to the navigation controller.
How can I expand my standard function to cater for these two different situations ?
You can see what I'm having to do instead, which means duplicate code :(
I have a class called which holds the standard code, I want to add calls to views here directly.
-(void)showHelpClickButtonAtIndex:(int)buttonIndex:(UIView *)vw {
if (buttonIndex == CommonUIHelpPagesBtnIdx) {
// do nothing
} else if (buttonIndex == 0) {
NSLog(#"Tutorial here");
}
}
I use in one view like this ...
- (void)actionSheet:(UIActionSheet *)actionSheet
clickedButtonAtIndex:(NSInteger)buttonIndex {
CommonUI *cui = [CommonUI alloc];
[cui showHelpClickButtonAtIndex:buttonIndex:self.view];
[cui release];
if (buttonIndex == CommonUIHelpPagesBtnIdx) {
UIViewController *theController = [[HelpViewController alloc]
initWithNibName:#"HelpView"
bundle:nil onPage:HelpPageCalcBalance];
[self.navigationController.topViewController
presentModalViewController:theController animated:YES];
[theController release];
}
}
And is another view like this...
- (void)actionSheet:(UIActionSheet *)actionSheet
clickedButtonAtIndex:(NSInteger)buttonIndex {
[cui showHelpClickButtonAtIndex:buttonIndex:self.view];
if (buttonIndex == CommonUIHelpPagesBtnIdx) {
theController = [[HelpViewController alloc] initWithNibName:#"HelpView"
bundle:nil onPage:HelpPageGettingStarted];
[self.view addSubview:theController.view];
}
}
Maybe the actionSheets could share one same delegate that would be a root viewController or the appDelegate that would know what to do according to it's current state. Hence the same actionSheet method would be used in both case.
If you put your appDelegate which can always be reached as the actionSheetDelegate, you should be able to gain control on every way to present your view, either modally or not in any of your views + the window.
EDIT (re-Edited with your code): Maybe try this
MyAppDelegate.h:
#interface MyAppAppDelegate : NSObject <UIApplicationDelegate, UIActionSheetDelegate>
(...)
- (void) showHelp;
MyAppDelegate.m:
- (void) showHelp {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#""
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles: #"Tutorial", #"Help Pages", nil];
actionSheet.actionSheetStyle = UIActionSheetStyleBlackOpaque;
[actionSheet showInView:window];
[actionSheet release];
}
- (void)actionSheet:(UIActionSheet *)actionSheet
clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == CommonUIHelpPagesBtnIdx) {
UIViewController *theController = [HelpViewController alloc];
if ([[self lvc] superview] != nil) {
theController = [initWithNibName:#"HelpView"
bundle:nil
onPage:HelpPageGettingStarted];
[window addSubview:theController.view];
} else {
theController = [initWithNibName:#"HelpView"
bundle:nil
onPage:HelpPageCalcBalance];
UINavigationController * navController = [tabBarController selectedViewController];
[navController.topViewController presentModalViewController:theController animated:YES];
}
[theController release];
}
}
and in your viewControllers:
- (void)viewDidLoad {
[super viewDidLoad];
MyAppDelegate *delegate = (MyAppDelegate *) [[UIApplication sharedApplication] delegate];
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
[infoButton addTarget:delegate
action:#selector(showHelp)
forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *infoItem = [[UIBarButtonItem alloc] initWithCustomView:infoButton];
self.navigationItem.rightBarButtonItem = infoItem;
[infoItem release];
}
Didn't try it but it should work.