如何使用 activity 指标进行 Web 服务登录

How to use an activity indicator for web service login

我刚刚创建了一个使用 Web 服务的 iOS 应用程序,我的问题是当我在视图控制器中点击登录时处于空闲状态,不显示 activity 指示器,但是当响应和数据来自获取网络服务并加载下一个视图控制器; activity 指示符很快出现。我希望在点击登录按钮时 activity 指示器可见


代码示例:

NSString *username = TextUsername.text; 
NSString *password = Textpassword.text; 
NSString *strLogInUrl=[NSString stringWithFormat:@""]; 
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 
NSLog(@"Url %@",strLogInUrl); 
NSURL *URLGet = [NSURL URLWithString:strLogInUrl]; 
NSData *data = [NSData dataWithContentsOfURL:URLGet]; 
NSError* error; 
DicForAllData = [NSJSONSerialization JSONObjectWithData: data options:kNilOptions error:&error];

听起来您正在使用同步代码登录。如果您这样做:

display activity indicator
log in (synchronously)
hide activity indicator

然后 activity 指标永远没有机会显示。 (UI 直到您的代码 returns 并且应用程序访问事件循环后,更改才会呈现到屏幕上。)

您应该更改登录代码以异步工作(最佳选择)或重写代码以访问显示 activity 指示器和登录之间的事件循环。伪代码可能如下所示:

display activity indicator
[self performSelector: @selector(login) withObject: nil afterDelay: 0];

以及您的登录方法伪代码:

- (void) login;
{
  log in (synchronously)
  hide activity indicator
}

(performSelector:withObject:afterDelay: 不会立即执行来自选择器的代码。相反,它会在下次应用程序访问事件循环时排队调用您的选择器。您的代码在系统之后被调用有机会更新屏幕{具体来说,在它有机会显示您的 activity 指标之后}。)

一旦您点击登录按钮,在调用 Web 服务调用 activity 指示器之前以及在第二次调用 Web 服务之后,activity 指示器将是 shown/displayed。收到响应后,首先停止 activity 指示器,然后推送到下一个视图控制器。

记得在进入下一个屏幕之前关闭键盘。这是一个很好的做法。

正如其他人所指出的,要让 UI 更新,您需要异步执行此操作。在我向您介绍一些细节之前,让我们考虑一下您问题中代码的高级抽象:

[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSData *data = [NSData dataWithContentsOfURL:URLGet]; 
// do something with data
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];

你真的想发出一个异步请求,而不是那个同步请求:

[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSMutableURLRequest *request = ...;
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    // handle the data or connectionError here, inside this block, and remember to turn off activity indicator
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}];

问题是,您的代码片段没有正确创建请求。你过度简化了这个过程。你的过程真的还应该考虑:

  1. 如果您要提供凭据,那通常是 POST 请求,而不是 dataWithContentsOfURL 执行的简单 GET

  2. 你真的应该构建一个合适的 application/x-www-form-urlencoded 请求。

  3. 您必须对传递给服务器的数据进行百分比转义(否则,如果密码恰好包含某些保留字符,例如 +&,登录将失败)。

  4. 你真的应该处理错误。

将所有这些放在一起,看起来更像:

// tell the user we're going to do this request for them
// maybe show a `UIActivityIndicatorView`, too

[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];

// construct the body of the request; replace the keys `username` and `password` with whatever your web service requires

NSString *username = textUsername.text;
NSString *password = textPassword.text;
NSString *parameterString = [NSString stringWithFormat:@"username=%@&password=%@", [self percentEscapeString:username], [self percentEscapeString:password]];
NSData *httpBody = [parameterString dataUsingEncoding:NSUTF8StringEncoding];

// now create the request itself

NSString *strLogInUrl = @"http://example.com";
NSURL *URLGet = [NSURL URLWithString:strLogInUrl];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URLGet];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:httpBody];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];

// now send the request

[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    if (!data) {
        NSLog(@"connectionError: %@", connectionError);
    } else {
        NSError* error;
        dicForAllData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];

        // do what you want with the data/error here, e.g. `[self.tableView reloadData]` or whatever
    }

    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}];

假设您有一个百分比转义例程,例如:

- (NSString *)percentEscapeString:(NSString *)string
{
    NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                                 (CFStringRef)string,
                                                                                 (CFStringRef)@" ",
                                                                                 (CFStringRef)@":/?@!$&'()*+,;=",
                                                                                 kCFStringEncodingUTF8));
    return [result stringByReplacingOccurrencesOfString:@" " withString:@"+"];
}

早些时候,UnicoRahul 询问您是否使用 AFNetworking。值得考虑,因为它大大简化了您必须编写的代码:

NSDictionary *params = @{@"username": username, @"password": password};
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

[manager POST:@"http://example.com" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
    dicForAllData = responseObject;

    // do what you want with the data here, e.g. `[self.tableView reloadData]` or whatever
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"%@", error);
}];