0

I still don't understand how I should get the updated frame of a UIView, when using autolayout.

Let's say I:

  1. Setup some views using autolayout.
  2. Make an HTTP request to get some data.
  3. When I get the data, I want to draw it but I need the updated frame size of a view.

How should I go about it?

My guess, but it's wrong:

layoutMyViews()
makeHttpRequest(callback)

func callback(data: MyData) {
    drawData(data, into:dataView)
}

func drawData(data: MyData, into view:UIView) {
    let size = view.frame.size
    // Here I want to draw data into view, depending on size, but it may be (0,0)
}
Ferran Maylinch
  • 10,919
  • 16
  • 85
  • 100
  • what is drawData? Unless you're reloading an existing API class, like a tableview, you'll need to manually update your view to accommodate the data and at that point you should know it's size. – Fred Faust Mar 16 '16 at 01:25
  • `drawData()` is where I want to add something into `view` (I added an example above) but I need the `view.frame.size` to be set. How can I do it? – Ferran Maylinch Mar 16 '16 at 01:36
  • If you use auto layout it's already set – Fred Faust Mar 16 '16 at 01:39
  • With auto-layout you set constraints, but frames are calculated afterwards. Size may be 0 if frames are not calculated yet, I guess. – Ferran Maylinch Mar 16 '16 at 01:42
  • Once the view is loaded you can access it's frame. So if you call your method from viewDidLoad or after in context of the lifecycle of a UIViewController's views you can access it's frame properties by calling view.frame.size. – Fred Faust Mar 16 '16 at 01:46
  • No, no, when using autolayout you can't get the frame of a view in viewDidLoad because frames are not set yet. – Ferran Maylinch Mar 16 '16 at 01:55
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/106413/discussion-between-ferran-maylinch-and-thefredelement). – Ferran Maylinch Mar 16 '16 at 02:00

2 Answers2

0

Here's just an example UIViewController - you can start your call in view did load and block the ui or at least indicate activity and then when that call completes you can update the UI based off of whatever current view properties you'd like.

class ViewController: UIViewController {

// You can start accessing view properties from the storyboard here.
override func viewDidLoad() {
    super.viewDidLoad()
    print(self.view.frame) // Outputs the frame of the view controller's view.
    // Do any additional setup after loading the view, typically from a nib.
}

override func viewDidLayoutSubviews() {
}

override func viewWillAppear(animated: Bool) {
}

override func viewDidAppear(animated: Bool) {
}
}
Fred Faust
  • 6,696
  • 4
  • 32
  • 55
  • I want the frame of a view, not the frame of the view controller. Anyway, even the frame of a view controller may be wrong at that stage. It's offtopic but you can check this: http://stackoverflow.com/a/16796881/1121497 – Ferran Maylinch Mar 16 '16 at 01:57
  • The frame is dependent upon constraints, after the view is loaded the frame is loaded. If you're using child views on the view controller on a storyboard that is also true for them. – Fred Faust Mar 16 '16 at 01:58
  • That's a good link and if you update your view in those methods you should of course check for their new values if you need them. Either way, just call your task when the view is made and update as necessary. – Fred Faust Mar 16 '16 at 02:00
0

This is more or less what I'm doing right now. If anyone can suggest a better alternative please post or comment about it.

As you will see, in drawDataIfPossible() I check that both the data is set and the frame size is set. I check both because drawDataIfPossible() is called from these two functions:

  1. drawData(data: MyData), which might be called before the frame size is set
  2. layoutSubviews(), which might be called before the data is set.

.

class Controller : UIViewController {

    // Here I will draw some data that comes from server
    var dataView: MyDataView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Here I add the views using autolayout, including dataView
        layoutMyViews()

        // Here I make the http request to get data
        makeHttpRequest(callback)
    }

    /** When the data comes, I tell the dataView to draw itself */
    func callback(data: MyData) {
        dataView.drawData(data)
    }

    //...
}

class MyDataView : UIView {

    var data: MyData!

    /** Draws the data in the view */
    func drawData(data: MyData) {
        self.data = data
        drawDataIfPossible()
    }

    func drawDataIfPossible() {
        // Here I check I have the data and that the frame size is set
        if data != nil && frame.size.width > 0 {
            drawData()
        }
    }

    /** Draws data in the view (needs the frame size) */
    func drawData {
        // ...
    }

    override func layoutSubviews()
    {
        super.layoutSubviews()
        layoutIfNeeded() // seems to be necessary for the frame size to be set
        drawDataIfPossible()
    }
}
Ferran Maylinch
  • 10,919
  • 16
  • 85
  • 100