0

I have a UILabel (named contentLbl) that will change dynamically within a UIScrollView. Above it is another UILabel (named contentTitleLbl) that is a title and does not contain dynamic content. To successfully scroll I know that I must at least set the ContentSize of the UIScrollView to that of the content view's. I've also seen that the Frame should be set. I have tried both but it still does not scroll.

In my example code contentView contains both the contentLbl and contentTitleLbl. scrollView is the reference to the UIScrollView that I'm currently trying to use.

Here is my current code:

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();
    //contentLbl is to have dynamic text, and currently stores a large lorem ipsum paragraph
    //contentTitleLbl is the title and is not dynamic
    this.contentLbl.SizeToFit();
    this.scrollView.ContentSize = new SizeF(this.contentView.Frame.Width, this.contentLbl.Frame.Size.Height + this.contentTitleLbl.Frame.Size.Height);
    this.scrollView.Frame = new RectangleF(this.scrollView.Frame.X, this.scrollView.Frame.Y, this.scrollView.ContentSize.Width, this.scrollView.ContentSize.Height);
    this.scrollView.AddSubview(this.contentView);
}

Any help would be greatly appreciated!

barinelg
  • 23
  • 2
  • 6

2 Answers2

3

Quick question: Are you intending for the title to scroll with your text? In this solution, I've done that, as this is my interpretation. I'll adjust it, if you'd like, for the title not to scroll. I recreated your sample by making this hierarchy in XCode

  • UIView (main view of the view controller)
    • UIView (contentView)
      • UILabel (contentTitleLbl)
        • Frame = (20,15,160,21)
      • UILabel (contentLbl)
        • Lines = 100
        • Frame = (20,43,160,190)
        • Text = "Lorem ipsum..." (long paragraph)
    • UIScrollView (scrollView)
      • Frame = (20,20,140,140)

I set the background colors of all the items to different values so that I could see the dimensions of all the components.

First, I needed to make sure all the auto-resizing of the labels was turned off. You can do this in XCode, or programmatically:

contentLbl.AutoresizingMask = UIViewAutoresizing.None;
contentTitleLbl.AutoresizingMask = UIViewAutoresizing.None;

I'd prefer to place the contentView inside the scrollView via XCode, but it looks like you had them as siblings under the main View. So we reparent the contentView to place it inside the scrollView

scrollView.AddSubview( contentView );

Then make the title the same width as the scrollView, positioning it at the origin and leaving the height as designed in XCode

var f = contentTitleLbl.Frame;
f.X = 0;
f.Y = 0;
f.Width = scrollView.Frame.Width;
contentTitleLbl.Frame = f;

Then the first major issue: resizing a UILabel to fit your contents dynamically. SizeToFit resizes the label to a single line of text (or at least, did for me). What you really want is NSString.sizeWithFont which appears in MonoTouch as UIView.StringSize. Be careful, as some of these return the size for a single line of text. Here I've constrained the width to the width of the scroll view, but left the height unbounded by using float.MaxValue.

SizeF sz = this.View.StringSize(
    contentLbl.Text,
    contentLbl.Font,
    new SizeF( scrollView.Frame.Width, float.MaxValue),
    UILineBreakMode.WordWrap );

Then resize and position the label just underneath the title:

f = contentLbl.Frame;
f.X = 0;
f.Y = contentTitleLbl.Frame.Bottom; // nudge the contentLbl right up against the title
f.Width = sz.Width;    // size matches the measured values from above
f.Height = sz.Height;
contentLbl.Frame = f;

Then adjust the size of the contentView to hold the title and the content. This view will be placed at the origin in the scroll view. The size is determined dynamically by the code above. I've added a small margin at the bottom so I can tell that the whole content has been rendered

f = contentView.Frame;
f.X = 0;
f.Y = 0;
f.Width = scrollView.Frame.Width;
f.Height = contentLbl.Frame.Bottom + 20;
contentView.Frame = f;

Then set the ContentSize of the scroll view to the size of the contentView. If the size is larger than the scroll view's size -- which should be the case with the Lorem Ipsum text -- you'll get your scrolling.

scrollView.ContentSize = contentView.Frame.Size;

Cheers.

[Edit: fixed typo for 'width' vs. 'height']

[Edit: simplest scroll view]

Here's the simplest scroll view I could come up with. Dragging up and down on the red area should show the scroll bar. When you get to the bottom, the green background of the scroll view should show.

var v =  new UIView( new RectangleF(0,0,200,1000));
v.BackgroundColor = UIColor.Red;

var sv = new UIScrollView( new RectangleF(20,20,200,300));
sv.BackgroundColor = UIColor.Green;
sv.ContentSize = v.Frame.Size;

View.AddSubview(sv);
sv.AddSubview(v);

[Edit: an even simpler scroll view] I created a scroll view in XCode and simply set the background color and the content size to be larger than the scroll view's size. When dragging on the blue scroll view, the scroll bars should appear.

designerScrollView.BackgroundColor = UIColor.Blue;
designerScrollView.ContentSize = new SizeF(1000,1000);
Community
  • 1
  • 1
cod3monk3y
  • 9,508
  • 6
  • 39
  • 54
  • also, see: [Sizing a UILabel to fit](http://stackoverflow.com/questions/406212/sizing-a-uilabel-to-fit?rq=1) – cod3monk3y Sep 18 '12 at 23:32
  • First off, thanks for the great post! It has helped me out with a lot of conceptual problems around how this works, and that alone has been greatly helpful. I do have one question for clarification: "If the size is larger than the scroll view's size -- which should be the case with the Lorem Ipsum text -- you'll get your scrolling." Do you mean that if the scrollView.Frame.Height < scrollView.ContentSize.Height I'll get my scrolling? – barinelg Sep 19 '12 at 00:31
  • Yes, exactly right. To illustrate, try something very simple like: `var v = new UIView(new RectangleF(0,0,1000,1000)); v.SetBackgroundColor(UIColor.Red); scrollView.AddSubview(v); scrollView.ContentSize = v.Frame.Size;` When you drag the scroll view around you should see the scroll bars. (haven't tested that though). – cod3monk3y Sep 19 '12 at 00:39
  • Gotcha. I seem to be really close, but it's still not scrolling. As of now, scrollView's frame width is 360 and height is 200 (forcing it to be small right now). The contentSize width is also 360, but the height is 489. So my frame height is less than the content size height. To make sure, I added the line Console.WriteLine("is less than: " + (scrollView.Frame.Height < scrollView.ContentSize.Height)); which displayed true. Are there other factors that could prevent scrolling? – barinelg Sep 19 '12 at 01:09
  • I've added some code for a simple scroll view test. Maybe this will be more helpful than my excruciating verbosity from earlier? Can you try that out and see if it works for you? The only other thing I can think of is the "auto resize subviews" setting in the designer. – cod3monk3y Sep 19 '12 at 01:30
  • And the smallest sample yet (see edits to original answer). Are you able to get scrolling motion from the minimal scrolling example I just added? – cod3monk3y Sep 19 '12 at 01:38
  • Something fishy is definitely going on. All the simple scroll view examples didn't really do anything; no scroll bars and only a red box. I'll check all the auto-resizing to see if that's affecting it. – barinelg Sep 19 '12 at 02:20
  • I deleted the xib files and started with new ones. I tried the most simplistic scrollview and now I'm seeing scroll bars. Going to slowly re-implement all pieces to see if it's working now. – barinelg Sep 19 '12 at 02:40
  • For some reason deleting the files for the view I was working on and creating them again worked. I guess something went weird in MonoDevelop. Either way, it's up and working now. Thanks for the post! It really did help me with a lot of conceptual aspects, which let me create my own method of managing the contents and their positions. One side note, `SizeToFits` works, but I think you have to have the lines of that label set to 0. Thanks again! – barinelg Sep 19 '12 at 16:17
  • You're welcome. Sorry we didn't figure out the *actual* problem. Glad to hear you're up and running. – cod3monk3y Sep 19 '12 at 16:24
0

Just add this method extension. Call it when you done with adding subview. This method will calculate new content Height and assign to the scroll view.

public static void RecalculateContentSize (this UIScrollView scrollView)
        {
            float sizeOfContent = 0;
            float height = 0;
            for (int i = 0; i < scrollView.Subviews.Length; i++) {
                UIView view = scrollView.Subviews [i];
                height = view.Frame.Location.Y + view.Frame.Size.Height;
                if (height > sizeOfContent)
                    sizeOfContent = height;
            }

            // Set content size for scroll view
            scrollView.ContentSize = new System.Drawing.SizeF (scrollView.Frame.Size.Width, sizeOfContent);
        }
Tola Ch.
  • 188
  • 1
  • 1
  • 7