Xamarin.iOS:iOS Layout gotchas (reprint)

Source: Internet
Author: User

About inheriting the layout of the UIView neutron control. Original address: http://blog.adamkemp.com/2014/11/ios-layout-gotchas-and-view-controller.html

In a previous post I touched on layout in IOS by describing the difference between Frame Bounds and, and in that post I cov Ered one of the most common layout mistakes I encounter in IOS code. However, there is several other common mistakes on layout that I had been seeing a lot recently. Read on to learn what to avoid these gotchas in your own code.

    • The Top-down Principle
      • Violation 1
      • Violation 2
      • Violation 3
    • Defer layout Until the layout Overrides
      • Violation 1
      • Violation 2
    • Never use UIScreen ' s Size for Layout
    • Summary
The Top-down Principle

The most general rule of layout are this:layout should always be do top-down. What I mean by so is, the size and location of a view should only ever are set by the owner of that view. Put another, you should never set the from Frame a view that you didn ' t create yourself.

Let's consider a few concrete examples of some common violations of this rule.

Violation 1
 public  Class myview:uiview{public myview (CGRect frame) {frame = new CGRect (0, 50, Span style= "color: #666666;" >50); }}

In this example you can see that we had a custom view, and the constructor for that view it sets it own Frame . never set your own Frame . Recall the Rule:don ' t set the of Frame a view that you didn ' t create. A view can ' t create itself so it shouldn ' t set its own Frame . The size and location of a view are something that only their creator should set.

Violation 2
 public  Class myviewcontroller:uiviewcontroller{// ... public  Override void viewdidload () { View.frame = UIScreen.MainScreen.Bounds; //...}}         

This was a similar example as the first one, but in this case we are using a view controller instead of a view. Some people seem to think that view controllers don ' t has to follow the same layout rules as view, but they is wrong. A View Controller does not control its layout. I'll discuss view controllers more later in this article.

Violation 3
 public  Class myview:uiview{private readonly Myotherview _myotherview; //... public  Override void layoutsubviews () { Span style= "color: #008000; Font-weight:bold; " >base. Layoutsubviews (); _myotherview.frame = Bounds; _myotherview.somesubview.frame = Bounds; }}

This example shows a violation from the other direction. In this case we're not setting our own Frame , but we're setting the Frame a view that we don ' t own: _myOtherView.SomeSubview belongs to _myOtherView. Presumably the LayoutSubviews override in would MyOtherView set SomeSubview ' s Frame . We should not being subverting its layout decisions. This is a good a-to-end up with subtle bugs where the pieces of code fight over how-to-do layout.

Defer layout Until the layout Overrides

Another common mistake in layout are trying to do layout too early. There is a few key locations where layout should be performed:

    • For a UIView subclass use LayoutSubviews .
    • For a UIViewController subclass use ViewDidLayoutSubviews .

These methods exist specifically to give a opportunity to do layout. If you try the to does layout before These methods then the is likely to base your layout on invalid information.

Violation 1
myview:uiview{    Base (frame) {Addsubview (new Myotherview (frame));}} 

This is a very common mistake, and it happens because people get lazy. You can see that we is creating a new view ( MyOtherView ), giving it a size, and adding it as a subview all in one line with our C Onstructor. It ' s so easy, right? It ' s also wrong. What happens when your Frame changes? That's Subview won ' t change size. This code assumes a static size set at the time of construction, but as we learned previously our size and position is OW Ned by our Creator, and therefore we creator can change we size and position at any time.

Here is the corrected code:

 public  Class myview:uiview{private readonly Myotherview _myotherview; public myview () {_myotherview = Span style= "color: #008000; Font-weight:bold; " >new Myotherview (); Addsubview (_myotherview); } public  Override void layoutsubviews () { Span style= "color: #008000; Font-weight:bold; " >base. Layoutsubviews (); _myotherview.frame = Bounds; }}

Now we assign our subview to a field, and we defer the layout to the proper layout method ( layoutsubviews ) . Yes, this was slightly more work, but it was also much more robust. As a bonus noticed that I set our Subview's Frame to our own Bounds , whereas in the Incorrect code we were using our own Frame . If you need a refresher in what the difference are then refer to my earlier post.

I also want to point out another subtle change:i removed the CGRect frame argument from the constructor. Have that argument in UIView ' s constructor is, in my opinion, a mistake on Apple's part. It encourages people to write incorrect code by trying to merge the act of view creation with the act of view layout. Those the belong together. If you include that parameter and not only would tempted to use that argument within your constructor (which we jus T learned is wrong), but you'll also be encouraging your clients to use it (which they shouldn ' t-do). I really hate writing code like this:

New MyView (Cgrect.empty);

That ' s just noisy code. It should just is this:

New MyView ();

Do yourself a favor and don ' t follow Apple's example. Don ' t include a frame argument in your custom view constructors.

Violation 2
myviewcontroller:uiviewcontroller{    //...    base. Viewdidload (); View.addsubview (new MyView () {Frame = view.bounds});}}  

This should look very familiar. It is, in fact, the view controller analogue of the previous example. In this case, just as before, we is trying to set the of Frame a view we just created. Instead, we should be deferring this work until later.

This mistake was actually very common, and it often goes unnoticed for a while. The reason is that often would has View.Bounds a perfectly reasonable value in ViewDidLoad . Frequently that size comes from the. Xib, and so it'll be whatever size the. Xib is designed for (often portrait screens Size). This bug was usually discovered in one of the following scenarios:

    • The screen size changes. A lot of people ran to this when updating their apps for the taller IPhone 5, and probably many more ran to it when up Dating their apps for the IPhones 6.
    • Adding IPad Support (a variation in the screen size changing).
    • Rotation (also technically a variation of the screen size changing).

All of these expose the same invalid assumption:the initial size of a view controller ' s view was not necessarily the Corre CT size. It may change later (remember rule 1:the owner of the view owns the size of this view). I'll go to more detail on view controllers in a future article.

Here are the corrected code, which should again look very familiar:

PublicClassmyviewcontroller:uiviewcontroller{private MyView _myview;    //... public  Override void viewdidload () {base. Viewdidload (); _myview = new MyView (); View.addsubview (_myview); } public  Override void  Viewdidlayoutsubviews () {base. Viewdidlayoutsubviews (); _myview.frame = View.bounds; }}

The same pattern as I used when fixing the first example, but it's adapted for a view controller.

Never use UIScreen' s Size for Layout

Another common mistake I see was trying to use the size of the screen to do layout. This typically happens in conjunction with a violation of the first rule. For example, look again at the second example from above:

 public  Class myviewcontroller:uiviewcontroller{// ... public  Override void viewdidload () { View.frame = UIScreen.MainScreen.Bounds; //...}}         

We already discussed why are you shouldn ' t is setting your own Frame here, but let's consider why do you shouldn ' t use the screen ' s bounds. The primary reason is that it violates the principle of top-down layout. When you use the screens size here is assuming that this view would always fill the screen (or fill some fixed portion of the screen). That's a problem because it makes this view controller (or view) hard to reuse. It ' s not adaptable. View controllers can presented full screen, but they can also is presented as form sheet modals, page sheet modals, in Popovers, in slide-out views, and so on. View controllers should adapt to the environment they is put in, just like custom subviews. Whenever possible you should base your layout logic on whatever size your view actually have rather than pinning it to the Size of the whole screen.

That said, there was times when a view controller really was designed specifically to fill the screens, and that's all that View controller would ever is used for. You could find yourself wanting to precisely lay out the ' views for a ' specific screen size. I have spent most of my iOS development time working on IPAD applications, and we had the luxury (so far) of making Assum Ptions about how many points (device-independent pixels) there is on the screens and very carefully laying out we views t O Take advantage of the screen real estate. For instance, if your look at the view in Virtualbench (an app I worked on) you'll see a example of a very carefully des Igned UI. We chose every font size, margin, and view size specifically for a landscape iPad view size because that's the only view S Ize we needed to support. In cases like thisstillShouldn ' t useUIScreen.MainScreen.Bounds. Instead should just use your own hard-coded constants. The reason for.UIScreen.MainScreen.BoundsCould still change! It ' s A variable, after all. It ' s not a constant. If your layout depends on a constant then use a constant.

This is a hypothetical concern. In fact, as I mentioned in a footnote in my previous article, in IOS 8 the meaning of Uiscreen.mainscreen.bounds did in the fact change. Previously (7.x and earlier) the screen ' s Bounds were fixed to a certain size regardless of the interface Ori Entation. That's, as you rotated the device the app's view would rotate around (swapping its width and height), but the screen ' s bounds never changed size (or origin). Starting in IOS 8, though, the screen ' s Bounds does change as you rotate the device. If You use code like you see in the bad example above then your view could get the wrong size depending on what the user is H Olding his device at the time.

This specific mistake could come up for people updating their code to use the IOS 8 SDK. I have the been working on doing the for one of my projects, and I found several examples of this mistake in our code. As a result our views were in the wrong place. The fix is to use and does the View.Bounds layout work in ViewDidLayoutSubviews instead.

Summary

These is the key takeaways from this article:

    1. Never set your own in Frame a UIView or your own in View.Frame a UIViewController .
    2. Do layout in LayoutSubviews A and in UIView ViewDidLayoutSubviews UIViewController .
    3. Don ' t use the screen's size for layout. Instead use of A and in Bounds UIView View.Bounds a UIViewController .

Xamarin.iOS:iOS Layout gotchas (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.