使用自动版式,如何保持一个UILabel在同一个地方当导航栏消失? [英] Using AutoLayout, how do I keep a UILabel in the same place when the navigation bar disappears?

查看:151
本文介绍了使用自动版式,如何保持一个UILabel在同一个地方当导航栏消失?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有它一个UILabel,打印一些话当按钮被窃听视图控制器。当按钮被窃听,导航栏设置为隐藏。

I have a view controller with a UILabel in it that prints some words when a button is tapped. When the button is tapped, the navigation bar is set to hidden.

于是,我采取的UILabel,并给予它在Interface Builder这些约束:

So I tried taking the UILabel and giving it these constraints in Interface Builder:

但这些,当我preSS按钮,跳转的UILabel下来的导航栏中消失,然后再回到向上,纠正自己,看着可怕。它应该留在原处永久,不管发生的事情与导航栏。

But with those, when I press the button, the UILabel jumps down with the nav bar disappearing, and then back up again, correcting itself, looking terrible. It should stay in its place permanently, no matter what goes on with the nav bar.

这里是直接链接到一个简短的视频显示会发生什么。

我将如何最好的去这样的UILabel停留在地方设置呢?

How would I best go about setting it so the UILabel stays in place?

项目: http://cl.ly/1T2K0V3w1P21

推荐答案

当你告诉导航控制器隐藏的导航栏,它会调整其内容视图(你的 ReadingViewController 的观点),以全屏和内容视图勾画出它的子视图的新的全屏幕大小。默认情况下,它的动画块的这个布局的之外,所以新的布局生效瞬间。

When you tell the navigation controller to hide the navigation bar, it resizes its content view (your ReadingViewController's view) to be full-screen, and the content view lays out its subviews for the new full-screen size. By default, it does this layout outside of any animation block, so the new layout takes effect instantly.

要解决这个问题,你需要做的视图进行动画块内的布局。幸运的是,SDK包括隐藏导航栏动画的持续时间常数,动画采用了线性曲线。更改 hideControls:方法如下:

To fix it, you need to make the view perform layout inside an animation block. Fortunately, the SDK includes a constant for duration of the animation that hides the navigation bar, and the animation uses a linear curve. Change your hideControls: method to this:

- (void)hideControls:(BOOL)visible {
    [UIView animateWithDuration:UINavigationControllerHideShowBarDuration animations:^{
        [self.navigationController setNavigationBarHidden:visible animated:YES];
        self.backFiftyWordsButton.hidden = visible;
        self.forwardFiftyWordsButton.hidden = visible;
        self.WPMLabel.hidden = visible;
        self.timeRemainingLabel.hidden = visible;
        [self.view layoutIfNeeded];
    }];
}

这里有两个变化。其中之一是,我使用 UINavigationControllerHideShowBarDuration 恒包裹方法体中的动画块,所以动画具有正确的时间。另一个变化是,我送 layoutIfNeeded 来认为,动画块里面,这样的意见,将要设置动画的新的帧。

There are two changes here. One is that I've wrapped the method body in an animation block using the UINavigationControllerHideShowBarDuration constant, so the animation has the correct duration. The other change is that I send layoutIfNeeded to the view, inside the animation block, so the views will animate to their new frames.

下面是结果:

您也可以使用这个动画块褪色您的标签进出改变,而不是他们的隐藏字母属性C>属性。

You could also use this animation block to fade your labels in and out by changing their alpha properties instead of their hidden properties.

在响应您的评论的问题:

In response to the questions in your comment:

首先,你需要了解运行循环的阶段。您的应用程序始终运行在它的主线程循环。环路,极度简化,看起来是这样的:

First, you need to understand the phases of the run loop. Your app is always running a loop on its main thread. The loop, extremely simplified, looks like this:

while (1) {

    wait for an event (touch, timer, local or push notification, etc.)

    Event phase: dispatch the event as appropriate (this often ends up
        calling into your code, for example calling your tap recognizer's action)

    Layout phase: send `layoutSubviews` to every view in the on-screen
        view hierarchy that has been marked as needing layout

    Draw phase: send `drawRect:` to any view that has been marked as needing
        display (because it's a new view or it received `setNeedsDisplay` or
        it has `UIViewContentModeRedraw`)

}

例如,如果你把 hideControls断点:,点击屏幕,然后看看在调试器堆栈跟踪,你会看到 PurpleEventCallback 曲线一路下滑(右上 __ __ CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION )。这告诉你,你在事件处理阶段是。 (紫色是苹果内部的iPhone项目的code的名字。)

For example, if you put a breakpoint in hideControls:, tap the screen, and then look at the stack trace in the debugger, you'll see PurpleEventCallback way down in the trace (right above __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__). This tells you you're in the event handling phase. (Purple was the code name of the iPhone project inside Apple.)

如果你看到 CA ::交易:: observer_callback ,你无论是在布局阶段或抽奖阶段。此外堆栈你会看到无论是 CA ::层:: layout_if_needed CA ::层:: display_if_needed 这取决于你在哪个阶段。

If you see CA::Transaction::observer_callback, you're in either the layout phase or the draw phase. Further up the stack you'll see either CA::Layer::layout_if_needed or CA::Layer::display_if_needed depending on which phase you're in.

所以这是运行循环及其阶段。现在,当它一观统领标记为需要布局?它被标记为需要布局在收到 setNeedsLayout 。如果,例如,你已经改变了你的观点应该显示的内容,他们需要被移动或调整相应的您可以发送这一点。但视图将自身发送 setNeedsLayout 自动两种情况:当其边界的大小改变(或大小其),并在其子视图阵的变化。

So that's the run loop and its phases. Now, when does a view get marked as needing layout? It gets marked as needing layout when it receives setNeedsLayout. You can send this if, for example, you've changed the content your views should display and they need to be moved or resized accordingly. But the view will send itself setNeedsLayout automatically in two cases: when the size of its bounds changes (or the size of its frame), and when its subviews array changes.

请注意,更改视图的大小或它的子视图不会使视图立即布置其子视图!它只是计划在运行循环的布局阶段后布置其子视图。

Note that changing the view's size or its subviews does not make the view lay out its subviews immediately! It's simply scheduled to lay out its subviews later, during the layout phase of the run loop.

所以...这是什么都与你做什么?

So... what does this all have to do with you?

在你的 hideControls:方法,你做 [self.navigationController setNavigationBarHidden:可见动画:是] 。假设可见 NO 。这里的导航控制器做什么回应:

In your hideControls: method, you do [self.navigationController setNavigationBarHidden:visible animated:YES]. Suppose visible is NO. Here's what the navigation controller does in response:


  • 它开始一个动画块。

  • 它设置导航栏的位置上面的屏幕的顶部。

  • 它通过44点(导航栏的高度)。
  • 增加了内容视图的高度
  • 该药可降低44点内容视图的Y坐标。

  • 这结束动画块。

  • It begins an animation block.
  • It sets the position of the navigation bar to above the top of the screen.
  • It increases the height of the content view by 44 points (the height of the navigation bar).
  • It decreases the Y coordinate of the content view by 44 points.
  • It ends the animation block.

到内容视图的框架的变化导致内容以自身发送 setNeedsLayout

The changes to the content view's frame cause the content view to send itself setNeedsLayout.

请注意,该更改导航条的框架和内容视图的框架的动画。但内容视图的子视图的帧具有的不可以的变化呢。在布局阶段后这些变化发生。

Note that the changes to the navigation bar's frame and the content view's frame are animated. But the frames of the content view's subviews have not change yet. Those changes happen later, during the layout phase.

所以导航控制器动画更改您的顶级内容的看法,但它不会改变动画您的内容视图的子视图。你必须强制将动画这些变化。

So the navigation controller animates the changes to your top-level content view, but it doesn't animate changes to the subviews of your content view. You have to force those changes to be animated.

您迫使这些变化通过采取两个步骤进行动画:

You force those changes to be animated by taking two steps:


  1. 您创建一个动画块,它的参数匹配导航控制器使用的参数。

  2. 里面的那个动画块,你强制布局阶段发生的立即的,通过发送 layoutIfNeeded 来的内容视图。

  1. You create an animation block whose parameters match the parameters used by the navigation controller.
  2. Inside that animation block, you force the layout phase to happen immediately, by sending layoutIfNeeded to the content view.

的<一个href=\"http://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/doc/uid/TP40006816-CH3-SW7\"><$c$c>layoutIfNeeded文档这样说:

The layoutIfNeeded documentation says this:

使用此方法强制子视图的布局绘制前。与接收器开始,这种方法向上通过视图层次只要superviews需要遍历布局。然后,它勾画出整个树的祖先之下。

Use this method to force the layout of subviews before drawing. Starting with the receiver, this method traverses upward through the view hierarchy as long as superviews require layout. Then it lays out the entire tree beneath that ancestor.

它勾画出从根发送 layoutSubviews 消息树的意见,以树叶整个树。如果你不使用自动布局,它也发送 layoutSubviews 来视图之前应用于每个视图的子视图的自动尺寸调整掩码。

It lays out the entire tree by sending layoutSubviews messages to the views in the tree, in order from root to leaves. If you're not using auto layout, it also applies the autoresizing mask of each view's subviews before sending layoutSubviews to the view.

因此​​,通过发送 layoutIfNeeded 您的内容来看,您强制自动布局立即更新您的内容视图的子视图的框架,前 layoutIfNeeded 的回报。这意味着,这些变化发生在您的动画块中,所以它们具有相同的参数(时间和曲线)为改变导航栏和内容视图动画。

So by sending layoutIfNeeded to your content view, you are forcing auto layout to update the frames of your content view's subviews immediately, before layoutIfNeeded returns. This means those changes happen inside your animation block, so they are animated with the same parameters (duration and curve) as the changes to the navigation bar and your content view.

布放子视图在动画块是如此重要,以至于苹果定义动画选项, UIViewAnimationOptionLayoutSubviews 。如果指定了此选项,然后在动画块结束时,它会自动发送 layoutIfNeeded 。但是,使用该消息的长版使用该选项要求, animateWithDuration:延迟:选项:动画:完成:,所以它通常更容易只是做 [self.view layoutIfNeeded] 自己在块的结尾。

Laying out subviews in an animation block is so important that Apple defined an animation option, UIViewAnimationOptionLayoutSubviews. If you specify this option, then at the end of the animation block, it will automatically send layoutIfNeeded. But using that option requires using the long version of the message, animateWithDuration:delay:options:animations:completion:, so it's usually easier to just do [self.view layoutIfNeeded] yourself at the end of the block.

这篇关于使用自动版式,如何保持一个UILabel在同一个地方当导航栏消失?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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