iOS 成功执行多次以编程方式触发的 segue
iOS programmatically triggered segue is performed multiple times on success
我将我的应用程序连接到 rest API 以登录用户。登录过程完成后,我使用 NSNotificationCenter 通知控制器登录成功,并让应用程序执行 segue 以将用户带到主菜单。这是我的代码:
//perform log in, or show error message
-(void)logInAttemptComplete:(NSNotification *)notification
{
Boolean loginSuccessful = (Boolean)[[notification userInfo] objectForKey:@"loginSuccessful"];
[SVProgressHUD dismiss];
//if the suer was successfully logged in, take him to the main menu
if(loginSuccessful)
{
//go to the main menu
[self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{
//copy error code and display appropriate error
int errorCode = [[[notification userInfo] objectForKey:@"HTTP Message"] intValue];
//404 no server, 401 wrong password/no user
if(errorCode==401)
{
//create and show error alert view
UIAlertView *loginErrorAlertView = [[UIAlertView alloc] initWithTitle:@"Log In Failure" message:@"Wrong credentials. Check your Username and/or Password." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[loginErrorAlertView show];
}
else
{
//create and show error alert view
UIAlertView *loginErrorAlertView = [[UIAlertView alloc] initWithTitle:@"Log In Failure" message:@"Failed to contact server, please try again later." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[loginErrorAlertView show];
}
}
}
问题是,如果第一次尝试登录不成功,假设需要 X 次尝试才能成功登录,那么 segue 会执行 X 次。它当然最终出现在主菜单上,但最终结果是丑陋的。任何想法如何解决这一问题?也许我应该避免使用 segue 并以编程方式将 hte 用户直接带到另一个控制器?
编辑并回答
所以我通过添加行
犯了一个错误
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logInAttemptComplete:) name:@"logInNotification" object:nil];
在按下登录按钮的功能中。
我通过在登录尝试失败后删除观察者来修复它
if(loginSuccessful)
{
//go to the main menu
[self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"logInNotification" object:nil];
我会使用两种不同的通知,这段代码似乎比它应该的更复杂。
你没有提供你的登录码本身,所以我只能笼统地回答。创建两个通知(一个用于成功,一个用于登录失败)。登录控制器根据登录尝试的结果发送通知。
您将上述方法拆分为两个方法,这两个方法在发送任一通知时调用。
您的代码可能如下所示:
[manager GET:stringWithURLforRequest parameters:nil success:^(AFHTTPRequestOperation *task, id responseObject) {
// ...
[[NSNotificationCenter defaultCenter] postNotificationName:@"LogInSuccessful" object:nil userInfo:nil];
} failure:^(AFHTTPRequestOperation *task, NSError *error) {
// ...
[[NSNotificationCenter defaultCenter] postNotificationName:@"LogInFailure" object:nil userInfo:@{ @"HTTP Message":statusCodeNumber}];
}];
...你只需要添加另一个通知来监听你的其他视图控制器。如果你这样做,你可以更安全地区分登录失败和成功(更好的语义)。
如果 @"loginSuccessful" 在您的 userInfo
中是 NSNumber
使用:
BOOL loginSuccessful = [notification.userInfor[@"loginSuccessful"] booleanValue];
在原始代码中,如果对象不是 nil
(如果我理解正确的话,它一直是),您将始终获得值为 YES 的 loginSuccessful,因为您检查了一个对象的存在不是它的价值。
您在这一行中使用了 NSNumber:
@{@"loginSuccessful":@YES}
@YES
只是 [NSNumber numberWithBool:YES]
的缩写
根据 this header 布尔值只是 unsigned char 所以你正在签署指向这个 char 的指针,它永远是真的(不是 NULL)。
据我了解,您发送的是成功通知,如果重试,它会被调用太多次。
一种简单的解决方法是停止在成功方法中收听该通知。
所以你会有类似的东西(伪代码)
- (void)loginAttempt:(NSNotifcation*)notif{
if (success){
//Stop listening to that notification, so the method never gets called again
performSegueWithIndetifier
}else{
//retry
}
}
另一种肮脏的单行方法是保留尝试次数的 ivar,并且仅在该次数当前等于零时才调用执行转场。
我认为我的解决方案可以解决您的问题,但如果您有时间,我建议您以一种仅在需要调用时才调用 segue 或其他 X 的回调成功的方式来审查您的设计被忽略。一种简单的想象方法是在上一次登录给出结果之前不重试。因此,在您真正知道自己是否失败之前,您不会重试(无论是超时还是成功或其他任何事情)
找到了:
问题是控制器保持 "listening" 即使由于
导致消息发送失败
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logInAttemptComplete:) name:@"logInNotification" object:nil];
在按下登录按钮的功能中。
我所要做的就是添加
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"logInNotification" object:nil];
之后的行
if(loginSuccessful)
{
//go to the main menu
[self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{
否则检查。
我将我的应用程序连接到 rest API 以登录用户。登录过程完成后,我使用 NSNotificationCenter 通知控制器登录成功,并让应用程序执行 segue 以将用户带到主菜单。这是我的代码:
//perform log in, or show error message
-(void)logInAttemptComplete:(NSNotification *)notification
{
Boolean loginSuccessful = (Boolean)[[notification userInfo] objectForKey:@"loginSuccessful"];
[SVProgressHUD dismiss];
//if the suer was successfully logged in, take him to the main menu
if(loginSuccessful)
{
//go to the main menu
[self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{
//copy error code and display appropriate error
int errorCode = [[[notification userInfo] objectForKey:@"HTTP Message"] intValue];
//404 no server, 401 wrong password/no user
if(errorCode==401)
{
//create and show error alert view
UIAlertView *loginErrorAlertView = [[UIAlertView alloc] initWithTitle:@"Log In Failure" message:@"Wrong credentials. Check your Username and/or Password." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[loginErrorAlertView show];
}
else
{
//create and show error alert view
UIAlertView *loginErrorAlertView = [[UIAlertView alloc] initWithTitle:@"Log In Failure" message:@"Failed to contact server, please try again later." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[loginErrorAlertView show];
}
}
}
问题是,如果第一次尝试登录不成功,假设需要 X 次尝试才能成功登录,那么 segue 会执行 X 次。它当然最终出现在主菜单上,但最终结果是丑陋的。任何想法如何解决这一问题?也许我应该避免使用 segue 并以编程方式将 hte 用户直接带到另一个控制器?
编辑并回答
所以我通过添加行
犯了一个错误[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logInAttemptComplete:) name:@"logInNotification" object:nil];
在按下登录按钮的功能中。
我通过在登录尝试失败后删除观察者来修复它
if(loginSuccessful)
{
//go to the main menu
[self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"logInNotification" object:nil];
我会使用两种不同的通知,这段代码似乎比它应该的更复杂。
你没有提供你的登录码本身,所以我只能笼统地回答。创建两个通知(一个用于成功,一个用于登录失败)。登录控制器根据登录尝试的结果发送通知。
您将上述方法拆分为两个方法,这两个方法在发送任一通知时调用。
您的代码可能如下所示:
[manager GET:stringWithURLforRequest parameters:nil success:^(AFHTTPRequestOperation *task, id responseObject) {
// ...
[[NSNotificationCenter defaultCenter] postNotificationName:@"LogInSuccessful" object:nil userInfo:nil];
} failure:^(AFHTTPRequestOperation *task, NSError *error) {
// ...
[[NSNotificationCenter defaultCenter] postNotificationName:@"LogInFailure" object:nil userInfo:@{ @"HTTP Message":statusCodeNumber}];
}];
...你只需要添加另一个通知来监听你的其他视图控制器。如果你这样做,你可以更安全地区分登录失败和成功(更好的语义)。
如果 @"loginSuccessful" 在您的 userInfo
中是 NSNumber
使用:
BOOL loginSuccessful = [notification.userInfor[@"loginSuccessful"] booleanValue];
在原始代码中,如果对象不是 nil
(如果我理解正确的话,它一直是),您将始终获得值为 YES 的 loginSuccessful,因为您检查了一个对象的存在不是它的价值。
您在这一行中使用了 NSNumber:
@{@"loginSuccessful":@YES}
@YES
只是 [NSNumber numberWithBool:YES]
根据 this header 布尔值只是 unsigned char 所以你正在签署指向这个 char 的指针,它永远是真的(不是 NULL)。
据我了解,您发送的是成功通知,如果重试,它会被调用太多次。
一种简单的解决方法是停止在成功方法中收听该通知。
所以你会有类似的东西(伪代码)
- (void)loginAttempt:(NSNotifcation*)notif{
if (success){
//Stop listening to that notification, so the method never gets called again
performSegueWithIndetifier
}else{
//retry
}
}
另一种肮脏的单行方法是保留尝试次数的 ivar,并且仅在该次数当前等于零时才调用执行转场。
我认为我的解决方案可以解决您的问题,但如果您有时间,我建议您以一种仅在需要调用时才调用 segue 或其他 X 的回调成功的方式来审查您的设计被忽略。一种简单的想象方法是在上一次登录给出结果之前不重试。因此,在您真正知道自己是否失败之前,您不会重试(无论是超时还是成功或其他任何事情)
找到了:
问题是控制器保持 "listening" 即使由于
导致消息发送失败[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logInAttemptComplete:) name:@"logInNotification" object:nil];
在按下登录按钮的功能中。
我所要做的就是添加
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"logInNotification" object:nil];
之后的行
if(loginSuccessful)
{
//go to the main menu
[self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{
否则检查。