About the disordered button order caused by modifying the built-in tabBarItem attribute during custom tabBar, tabbartabbaritem

Source: Internet
Author: User

About the disordered button order caused by modifying the built-in tabBarItem attribute during custom tabBar, tabbartabbaritem
Questions about Button Order disorder caused by modifying the built-in tabBarItem attribute during custom tabBar

Test code: http://git.oschina.net/Xiyue/TabBarItem_TEST

Book address: http://www.jianshu.com/users/f599d56f0592/latest_articles

Collation

In today's mainstream frameworks, the attributes of tabBar are generally set globally in tabBarController and will not be changed once set. in addition, tabBar is customized in most apps, And the layoutSubviews method is rewritten to re-layout items. for example:

  
1-(void) layoutSubviews {2 [super layoutSubviews]; 3 4 CGFloat btnX = 0; 5 CGFloat btnY = 0; 6 CGFloat btnW = self. frame. size. width/5; 7 CGFloat btnH = self. frame. size. height; 8 9 NSInteger index = 0; 10 // traverse the child Control 11 for (UIView * tabBarButton in self. subviews) {12 if ([tabBarButton isKindOfClass: NSClassFromString (@ "UITabBarButton")]) {13 if (index = 2) {14 index + = 1; 15} 16 17 btnX = index * btnW; 18 tabBarButton. frame = CGRectMake (btnX, btnY, btnW, btnH); 19 20 index ++; 21} 22} 23}

 

However, in this case, if you need to modify the attributes of tabBarItem in the sub-controller of tabBarController, some unexpected problems will occur. What are the problems? Let's look at the figure:


Snip20160719_9.png
Snip20160719_11.png Problem proposal

Did you find that the sequence of sub-controllers in tabBarController is different from that displayed in running? The first controller we set unexpectedly ran to the last one, but after the program was started, the first "I" Controller view is displayed on the window by default. That is to say:SelectedViewController has not changed. It is the first in the default tabBarController to set the subcontrol sequence (childViewControllers [0]). However, the position of the tabBarItem bound to this subcontroller has changed.

Cause search

What causes the change? The test shows that the effect of this combination is as follows:

  • Condition 1: Customize the tabBar andOverride the layoutSubviews method and customize the Layout; This problem does not occur if the layoutSubviews method is not rewritten;
  • Condition 2: Modify the attributes of tabBarItem that comes with the system. Examples of common attributes are as follows:
    • 2.1 If the modified title (tabBarItem. title) is the same as the title set in tabBarController, this phenomenon does not occur. If it is changed to a different one, this phenomenon can occur.
    • 2.2 image, selectedImage, TitleTextAttributes, and TitleTextAttributesProperties related to status classesWhether it is the same as the previous attribute or not, this phenomenon will occur. In particular, TitleTextAttributes, even if you upload an empty dictionary, it will still cause this phenomenon.

Snip20160719_12.png Exploration

OK, sinceOverride the layoutSubviews method and customize the LayoutThis happens, andOverwrite but not customize the LayoutBut this does not happen, so we can start to explore the reasons here.
Below are some simple pieces of code that I wrote myself to output items. Because UITabBarButton is a private control, we cannot view the Internal Attributes and implementation logic. We can only look at the clues from some clues:

1-(void) layoutSubviews {2 for (UIView * tabBarButton in self. subviews) {3 if ([tabBarButton isKindOfClass: NSClassFromString (@ "UITabBarButton")]) {4 NSLog (@ "% @", tabBarButton ); 5} 6} 7 NSLog (@ "outputs"); 8 [super layoutSubviews]; 9 10 CGFloat btnX = 0; 11 CGFloat btnY = 0; 12 13 CGFloat btnW = self. frame. size. width/5; 14 CGFloat btnH = self. frame. size. height; 15 NSInteger index = 0; 16 // traverse the sub-control 17 for (UIView * tabBarButton in self. subviews) {18 if ([tabBarButton isKindOfClass: NSClassFromString (@ "UITabBarButton")]) {19 NSLog (@ "% @", tabBarButton); 20 if (index = 2) {21 index + = 1; 22} 23 24 btnX = index * btnW; 25 26 tabBarButton. frame = CGRectMake (btnX, btnY, btnW, btnH); 27 28 index ++; 29} 30} 31 NSLog (@"----------------------------------------------"); 32 for (UIView * tabBarButton in self. subviews) {33 if ([tabBarButton isKindOfClass: NSClassFromString (@ "UITabBarButton")]) {34 NSLog (@ "% @", tabBarButton ); 35} 36} 37 NSLog (@ "================================ ================== "); 38}

 

The following is the result:


Snip20160719_13.png

For convenience, the six ABCDEF regions are distinguished, and a frame change point is left between 1 and 6.
Note:
Before the first equals sign (=)All the outputs are printed on the layoutSubviews method for the first time;
After the first equal sign (=) is splitAll outputs of are the printed results of the layoutSubviews method after the tabBarItem attribute is modified;
The first minus sign (-) is before [super layoutSubviews ].;
The second minus sign (-) is before [super layoutSubviews], before the custom Layout;
The second minus sign (-) is after the custom Layout.

  • FirstFrom region A and Region BLabel 1AndLabel 2It can be seen that the system's default location coordinate (origin) of the First UITabBarButton (the tabBarItem type of the system is UITabBarButton type) is (), after the first custom layout is changed ), at this time, the UITabBarButton is the tabBarItem corresponding to the first sub-controller ('my'), and its memory address is:0x7fab39530010. (Take a look at other memory addresses. The memory addresses on the layer will be used for comparison later .)
  • SecondLet's look at the areas C and D.Tag 3 4 5We can see that:
    • When the tabBarItem attribute is modified and the method is used again, no more information can be found.0x7fab39530010This memory address, but an additional0x7fab2131fc50Memory address, which is in tabBar. subviewsEnd of Array.The same is true for layer-based memory addresses..
    • 0x7fab39530010This frame is the frame before the first custom layout.
    • Observe that the memory address of other tabbaritems has not changed. The same is true for the memory address of the layer.
    • Check the Red Arrow.Green Label 6Misleading. Its memory address shows that it is the second element in the original tabBar. subviews.
  • AgainFrom the BD area, we can see the tabBar at the beginning of the first custom layout and the second custom layout. the frame of subviews is different, but in terms of memory address, except that the memory address of the tabBarItem that we changed the attribute is different, all others are the same.
Conjecture

Since tabBar is a private control, you cannot view the internal code logic, and perform conjecture analysis on some of the above appearances again:

  • A: The set method is used inside the tabBar to filter the attributes, including checking whether the attributes to be modified are consistent with those previously modified (except for state-related or state-related attributes cannot be filtered)
    Therefore, this problem does not occur if the title attribute is consistent with that set in tabBarController.If the logic is filtered, a processing is executed, which is the culprit of this phenomenon.
  • B> what is the culprit? From the previous analysis, we can know that, although the memory address is changed, the object to be pointed to is an object that is exactly the same as the previous attribute. This is actually a method of deep copy, right?
    So why is this not the case when the title attribute is changed if it is consistent with the tabBarController setting?Deep copyIs there a correspondingShortest copy? We can see it.

Snip20160719_15.png

As shown in the figure, when the modified attribute content is the same as that set by the Controller (that is, self. title = @ "I";), the whole memory address is the same, without any changes, only some changes have occurred in the middle of the frame, changed back to the system default.
So: Can we guess:
1: In fact, each layoutSubviews operation is performed by default in the system (note the 'default' keyword). The tabBarItem of the system's default (childViewControllers order) is copied and the frame is re-computed, this is done in [super layoutSubviews;
2: When modifying some attributes of tabBarItem, the filter in the set method will be executed;
(A) If the attribute to be modified is exactly the same as the current one (except for state-related attributes, or state-related attributes cannot be filtered through this filter), it is a shortest copy, (that is, the default situation );
(B) When the attribute to be modified is completely different from the current one, the filtered logic is executed, that is, deep copy;
This explains why the original object memory address cannot be found when some attributes are modified, but another new memory address appears,Because the memory address pointed to by the tabBarItem changes to the address of the object that is copied in depth.

  • C: as to why the order of arrays has changed, I have thought a lot about it. Here is the most likely idea:
    TabBarItem with no attribute change is used as the basic array of Subviews after an address is copied in A shortest. Then, A copies A new array A_new address obtained after modifying the data and adds it to the array, in this way,However, the order of childViewControllers has not changed, so selectedViewController is still A instance. Therefore, after the program is started, the view of the controller corresponding to the last tabBarItem is displayed.As shown in.

Snip20160719_17.png

Finally, if the attributes of multiple tabbaritems are modified, the order of modification is also the sequence in which the subcontroller is set in the tabBarController.
All of the above are personal guesses. Only Apple officially knows what is done inside the system. If there is any error, I still hope to correct it.

Code: @ XiYue on git.oschina.net.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.