If we use the navigation bar of iOS system and set titleView, leftItem and rightItem by ourselves, when the length of titleView reaches a certain value, push titleView will jump left and right. This paper will analyze the reasons and solutions for the jump.

Internal layout of navigation bar

In a brand new APP, after customizing the left, middle and right of the navigation bar, check the layout and you will find that the internal layout of the navigation bar is as follows

Set the custom leftItem, titleView, and rightItem. In the navigation bar, our custom view will be wrapped by _UITAMICAdaptorView. Which leftItem and rightItem will parcel layer outside the _UITAMICAdaptorView _UIButtonBarStackView, the final layout in _UINavigationBarContentView.

In the navigation bar internal layout of the left block, the middle block and the right block, hereinafter referred to as ABC, the entire screen Width is.

Take the iPhone XS Max as an example. Gap1 is 20 and GAP2 is 6.

The safety area

A must be away from gAP1 on the left, regardless of the width (including 0).

C no matter the width (including 0), must be away from the right gAP1.

B, however wide, must be gAP2 away from A and C.

When the width of A and C is set to 0, B is separated from the left and right of the screen (gap1+ Gap2).

When A and C are set to nil, B is 12 to the left and 12 to the right of the screen (GAP3).

alignment

When the width of A is increased, A is widened by keeping the left side motionless and increasing the right side. The width of B will be compressed as the width of A increases, and the maximum width of A is not more than C.left-gap2*2.

When the width of C is increased, C is widened by keeping the right side motionless and increasing the left side. The width of B will be compressed as the width of C increases. The maximum width of C is no more than A. light -gap2*2.

When adjusting the Width of B, B defaults to the anchor point in the center of the navigation bar, and increases both left and right at the same time, and the maximum value will not exceed 162(width-a.width-b.width-gap12-gap22).

When all ABC is set to screen Width, B will be completely crowded, and AC will split all space except the safety zone (width-gap12-gap22).

Navigation bar title bar animation

The creation of a jump from left to right

First understand the previous layout, can know the x coordinates of B relative to A calculation formula

B.left = Max( (Width – B.width)/2 , A.right+gap2)

The x-coordinate of B should ideally be (width-b.width)/2, which is where the animation ends. The actual x-coordinate position could be (width-b.width)/2 or (a.light +gap2) (whichever is the maximum), which is the final layout position.

When the actual position is A.light +gap2, it indicates that the initial position of the animation is to the left of the actual position, and there will be a jump from left to right in the case of push.

The creation of a jump from right to left

Similarly, the formula for calculating the right coordinate of B relative to C

B.right = Min( (Width + B.width)/2 , C.left-gap2)

The right coordinate of B is ideally (Width + B.width)/2, which is the end position of the animation. The actual position may be (Width + B.width)/2 or (c.left-gap2) (whichever is the minimum), which is the final position of the layout.

When the actual position is (c. left-gap2), it indicates that the initial position of the animation is to the right of the actual position, and there will be a jump from right to left on the right side of the navigation bar title when push occurs.

To prevent jumping conclusions

In order to prevent the above two jumps, let the left position of B be (Width – b.width)/2 and the right position of B be (Width + b.width)/2, i.e

(Width + b.width)/2 > (a.light +gap2) and (Width + B.width)/2 < C.left-gap2. A. light = gap1 + a. time + gap2 And the Width limit of B. Width < width-gap12-gap22-a. width2 and B. Width < width-gap12 can be obtained by c. ft = width-gap2-c. width-gap1 < Width -gap12-gap22-max (a. witchth, c.witchth)*2

The width of B must not exceed the width of the screen. Subtract 2 times the width of A and C from the fixed security zone.

The solution?

No, yet, until now this step, the practice of is A hand before Q8.0.0, possible maximum width has A and C (because the width of the AC is may change, such as the left there is no unread messages and 99 unread width is different, for example there may be an icon or two ICONS on the right), and then get B width is very narrow.

As shown in the figure, there is still A large distance between B and A that has not been used. What should I do if I want to use this space without any jump?

Overrides jumps from right to left

First of all, go back to the navigation bar title bar animation – jump from right to left generation, in fact, because the system animation itself is from right to left, so you can’t see the jump, will make people think it is a normal animation, the following two pictures, as far as animation is concerned, will not make people jump feeling.

The reason for the jump is that the content of B slides through THE content of C

However, in general, C only places ICONS, and the blank area is large. It is actually acceptable for B’s content to slide through C with animation.

If acceptable, then the width of B becomes dependent only on the width of A

B.width < Width – gap12 – gap22 – A.width*2

Does not accept “override jumps from right to left”

No, the perfectionist said, I just can’t accept A little jump, and the above method only solved the case of C greater than A, A greater than C is still A problem!

Ok, so let’s focus on planB

Content out of bounds scheme

First of all, content in ABC can be displayed beyond the width limit of ABC! (Hereafter referred to as ABC)

What does that mean? Going back to the previous picture, when I set the x-coordinate of A’s content “< left” to -20, A appears on the left side of the screen.

If I set ABC width to 0 and look at the content display:

It can be seen that, except that THE X coordinate of A is set to -20, both B and C are displayed from the origin of the X coordinate of B and C, and they are all displayed, and will not be displayed because the width is 0, which is the conclusion: the display of the content of ABC will not be affected by its width, but its position will be affected by the X coordinate of ABC. (If you can’t set clipsToBounds to true for custom views, of course)

In other words, based on the “conclusion of preventing jump”, we can adjust the position of B according to the width of AC, as shown in the figure below

C is wider than A, and the width of X between B and A is empty (x. witchth = c. witchth – a. witchth), then the position of the starting point of X of B can be calculated as -x. witchth (a.witchth – C.witchth). The maximum Width of b is width-a.width-c.width-GAP12-GAP22;

Similarly, if A is wider than C, the Width of X is empty between B and C (X.width = A.width-c.width), then the x-coordinate of B is 0, and the Width of B is width-a.width-c.width-gap12-gap22.

To sum up, the formula for calculating B is

b.left = Min(0, A.width – C.width) b.width = Width – A.width – C.width – gap12 – gap22

When the background color of B is set to transparent, only the content of B can be seen.

(PS. By the practice that, when a x coordinate in safety area within gap1, push the animation will have a change from the region, with the need to the right place at the right of c also has the safety area, so suggest a and c content not across the safety area, but this also is to have a solution, later.)

Based on the above scheme, we can also set the width of B to 0 at the beginning, and then only calculate the coordinates and width of B each time. We can also make B occupy the area left and right of GAP2 through calculation.

Practical effect on hand Q: Long caption on left, short caption on right (number of unread messages from zero on left)

Attached: values of GAP1 and GAP2 under different models

Added gAP3 (left/right screen distance when A and C are set to nil)

To sum up, it can be judged that

if (SCREEN_WIDTH > 375) {
    gap1 = 20;
    gap3 = 12
} else {
    gap1 = 16;
    gap3 = 8;
}
    gap2 = 6;
Copy the code

Demo source: github.com/Xieyupeng52…

If it helps you, please give me a Star on Github to encourage you O(∩_∩)O thank you!