iPhone - 带进度条的启动画面

发布于 2024-08-04 09:48:01 字数 2175 浏览 12 评论 0原文

我尝试创建一个 SplashView,它在后台显示 Default.png,在前面显示 UIProgressBar。但是启动画面没有被更新......

在我的视图控制器中,我首先加载启动视图,参数是我的初始化有多少步,然后我通过 NSTimer 启动第二个线程,在每个初始化步骤之后,我告诉 SplashView 显示新的进度值。

理论上一切看起来都不错,但是当运行这个应用程序时,进度条没有更新(启动屏幕的方法接收值,我可以在日志中看到它)。我还尝试添加 usleep(10000);在这之间给视图更新一点时间,并且我没有使用进度条,而是直接在视图上绘制并调用 [self setNeedsDisplay];但一切都不起作用:/

我做错了什么?

感谢您的帮助!

汤姆

这是一些代码:

SPLASHSCREEN:
- (id)initWithFrame:(CGRect)frame withStepCount:(int)stepCount {
    if (self = [super initWithFrame:frame]) {
        // Initialization code

        background = [[UIImageView alloc] initWithFrame: [self bounds]];
        [background setImage: [UIImage imageWithContentsOfFile: [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], @"Default.png"]]];
        [self addSubview: background];

        progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
        [progressView setFrame:CGRectMake(60.0f, 222.0f, 200.0f, 20.0f)];
        [progressView setProgress: 0.0f];

        stepValue = 1.0f / (float)stepCount;

        [self addSubview:progressView];
    }
    return self;
}

- (void)tick {
    value += stepValue;
    [progressView setProgress: value];
}



VIEWCONTROLLER:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {

        splashView = [[SplashView alloc] initWithFrame: CGRectMake(0.0f, 0.0f, 320.0f, 480.0f) withStepCount:9];
        [self setView: splashView];

        NSTimer* delayTimer;
        delayTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(finishInitialization) userInfo:nil repeats:NO];
    }
    return self;
}

- (void)finishInitialization {
    // do some stuff, like allocation, opening a db, creating views, heavy stuff...
    [splashView tick]; // this should update the progress bar...

    // do some stuff, like allocation, opening a db, creating views, heavy stuff...
    [splashView tick]; // this should update the progress bar...

    // init done... set the right view and release the SplashView
}

I tried to create a SplashView which display the Default.png in the background and a UIProgressBar in front. But the splash screen is not being updated...

Inside my view controller I load first the splash view with a parameter how many steps my initialisation has and then I start a second thread via NSTimer and after each initialisation step I tell the SplashView to display the new progress value.

All looks good in theory, but when running this app the progress bar is not being updated (the method of the splash screen receives the values, I can see it in the logs). I also tried to add usleep(10000); in between to give the view updates a bit time and also instead of using the progress bar I drew directly on the view and called [self setNeedsDisplay]; but all didn't work :/

What am I doing wrong?

Thanks for your help!

Tom

Here is some code:

SPLASHSCREEN:
- (id)initWithFrame:(CGRect)frame withStepCount:(int)stepCount {
    if (self = [super initWithFrame:frame]) {
        // Initialization code

        background = [[UIImageView alloc] initWithFrame: [self bounds]];
        [background setImage: [UIImage imageWithContentsOfFile: [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], @"Default.png"]]];
        [self addSubview: background];

        progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
        [progressView setFrame:CGRectMake(60.0f, 222.0f, 200.0f, 20.0f)];
        [progressView setProgress: 0.0f];

        stepValue = 1.0f / (float)stepCount;

        [self addSubview:progressView];
    }
    return self;
}

- (void)tick {
    value += stepValue;
    [progressView setProgress: value];
}



VIEWCONTROLLER:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {

        splashView = [[SplashView alloc] initWithFrame: CGRectMake(0.0f, 0.0f, 320.0f, 480.0f) withStepCount:9];
        [self setView: splashView];

        NSTimer* delayTimer;
        delayTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(finishInitialization) userInfo:nil repeats:NO];
    }
    return self;
}

- (void)finishInitialization {
    // do some stuff, like allocation, opening a db, creating views, heavy stuff...
    [splashView tick]; // this should update the progress bar...

    // do some stuff, like allocation, opening a db, creating views, heavy stuff...
    [splashView tick]; // this should update the progress bar...

    // init done... set the right view and release the SplashView
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

花伊自在美 2024-08-11 09:48:01

正如另一个答案中提到的,在一段有限的时间内,当您的应用程序启动时,会显示 Default.png 并且您无法控制它。但是,如果在 AppDelegate 中创建一个显示相同 Default.png 的新视图,则可以创建从原始 Default.png 到可添加进度条的视图的无缝过渡。

现在,大概您已经创建了一个视图或类似的视图,并且您经常更新进度条,以便为用户提供一些反馈。这里的挑战是,您的视图仅在调用它来执行绘制矩形时才被绘制。但是,如果您从 AppDelegate 转到视图控制器的 viewDidLoad 的一些初始化代码,而运行循环没有机会确定哪些视图需要调用 drawRect,那么您的视图将永远不会显示其状态栏。

因此,为了完成您想要的任务,您必须确保调用drawRect,例如将大量初始化代码推送到其他线程或计时器任务中,或者您可以在之后自己调用drawRect来强制绘制设置上下文等。

如果您使用后台任务,请确保您的初始化代码是线程安全的。

As mentioned in another answer, for some finite amount of time, as your app is being launched, Default.png is displayed and you have no control over it. However, if in your AppDelegate, you create a new view that displays the same Default.png, you can create a seamless transition from the original Default.png to a view that you can add a progress bar to.

Now, presumably, you have created a view or similar and you are updating a progress bar every so often in order to give the user some feedback. The challenge here is that your view is only drawn when it gets called to do a drawRect. If, however, you go from AppDelegate to some initialization code to a viewcontroller's viewDidLoad, without the run loop getting a chance to figure out which views need to have drawRect called on, then your view will never display its status bar.

Therefore in order to accomplish what you want, you have to either make sure that drawRect gets called, such as by pushing off a lot of your initialization code into other threads or timer tasks, or you can force the drawing by calling drawRect yourself, after setting up contexts and such.

If you go with the background tasks, then make sure your initialization code is thread-safe.

恏ㄋ傷疤忘ㄋ疼 2024-08-11 09:48:01

Default.png 只是一个图形,是应用程序启动时显示的静态图像。如果您想显示进一步的进度,则必须在 applicationDidLaunch 阶段显示所有内容。首先在此处显示模式“启动屏幕”(创建一个视图控制器,将其视图添加为主窗口的子视图),并在完成所需的任何额外加载后将其关闭。

Default.png is just a graphic, a static image shown while the application is launching. If you want to show further progress, you'll have to show everything at the applicationDidLaunch phase. Show your modal "Splash Screen" there first (Create a view controller, add its view as a subview of your main window) and dismiss it when you are done whatever additional loading you needed to do.

千秋岁 2024-08-11 09:48:01

另外,您需要在单独的线程中更新进度栏。在进行大量业务的同一线程中更新 GUI 是一个坏主意(在我看来,但我可能是错的)。

Also, you need to do update your progress bar in a seperate thread. Updating your GUI in the same thread where a lot of business is going on is (in my opinion, but I could be wrong) a bad idea.

不醒的梦 2024-08-11 09:48:01

据我所知,主线程是唯一可以安全地执行 GUI 操作的线程,它的事件循环(即主应用程序线程)是在调用 后执行实际显示的线程-setNeedsDisplay。生成一个新线程来执行加载,并更新主线程上的进度。

The main thread is, as far as I know, the only one that can safely do GUI things, and its event loop (that is, the main application thread's) is the one that does the actual displaying after you've called -setNeedsDisplay. Spawn a new thread to do your loading, and update the progress on the main thread.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文