Xamarin.iOS:iOS Layout gotchas Redux (reprint)

Source: Internet
Author: User

Original address: http://blog.adamkemp.com/2014/12/ios-layout-gotchas-redux.html

IOS Layout Gotchas Redux

Since my last Post on IOS layout Gotchas I had encountered a few more basic Layout mistakes that can leads to bugs and BRI Ttle code. Some of these is things I has found myself doing so hopefully they would be useful to others.

    • Don ' t use the parent ' s Center
    • Avoid misaligned views
    • Don ' t call Layoutsubviews directly
    • Don ' t set your own Autoresizingmask
Don ' t use the parent ' s Center

I have seen (and even written) code like this:

var parent = child. Superview;child. Center = child. Convertpointfromview (parent. Center, parent);

This code does handle coordinate systems properly (see my previous post), but it's still brittle code. Consider what's the intent is. What are we ' re trying to does is center the child in its parent. The code makes sense because it's just saying the center of the child should be the same as the center of the parent (translated to the child's coordinate system). The problem is, this code is assuming, the parent already has a valid center. That's, this code assumes. Layout has already been do for the grandparent (most likely the view Responsibl E for positioning the parent). That is, the IS is the case.

Aside from explicit requests to layout (via SetNeedsLayout ), layout typically happens in a given view because it size has changed . When the IT size changes it redoes layout to position its children. If the size doesn ' t change and layout probably won ' t happen (even if the position does change). That's means it's possible for the size of the parent to being set and trigger layout before it has a position. The position change later (meaning it change Center ) without going through laying off its children.

Basically what's means is your should never trust the property of Center a view's parent unless you ' re the one who set it . Instead you should use the of the Bounds parent view:

var parent = child. Superview;  new cgrect{    X = (2),    Y = (2), width = width, height = height,};  

Notice that I switched Frame to the using, and I cast to an int . For a explanation see the next item.

Avoid misaligned views

On IOS the layout and rendering system uses floating point values for size and position. This was nice because it allows for transforms and coordinate systems so, views don ' t has to be exposed to things like The pixel density of the device. Everything works in generic "points", and somehow that gets rendered to the screen. However, at some point those points is converted to actual pixels, and pixels is discrete things. If you want a line that's precisely one pixel thick then the need to make sure the it falls precisely on a pixel Boundar Y. Otherwise it'll get anti-aliased, and it'll look blurry. In fact, for any view if the position of this view does not fall on a pixel boundary when it's rendered then that view WI ll be anti-aliased. This can result in the whole view being blurry, which are especially noticeable in text on Non-retina devices. To find these kinds of problems easily your can run your app in the simulator and then choose the "Color misaligned Images" Option froM the "Debug" menu. It'll color any problematic views in purple (ignore the yellow views; they represent a different situation Cases is expected and benign).

The most common-in-the-end situation is with centering code like the previous example. Consider this naive implementation:

var parent = child. Superview;  New cgrect{    2,    2,    width = width,    height = height,};  

This is a common-to-center a view, and it technically gets the exact right answer. If the width of the parent is and the width of the that child are then the 10 5 X position of the should be 2.5 ( (10 - 5) / 2 = 5/2 = 2.5). However, is isn't on 2.5 a pixel boundary, which means this view would be anti-aliased. You don ' t want that. The simple fix for truncate the X and Y after centering, which would possibly make it a half pixel off center Avoids the anti-aliasing and is in almost every case totally unnoticeable.

What does if you have the Center ? Well, that gets a little trickier. It seems simple. Again, the naive implementation is this:

 var parent = child. Superview; var parentbounds = parent. Bounds;child. Center = new cgpoint{X = parentbounds.width/2, Y = parentbounds.height/2,}      

You might think after looking at the previous fixed example then all you need to does here are truncate X and Y, but that Would is wrong as well. To see why consider happens if the parent's width is all and the child's width is 5 . If you center with the above code then you'll get a center X of 7 , which would put the Top-left X at 4.5 ( 7-(5/2) = 7-2.5 = 4.5 ). That ' s not a pixel boundary! That's might make think your should just force it to always be a half-pixel and that's would be wrong too. Generally speaking, whether the center should be an integer or not depends on whether the size of the child is even or not. That makes just setting the center complicated. Easier I wrote an extension method, does the right thing:

 public  static void safesetcenter (this UIView View, Cgpoint Center) {var size = view. Bounds.size; Center = center. Floor (); if ((int) size. Width% 2! = 0) {Center. X + = 0.5f;} if ((int) size. Height% 2! = 0) {Center. Y + = 0.5f;} view. Center = center;}               

(This relies the another extension method CGPoint for the floor both X and Y, but that's trivial to write).

What this function does are first truncate the given center and then optionally add back half a pixel to each component if necessary. This code does isn't handle non-integral sizes, but the ' s usually a bad idea so I just ignore it.

Would modify the previous example like this:

var parent = child. Superview; var parentbounds = parent. Bounds;child. Safesetcenter (new cgpoint{    2,    2,});   

Since centering a view within its parent was an especially common operation I also made an extension method for that Specif IC Case:

Centerinparent (this UIView view) {    = view. Superview;    = = null) {throw new InvalidOperationException (2));}  

Now-to-use the could just write this code:

Child. Centerinparent ();
Don ' t call LayoutSubviewsDirectly

The IOS layout system is designed to being mostly asynchronous to avoid doing extra work. When you need layout SetNeedsLayout to happen, and at some later time the method would be LayoutSubviews called. However, sometimes you need to ensure that layout happens synchronously. Usually this comes up when dealing with animations. If you make a change of that affects layout, and you want the change to is animated, then the need to force a Synchronou s layout within the animation block (or maybe force it before, the animation so, the first layout is a part of the Imation). To force a layout you should never call LayoutSubviews directly. Instead should call LayoutIfNeeded , which would do a synchronous layout only if the view is marked as needing layout.

Still, there is times when you need to force a layout to happen synchronously always(as in, you need to the IT "you had a change of that requires redoing layout, andI want you to do the layout right now instead of later). In those situations I has previously made the mistake of thinking that I should just callLayoutSubviewsDirectly. After all, ifSetNeedsLayoutImmediately followed byLayoutIfNeededThen you might expect those and lines to the same effect as aLayoutSubviews. Why isn't write just the one line? However, it turns out of that and the lines don ' t do exactly the same thing as the one. Specifically,LayoutSubviewsDoes one and only one thing:it lays out the subviews for that oneView. It doesn ' t force layout recursively on all of the descendants of that view. But if you is needing layout to happen synchronously then you almost certainly want it to happen for all descendants as W Ell. The function, that does, isLayoutIfNeeded. Therefore, if you really want to force layout to happen and you want it did synchronously then you should call both Funct Ions like this:

View. Setneedslayout (); view. Layoutifneeded ();

If you are only require this layout happen synchronously if necessary, but you don ' t need to force it to happen if it wasn ' t a Lready necessary then your just need the one line:

View. Layoutifneeded ();

In any case you should never call LayoutSubviews directly on any view (even your own subviews). Just let the layout system does its job.

Don ' t set your own AutoresizingMask

This was a special form of violating the "Top-down Principle" I described in my previous post. This principle is, the owner of a view (usually its parent view), should set the size and position of that view. The view should never set its own size and position.AutoresizingMaskis a a-causing a view to grow or move along with its parent, which have the effect of setting that view ' s size and POS Ition automatically. That's still a decision that should was left to the parent, though. I have encountered code recently in which a view is setting its ownAutoresizingMask, effectively deciding for the view how it would behave within its parent. The result is, the parent was no longer in control, and that can make changing the layout process difficult. For instance, I is trying to does a specific animation, and because the view had asserted this relationship between its siz E/position and its parent ' s I is not able to get the effect I wanted. The fix is to remove theAutoresizingMaskLine. If the code had been in the parent in the first and then the would has been easy to determine, but instead I spent Qu Ite a bit of time trying to figure out why the animation was misbehaving.

The is AutoresizingMask a layout trait just like Bounds , Center , and Frame , and it should being the responsibility of the owner alone to S ET those properties.

Xamarin.iOS:iOS Layout gotchas Redux (reprint)

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.