是否可以在执行同步调用或其他操作时更新 UI 中的某些内容? [英] Is it possible to update something in the UI while performing synchronous calls or other operations?

查看:80
本文介绍了是否可以在执行同步调用或其他操作时更新 UI 中的某些内容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的项目中,我需要执行一些同步 Web 服务调用或从 CoreData 读取.执行这些操作可能需要几秒钟,所以我创建了一个 LoadingView 类,它是 UIView 的子类,它有一个简单的消息和一个 UIActivityIndi​​catorView代码>.对于普通的UIButton,我只是在Touch Down 事件上[myLoadingView setHidden:NO][myLoadingView setHidden:YES]Touch Up Inside 事件上.这一切都如我所愿.

At several points in my project, I need to perform some synchronous web service calls or read from CoreData. It may take a couple seconds to preform these operations, so I created a LoadingView class that is a subclass of UIView that has a simple message and a UIActivityIndicatorView. For normal UIButtons, I just [myLoadingView setHidden:NO] on the Touch Down event, and [myLoadingView setHidden:YES] on the Touch Up Inside event. This all works exactly how I want.

问题是我无法找到如何使用键盘上的 Return 键执行此操作.据我所知,当用户触摸 Return 键(UITextFieldDelegate protocol textFieldShouldReturn:),我需要两种方法才能使我的 [myLoadingView setHidden:NO] -> [myLoadingView setHidden:YES] 技术起作用,因为 Objective-C 直到方法的最后才会更新屏幕,而不是像其他语言一样不断更新屏幕.

The problem is I cannot find out how to do this with the Return key on the keyboard. From what I can tell, there is only 1 method that gets called when the user touches the Return key (the UITextFieldDelegate protocol textFieldShouldReturn:), and I need two methods to be able to get my [myLoadingView setHidden:NO] -> [myLoadingView setHidden:YES] technique to work, since Objective-C doesn't update the screen until the very end of the method, as opposed to continuously updating the screen like other languages.

如何让我的加载屏幕在用户触摸 Return 键后立即显示,执行一些操作,并在操作完成后隐藏加载屏幕?

How can I make my Loading screen show up as soon as the user touches the Return key, perform some operations, and they hide the Loading screen once the operations are done?

编辑:

我尝试过使用 NSNotificationCenter,但我似乎遇到了同样的问题.例如,如果我有一个 UIButton Touch Up Inside 方法:

I've tried looking at using NSNotificationCenter, but I seem to be running into the same problem. For example, if I have a UIButton Touch Up Inside method:

- (void) btnClick:(id) sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"Show Loading" object:nil];

    // Do stuff that takes a long time here

    [[NSNotificationCenter defaultCenter] postNotificationName:@"Hide Loading" object:nil];
}

在我的 LoadingView.m 中,我有:

- (id) init
{
    self = [super init];
    if (self)
    {
        // Do normal init stuff here

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showLoading) name:@"Show Loading" object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hideLoading) name:@"Hide Loading" object:nil];
    }
    return self;
}

- (void) showLoading
{
    // Do stuff to set up the loading screen 

    [self setHidden:NO];
}

- (void) hideLoading
{
    [self setHidden:YES];
}

这样设置后,当我单击按钮时,我看不到加载屏幕.当我发布通知时,它是否会立即执行并更改屏幕?如果是这样,我一定是做错了什么,可能是在我的 //Do stuff 部分.如果没有,我认为 NSNotificationCenter 不会帮助我任何 =(.

With it set up like this, when I click the button, I never see the loading screen. When I post the notification, does it execute and change the screen right away? If it does, I must be doing something wrong, maybe in my // Do stuff parts. If it does not, I don't think NSNotificationCenter is going to help me any =(.

编辑 2:

我设置了一个快速测试项目,并确认通知不会立即更新屏幕.我在上次编辑中发布的 btnClick 代码的行为与简单地执行完全相同

I set up a quick test project, and I confirmed that the notifications DO NOT update the screen right away. The btnClick code I posted in my last EDIT behaves exactly the same as simply doing

- (void) btnClick:(id) sender
{
    [loadingView setHidden:NO];

    // Do stuff that takes a long time here

    [loadingView setHidden:YES];
}

所以这证实了依赖于 NSNotificationCenter 的任何东西都不会帮助我.

So that confirms that nothing that relies on NSNotificationCenter is going to help me.

真的,只是看起来不支持在同步操作期间更改屏幕,这真的很令人失望.我现在没有想法.

Really, it just looks like there isn't any support for changing the screen during a synchronous operation, which is really disappointing. I'm out of ideas at this point.

推荐答案

我最终使用了 Grand Central Dispatch 来处理这个问题.最简单的方法是

I ended up using Grand Central Dispatch to handle this. The simplest way is to

- (void) doStuff
{
    // Show wait on start
    [myLoadingView setHidden:NO];

    dispatch_queue_t queue = dispatch_queue_create("com.myDomain.myApp",null);
    dispatch_async(queue, ^{
        // Code to execute
        {
            //... Do my time consuming stuff here ...
            // For testing purposes, I'm using
            int i = 0;
            while (i < 1000000000)
            {
                i++;
            }
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            // Hide Wait Screen on End
            // ... also need to do any other UI stuff here ...
            [myLoadingView setHidden:YES];
        });
    });
}

对我来说不幸的是,我的实际耗时的东西有一些 GUI 更改(例如显示警报、执行 segue、更改标签等).我发现将所有内容都嵌套在另一个 dispatch_async 中,而不是重写整个内容以尝试提取所有 GUI 内容,我仍然可以在操作执行时显示等待屏幕,以及所有在耗时的东西中完成的其他GUI内容将在完成时更新(等待屏幕消失后).

Unfortunately for me, my actual time consuming stuff has some GUI changes in it (like showing alerts, preforming segues, changing labels, etc.). Instead of rewriting the whole thing to try to pull all the GUI stuff out, I discovered that nesting it all in another dispatch_async, I can still get the waiting screen to show while the operations are performing, and all the other GUI stuff that is done in time consuming stuff will update when it's done (after the waiting screen disappears).

- (void) doStuff
{
    // Show wait on start
    [myLoadingView setHidden:NO];

    dispatch_queue_t queue = dispatch_queue_create("com.myDomain.myApp",null);
    dispatch_async(queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            // Double nesting the dispatches seems to allow me to do UI changes as part of 'Code to execute' below.
            // If I do not double nest like this, GUI behavior in "time consuming stuff" will be erratic
            dispatch_queue_t queue2 = dispatch_queue_create("com.myDomain.myApp",null);
            dispatch_async(queue2, ^{
                dispatch_async(dispatch_get_main_queue(), ^{

                    // Code to execute
                    {
                        //... Do my time consuming stuff here - GUI changes will appear when all the code has finished running ...
                        // For testing purposes, I'm using
                        int i = 0;
                        while (i < 1000000000)
                        {
                            i++;
                        }
                    }

                    // Hide Wait Screen on End
                    [myLoadingView setHidden:YES];
                });
            });
        });
    });
}

请注意,如果 doStuff 需要返回一个值,您可能需要稍微更改它以使用完成块",在此处接受的答案中有详细说明.

Note that if doStuff needs to return a value, you'll probably need to change it up a little to use a "completion block", detailed in the accepted answer here.

这篇关于是否可以在执行同步调用或其他操作时更新 UI 中的某些内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆