Avoid showing UIAlertView when using PFLogInViewController - objective-c

I'm using a subclass of the PFLogInViewController in which I want to display errors in a different way to that of the default behaviour which is to pop up a UIAlertView.
Does anyone know if there's a way to avoid showing the UIAlertView? I'm already using the following method, however that doesn't actually allow me to avoid the UIAlertView being shown in the event of a failed login.
- (BOOL)logInViewController:(PFLogInViewController *)logInController shouldBeginLogInWithUsername:(NSString *)username password:(NSString *)password

PFLogInViewController does not provide hooks to change this behavior. You might want to build your own custom PFLogInViewController subclass and override the method which display alert view when login failed.
Since PFLogInViewController's code has been open sourced, according to it the method which displays an alert view is _loginDidFailWithError.
https://github.com/ParsePlatform/ParseUI-iOS/blob/master/ParseUI/Classes/LogInViewController/PFLogInViewController.m#L382-L390
- (void)_loginDidFailWithError:(NSError *)error {
if (_delegateExistingMethods.didFailToLogIn) {
[_delegate logInViewController:self didFailToLogInWithError:error];
}
[[NSNotificationCenter defaultCenter] postNotificationName:PFLogInFailureNotification object:self];
NSString *title = NSLocalizedString(#"Login Error", #"Login error alert title in PFLogInViewController");
[PFUIAlertView showAlertViewWithTitle:title error:error];
}
For example, if you like the following, you can not to display alerts when the login fails.
Define MYLogInViewController as subclass of PFLogInViewController
#interface MYLogInViewController : PFLogInViewController
#end
#implementation MYLogInViewController
- (void)_loginDidFailWithError:(NSError *)error {
if ([self.delegate respondsToSelector:#selector(logInViewController:didFailToLogInWithError:)]) {
[self.delegate logInViewController:self didFailToLogInWithError:error];
}
[[NSNotificationCenter defaultCenter] postNotificationName:PFLogInFailureNotification object:self];
}
#end
and use it instead PFLogInViewController
MYLogInViewController *logInViewController = [[MYLogInViewController alloc] init];
logInViewController.delegate = self;
[self presentViewController:logInViewController animated:YES completion:nil];

Related

Cordova - Implementing the Privacy Screen functionality with the ASWebAuthenticationSession usage

I'm adding the privacy screen functionality to the hybrid Cordova app via a plugin and following the approach adviced by apple.
Though it leads to unexpected issues when I open ASWebAuthenticationSession window I use for the OAuth authentication. What happens is, when system dialogue appears with a text "Your app wants to use xxx for Sign In", it makes the app to lose a focus and the privacy screen appears behind the overlay. After I choose "Yes", the app gains focus back and the code removing the privacy screen fires, the same code also closes the freshly opened ASWebAuthenticationSession window.
The code in PrivacyScreenPlugin.m:
UIViewController *blankViewController;
#interface PrivacyScreenPlugin ()
#end
#implementation PrivacyScreenPlugin
- (void)pluginInitialize
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onAppDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onPageDidLoad) name:CDVPageDidLoadNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onAppWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
}
-(UIViewController *)createViewWithGradient {
UIViewController *viewController;
viewController = [UIViewController new];
viewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
return viewController;
}
-(void) applyPrivacyScreen
{
if (blankViewController == NULL) {
blankViewController = [self createViewWithGradient];
}
blankViewController.view.window.hidden = NO;
[self.viewController.view.window.rootViewController presentViewController:blankViewController animated:NO completion:NULL];
}
#pragma mark - Explicit Commands
- (void) hidePrivacyScreen:(CDVInvokedUrlCommand*)command
{
[self removePrivacyScreen];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void) showPrivacyScreen:(CDVInvokedUrlCommand*)command
{
[self applyPrivacyScreen];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
#pragma mark - Triggered functions
- (void) onPageDidLoad
{
[self removePrivacyScreen];
}
- (void)onAppDidBecomeActive:(UIApplication *)application
{
[self removePrivacyScreen];
}
- (void)onAppWillResignActive:(UIApplication *)application
{
[self applyPrivacyScreen];
}
#pragma mark - Helper functions
-(void) removePrivacyScreen
{
[self.viewController dismissViewControllerAnimated:NO completion:nil];
}
#end
So far I got that issue is related to the way view is dismissed, namely dismissViewControllerAnimated which dismisses the stack of modal windows:
[self.viewController dismissViewControllerAnimated:NO completion:nil];
Can it be helped or worked around? Maybe instead of removing a security screen, it can be hidden? Or is there a different way to draw the overlay which is free of the issue?
P.S. I tried to listen to UIApplicationDidEnterBackgroundNotification event, but it's not what I want. I'd like the app screen to be covered as soon as it's sent to the list of apps (via a double tap on the home button or long swipe).
To accomplish it, it's needed to render the privacy screen in a subview, instead of a modal view. It enables to hide/show a privacy screen view instead of adding/removing it:
-(void) removePrivacyScreen
{
blankViewController.view.hidden=YES;
}
-(void) applyPrivacyScreen
{
if (blankViewController == NULL) {
blankViewController = [self createViewWithGradient];
}
blankViewController.view.hidden = NO;
[self.viewController.view.window addSubview:blankViewController.view];
}

Trying to change button text in a view from an appDelegate method

I'm a little new to iOS development and am running into a little problem.
I've implemented the FB SDK for iOS login into my app, and I can login and out no problem.
However, what I'm trying to do is change the single button text from 'Log in' to 'Log out', depending on the state of the FB session.
In my appDelegate (which handles the FB session state changes), I'm calling two methods from my main view controller like this:
helloappViewController * vc = [[helloappViewController alloc]init];
[vc showLogInButton];
...and...
helloappViewController * vc = [[helloappViewController alloc]init];
[vc showLogOutButton];
The methods in helloappViewController that are being called are these:
- (void) showLogInButton {
NSLog(#"Changing button text to 'Login'.");
[self.buttonLogInLogOut setTitle:#"Login" forState:UIControlStateNormal];
}
- (void) showLogOutButton {
NSLog(#"Changing button text to 'Logout'.");
[self.buttonLogInLogOut setTitle:#"Logout" forState:UIControlStateNormal];
}
I know these methods is being called properly because I can see the console log output fine, and I know I'm logged in and out via FB because of other console log outputs I've set.
However, the button title text is not being changed.
Anyone have any idea where I might be going wrong?
Thx.
Try to move code
[self showLogInButton];
and
[self showLogOutButton];
to viewDidLoad method of the helloappViewController view controller
something like this:
helloappViewController * vc = [[helloappViewController alloc]init];
vc.fbIsConnected = YES;
in viewDidLoad:
if (self.fbIsConnect)
{
[self showLogOutButton];
}
else
{
[self showLogInButton];
}
In vc:
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(fbDidChange)
name: FBDidChangeNotification
object: nil];
- (void) fbDidChange
{
// change button title
}
in fb controller:
[[NSNotificationCenter defaultCenter] postNotificationName:FBDidChangeNotification object:self];

Is completion block syntax for MFMailComposeViewController mailComposeDelegate possible?

I would like to use the MFMailComposeViewController mailComposeDelegateproperty with completion block syntax, but not sure if that is possible. Something similar to how the TWTweetComposeViewController completionHandler property works:
TWTweetComposeViewController __weak *twee = tweeter;
tweeter.completionHandler = ^(TWTweetComposeViewControllerResult result) {
// code here
};
The code I have creates the MFMailComposeViewController, but then has to save a reference my viewController parameter, since this "share via email" functionality is not in a UIViewcontroller, but a custom MYSharing class.
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
// more code here
_viewController = viewController;
picker.mailComposeDelegate = self;
[viewController presentModalViewController:picker animated:YES];
The mailComposeController:didFinishWithResult:error: from MFMailComposeViewControllerDelegate uses that _viewController to dismiss the modal.
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
[_viewController dismissModalViewControllerAnimated:YES];
}
Not understanding blocks very well, is there some syntax that look like this?
picker.mailComposeDelegate = ^(???)(??? ??? ???) {
[viewController dismissModalViewControllerAnimated:YES];
}
I did find MFMailComposeViewController(BlocksKit) (which has source on github), but even with that, the syntax conversion trips me up.
No completion handler for MFMailComposeViewController now.
if you want to reference the view controller who presented the MFMailComposeViewController, code like below, use presentingViewController property.
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
[controller.presentingViewController dismissModalViewControllerAnimated:YES];
}
Why don't you just call [self dismissModalViewControllerAnimated:YES]; from the MFMailComposeViewControllerDelegate method?

Showing Viewcontrollers modally in a delege functions

I have some code that shows two UIViewController in a delegate.
RootViewController.m
request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"***some https url here ***"]];
// custom implementation of NSURLConnectionDelegate
dataman = [[DataManager alloc] initWithParentcontroller:self];
mainConn = [[NSURLConnection alloc] initWithRequest:request delegate:dataman];
In AuthenticationViewController.h
#protocol ShowAuthenticationWindowDelegate <NSObject>
#required
- (void) onFinishedEnteringCredentials:(NSURLCredential*)credentials;
- (void) onCancelAuthentication;
#end
in AuthenticationViewController.m
- (IBAction) onClickLogin:(id)sender;
{
....
// authDelegate => id <ShowAuthenticationWindowDelegate>
[authDelegate onFinishedEnteringCredentials:credentials];
[self dismissModalViewControllerAnimated:YES];
....
}
in DataManger.h (DataManager class) implements the NSURLConnectionDelegate and ShowAuthenticationWindowDelegate.
In Datamanager.m
In the didReceiveAuthenticationChallenge delegate function I show the AuthentiationViewController as a modal dialog to gather username/password.
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
AuthenticationViewController *authview = [[AuthenticationViewController alloc] initWithNibName:#"AuthenticationViewController" bundle:[NSBundle mainBundle]];
authview.modalPresentationStyle = UIModalPresentationFullScreen;
authview.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
authview.credentialsDelegate = self;
[rootController presentModalViewController:authview animated:YES];
}
Here I show a UIViewController which is an activity indicator in a view. I am showing it modally after I dismiss the previous AuthenticationViewController dialog in one of the login button event handler by called dismissModalViewController. After sending the credentials with challenge object (previously cached) I am showing the ActivityViewController modally, but it is not shown no matter what I do. I tried to show an UIAlertView which works, but my activityviewcontroller is not shown. I checked the parameters and objects everything is valid. even the delegate wire ups!!! All the code is getting called but the dialog is not shown.
May be I am missing something ???
- (void) onFinishedEnteringCredentials:(NSURLCredential*)credentials;
{
[[authChallenge sender] useCredential:credentials forAuthenticationChallenge:authChallenge];
// create an activity modal dialog
if (activityController == nil) {
activityController = [[ActivityViewController alloc] initWithNibName:#"ActivityViewController" bundle:[NSBundle mainBundle]];
activityController.modalPresentationStyle = UIModalPresentationFullScreen;
activityController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
}
[rootController presentModalViewController:activityController animated:YES];
}
I have figure out the solution, if anyone want to show two modal dialogs back to back, you should set the "animated" parameter to "NO" on the controller that is being dismissed. It seems like the animation transition is not being completed by the time the next controller is shown with "presentViewController" function.

iphone SDK: Not sure why I am not receiving UITextField events?

I have defined the controller to receive the events.
#interface salesViewController : UIViewController
<UITextFieldDelegate>{
However, none of my events are not firing.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
//this is not getting called
}
In Interface Builder I assigned the TextField delegate to the salesView.
What am I missing?
You have to set the delegate properly. You observe the protocol, but you need to do this:
#interface YourController : UIViewController<UITextFieldDelegate> {
IBOutlet UITextField* field;
}
#end
#implementation YourController
-(void)viewDidLoad
{
[field setDelegate:self];
}
And you will receive the events. Alternatively, you can set the delegate in Interface Builder as well, along with doing it programmatically in loadView, allocating the field and setting the delegate.
Additionally, try to use NSNotificationCenter as little as possible. Notifications are somewhat obsolete unless there isn't really a direct path between you and the object in question. Just a small comment on the answer above.
what are you trying to accomplish? textFieldDidBeginEditing is messaged whenever the user selects the text field. If you are trying to update a label or something as the user makes edits, you need to setup an observer w/ NSNotificationCenter and watch for the notification that is fired whenever this happens.If you take this approach, make sure to remove the observer once you are done with it
for example:
#pragma mark
#pragma mark -
#pragma mark Notification Observers
- (void)addObservers {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(textFieldDidChange:) name:#"UITextFieldTextDidChangeNotification" object:nil];
}
- (void)removeObservers {
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"UITextFieldTextDidChangeNotification" object:nil];
}
if you need to keep tabs on multiple text fields, do something like this for your selector:
- (void)textFieldDidChange:(NSNotification*)aNotification {
UITextField *textField = (UITextField *)[aNotification object];
if([textField isEqual:usernameTextField])
{
[user setUsername:usernameTextField.text];
}
else if([textField isEqual:phoneNumberTextField])
{
[user setPhoneNumber:phoneNumberTextField.text];
}
}