I'm trying to get the following working, this example appears on a few websites but i just can't seem to get it working.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Yes, like this" message:#"What are you looking at?" cancelButtonTitle:#"Leave me alone" otherButtonTitles:#"Button 1",#"Button 2",nil];
[alert showWithDismissHandler:^(NSInteger selectedIndex, BOOL didCancel) {
if (didCancel) {
NSLog(#"User cancelled");
return;
}
switch (selectedIndex) {
case 1:
NSLog(#"1 selected");
break;
case 2:
NSLog(#"2 selected");
break;
default:
break;
}
}];
The warnings I'm getting are
No visible #interface for 'UIAlertView' declares the selector 'initWithTitle:message:cancelButtonTitle:otherButtonTitles:'
No visible #interface for 'UIAlertView' declares the selector 'showWithDismissHandler:'
Properly a really daft question but what am i missing.
Thanks
The signature is not correct. You missed delegate.
– initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:
The standard UIAlertView does not have showWithDismissHandler method. If you copied some code from internet, likely you will need to download some third party package that supports UIAlertView with block callback (there are quite a few of them).
Yes, Peter is right! The first warning is because the signature is not correct:
– initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:
and what you trying to do here is to do something when the alertview is dismissed and the method you are using is not available. Implement UIAlertViewDelegate and then use any of these methods to do what you are trying to do here.
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
Here's the link to UIAlertView delegate reference:
http://developer.apple.com/library/ios/documentation/uikit/reference/UIAlertViewDelegate_Protocol/UIAlertViewDelegate/UIAlertViewDelegate.html
Are you using something like UBAlertView, by any chance? If so, you need to instantiate a UBAlertView, not a UIAlertView:
UBAlertView *alert = [[UBAlertView alloc] initWithTitle:#"Yes, like this"
message:#"What are you looking at?"
cancelButtonTitle:#"Leave me alone"
otherButtonTitles:#"Button 1",#"Button 2",nil];
[alert showWithDismissHandler:^(NSInteger selectedIndex, BOOL didCancel) {
... etc ...
(I like to put line breaks in long method calls to make them more readable.)
Related
I try to use the new iOS 9.0 CNContactPickerViewController to select a contact in objective-C. I set the delegate and implement the CNContactPickerDelegate methods.
#import ContactsUI;
#import Contacts;
//-----------------------------------------------------------------------
- (void) presentContacts
{
CNContactPickerViewController *contactPicker = [[CNContactPickerViewController alloc] init];
contactPicker.delegate = self;
contactPicker.predicateForEnablingContact = [NSPredicate predicateWithFormat:#"familyName LIKE[cd] 'smith'"];
contactPicker.predicateForSelectionOfContact = [NSPredicate predicateWithFormat:#"TRUEPREDICATE"];
[_myViewController presentViewController:contactPicker animated:YES completion:nil];
}
//-----------------------------------------------------------------------
- (void) contactPickerDidCancel: (CNContactPickerViewController *) picker
{
NSLog(#"didCancel");
}
//-----------------------------------------------------------------------
- (void) contactPicker: (CNContactPickerViewController *) picker
didSelectContact: (CNContact *) contact
{
NSLog(#"didSelectContact"):
}
//-----------------------------------------------------------------------
- (void) contactPicker: (CNContactPickerViewController *) picker
didSelectContactProperty: (CNContactProperty *) contactProperty
{
NSLog(#"didSelectProperty");
}
//-----------------------------------------------------------------------
The contacts picker is presented with 'smith' selectable but I get the following message:
[CNUI ERROR] Selection predicates are set but the delegate does not implement contactPicker:didSelectContact: and contactPicker:didSelectContactProperty:. Those predicates will be ignored.
And I never get any log from the delegate methods. It behaves exactly as the line
contactPicker.delegate = self;
is ignored. Even I click on the "cancel" button in the picker, I don't get my "didCancel" message but I get another message:
plugin com.apple.MobileAddressBook.ContactsViewService invalidated
I found in https://forums.developer.apple.com/thread/12275 somebody with the similar problem in swift and he solved it telling us: "So I found that the ContactsPicker I was calling was in the wrong module" but I do not understand how we can get the wrong module and how to call the "right" module.
I have the same problem on the simulator and on a real device (iPad).
Thanks to Joel in my related question With CNContactPickerViewController in iOS 9.0, how to enable/disable single or multiple selection?, I found that I just forgot to store the CNContactPickerViewController in a property that exists the time the user make the selection.
So my code becomes:
- (void) presentContacts
{
_contactPicker = [[CNContactPickerViewController alloc] init];
contactPicker.delegate = self;
...
}
I have a UIAlertView that initiates an update process.
The UIAlertView asks the user whether they'd like to update or not.
Here's my code:
- (void)reachabilityChanged:(NSNotification *)notification {
if ([connection isReachable]){
[updateLabel setText:#"Connection Active. Checking Update Status"];
[[[UIAlertView alloc] initWithTitle:#"Update Available" message:#"Your File Database is Out of Date. Would you like to Update?\nNote: Updates can take a long time depending on the required files." delegate:self cancelButtonTitle:#"Later" otherButtonTitles:#"Update Now", nil] show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 1) {
[self updateFiles:[UpdateManager getUpdateFiles]];
}
}
The above code runs fine, however, within my updateFiles: method, I require some UI adjustments.
- (void)updateFiles:(NSArray *)filesList {
for (NSDictionary *file in filesList) {
[updateLabel setText:[NSString stringWithFormat:#"Downloading File: %#", [file objectForKey:#"Name"]]];
[UpdateManager updateFile:[file objectForKey:#"File Path"]];
}
[updateIndicator stopAnimating];
[updateLabel setText:#"Update Completed"];
}
The UIAlertView doesn't dismiss until after the for statement in the updateFiles method is run.
I can't get the updateLabel to display the files it's currently downloading, though at the end of the update process, we do get a 'Update Completed' in the label.
Can anybody help?
UPDATE
I'm starting to suspect this is more of a process that's being delayed by some heavy synchronous processes. For example, my [UpdateManager getUpdateFiles] method is heavy and involves getting resources from the web. Likewise with my [UpdateManager updateFile:[file objectForKey:#"File Path"]]; method.
Is there any way I can force the UI updates to take priority over these methods?
I'm just trying to give the user some feedback on what's happening.
I found the solution.
I couldn't update the UI and process some heavy methods on the same thread.
Since I can only update the UI on the main thread, I had to do some re-organising to ensure the processes were on a background thread, but then promote the UI changes to the main.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 1) {
[self performSelectorInBackground:#selector(updateFiles:) withObject:[UpdateManager getUpdateFiles]];
}
}
- (void)updateFiles:(NSArray *)filesList {
for (NSDictionary *file in filesList) {
[updateLabel performSelectorOnMainThread:#selector(setText:) withObject:[NSString stringWithFormat:#"Downloading File: %#", [file objectForKey:#"Name"]]];
[UpdateManager updateFile:[file objectForKey:#"File Path"]];
}
[updateIndicator stopAnimating];
[updateLabel setText:#"Update Completed"];
}
So, I send updateFiles: to the background and promote setText: and any other UI changes to the Main Thread.
Here is my code that calls "displayAlert". The problem is not only do I get an error message (wait_fences: failed to receive reply: 10004003) but the "alert" is displayed twice!
if(gSiteID.globalSiteID.length == 0) { // user didn't choose site
[self displayAlert:NSLocalizedString(#"Missing Site ID", nil) andData:NSLocalizedString(#"You must choose a site from the View Sites page",nil)];
return;
}
This the code for "displayAlert":
- (void) displayAlert: (NSString *) title andData: (NSString *) errorMsg {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle: title
message: errorMsg
delegate:nil
cancelButtonTitle: #"OK"
otherButtonTitles: nil];
[alert show];
return;
}
I have searched SO and Google and found nothing that is specific to my issue. What am I doing wrong?
Are you testing this on a real device or the simulator? wait_fences: failed to receive reply usually means something bad happened with the debugger's connection to your device, or that you sat at a breakpoint for a really long time and it timed out. Are you sure that the code only executes once, and that nothing else could call that method? Stick breakpoints in your if statement and in your displayAlert:andData: method and see what happens. Run through your logic and find all the cases when that display alert method can be called and stick breakpoints on all of them.
I found the problem: indeed I was calling it twice from different .cs files (do you see the egg on my face?). Jack Lawrence please post your answer to the question, since you hit it on the head.
How do I display a popup informing a NSTextField can't be empty in cocoa ?
If the user click apply and the NSTextField is empty a popup should appear saying the field can't be empty.
thanks
The answer by #beryllium only tells part of the story.
In fact, to properly validate text field input in Cocoa you should be using an NSFormatter attached to your text field cell and then in your NSTextFieldDelegate you should implement the method: control:didFailToFormatString:errorDescription:. In this delegate method you can prompt the user to correct their input.
All you need to do in your NSFormatter subclass is something like this:
#implementation RKTextFormatter
- (NSString*)stringForObjectValue:(id)object
{
return (NSString*)object;
}
- (BOOL)getObjectValue:(id*)object forString:(NSString*)string errorDescription:(NSString**)error {
BOOL stringPassesTest = NO;
//put your test here
if(!stringPassesTest)
{
//set the error and return NO
if(error)
*error = [NSError errorWithDomain:#"YourErrorDomain" code:1 userInfo:[NSDictionary dictionaryWithObject:#"It's a bingo" forKey:NSLocalizedDescriptionKey]];
return NO;
}
//otherwise, just assign the string
*object = string;
return YES;
}
#end
You would assign the formatter to your text field like so:
RKTextFormatter* formatter = [[RKTextFormatter alloc] init];
[[textField cell] setFormatter:formatter];
And then in your NSTextFieldDelegate you handle any invalid input:
- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)string errorDescription:(NSString *)error
{
//display an alert, obviously it would be more useful than this
NSAlert* alert = [NSAlert alertWithMessageText:#"You have failed me for the last time" defaultButton:#"Revise Input" alternateButton:nil otherButton:nil informativeTextWithFormat:#"%#",error];
[alert beginSheetModalForWindow:control.window modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
//if you return NO here, the user's input will not be accepted
//and the field will remain in focus
return NO;
}
If the user click apply and the NSTextField is empty a popup should
appear saying the field can't be empty.
Please, please, do not do this. You can be smarter than that.
Instead of investing your time in writing an alert dialog to handle the "unexpected" situation, invest it in creating a method that prevents the problem from occurring in the first place: keep the Apply button disabled until a proper value has been entered in the text field.
In addition, as #Rob Keniger mentioned, you should consider using NSFormatters to validate the input to make sure it's of the appropriate kind.
Try to use this code:
- (IBAction)pushBtn:(id)sender {
if(self.textfield.stringValue.length == 0){
NSAlert * alert = [NSAlert alertWithMessageText:#"Error"
defaultButton:#"OK"
alternateButton:nil
otherButton:nil
informativeTextWithFormat:#"Enter a text into text field"];
[alert beginSheetModalForWindow:self.window
modalDelegate:self
didEndSelector:#selector(alertDidHidden:)
contextInfo:nil];
//[alert runModal]; - more simple way
}
}
-(void)alertDidHidden:(NSAlert *)alert{
}
I'm having trouble finding examples of the correct way to use NSError, UIAlertView, and NSErrorRecoveryAttempting together on iOS. Most of the documentation and examples I can find cover the equivalent functionality on OS X, where the relevant behaviors are integrated by Cocoa. But in iOS it seems to be necessary do do this "by hand", and I can't find good examples of how it's done.
I'd very much appreciate a few examples of best practice in using information in NSError to support recovery attempts from NSErrors reported to the user.
According to Apple's documentation:
Important: The NSError class is available on both Mac OS X and iOS. However, the error-responder and error-recovery APIs and mechanisms are available only in the Application Kit (Mac OS X).
So, I'm not sure if you can use NSErrorRecoveryAttempting even though it does appear to be defined in the documentation (it looks like this is an area of the UIKit docs that have not yet been updated after being copied from AppKit's documentation).
Here is how I handle errors in my code:
NSError *error = nil;
id result = [SomeClass doSomething:&error];
if (!result) {
NSLog(#"Do something failed: %#", error);
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Something failed!" message:#"There was an error doing something." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease];
[alert show];
return;
}
I found a great example of this.
See the following blog post and GitHub code (including sample project) by James Beith
http://www.realmacsoftware.com/blog/cocoa-error-handling-and-recovery
https://github.com/realmacsoftware/RMErrorRecoveryAttempter
I was able to successfully use this on the iPhone simulator.
I'm trying to mirror AppKit's error handling mechanism in UIKit, mainly because I want to take advantage of the responder chain to forward errors upwards. I haven't tested this fully, but at the moment it's looking like below.
It reflects AppKit pretty closely, but the will/did hooks can be overridden to perform custom error presentation and recovery respectively. The default behaviour is to show a UIAlertView for presentation and use a psuedo-NSErrorRecoveryAttempting object for recovery.
#implementation UIResponder (ErrorHandling)
- (void)presentError:(NSError *)error
completion:(void (^)(BOOL recovered))completion
{
if (nil == (error = [self willPresentError:error])) {
return;
}
if (self.nextResponder) {
[self.nextResponder presentError:error completion:completion];
return;
}
// Code to create and show UIAlertView
// e.g. https://github.com/jayway/CWUIKit/blob/master/Classes/UIAlertView%2BCWErrorHandler.m
// The UIAlertViewDelegate calls didPresentError...
}
/*
Override to customise the error object as in AppKit.
You can also perform your own error presentation, and return nil to terminate the default handling.
Custom error presentation UI should still call didPresentError... when dismissed
*/
- (NSError *)willPresentError:(NSError *)error
{
return error;
}
/*
Override to perform custom error recovery.
*/
- (void)didPresentError:(NSError *)error optionIndex:(NSInteger)optionIndex completion:(void (^)(BOOL recovered))completion
{
id recoveryAttempter = [error recoveryAttempter];
if ([recoveryAttempter respondsToSelector:#selector(attemptRecoveryFromError:optionIndex:completion:)]) {
[recoveryAttempter attemptRecoveryFromError:error optionIndex:optionIndex completion:completion];
}
}
#end