Search code examples
iosswiftuiviewnslayoutconstrainttimeline

Position multiple UIViews on a visual timeline with NSLayoutConstraint in Swift


I need to figure out how to position 3 - 5 UIViews (basically a circular drawn colored dot) on a horizontal line which reach from side to side on the screen with constraints. Or is it better to go without constraints?

The line should be based on a day (24 hours)

E.g - If I have 3 NSDates which represents three times (7:00, 12:00, and 22:00) I want them to be positioned correctly on the timeline:

|------7----12--------22-|

EDIT. With a little more thinking I now see that I will not able to achieve this. The thing is that each dot on the timeline is, say a "todo"-time for a specific task. But there could be several tasks. And each task is a table cell. So there will be multiple timelines:

|------7----12--------22-|
|----5------12------20---|
|--------10-12-------21--|
|------7----12-----18----|

And because each dot has a label above it I will not be able to position the dots according to time. Because in vertical orientation theres not room for 24 positions. And therefor dots with the same time (in two different timelines) will not be align correctly.

How I solved it for now is by splitting each tasks times into two arrays, one with all times before 12.00 and another one with all times after 12.00.

I then take the first array starting with the earliest time from the left (constraint leading to superview) and the rest right to the view before and so on.

I then take the second array and starts from the end (latest time) and adding them to the right (constraint trailing to superview) and the rest left to the view before and so on.

This is the result: enter image description here


Solution

  • I did something similar. I return a UIImage for each tableview cell view with numbers laid out horizontally according to an NSDate. You'll just have to turn the NSDate into a CGFloat value. This extension gives me a decimal value representing hour and minute from an NSDate.

    extension NSDate {
        var hourInDecimal: CGFloat {
            get {
                let userCalendar = NSCalendar.currentCalendar()
    
                let timeNowInDecimal = CGFloat(userCalendar.component(.Hour, fromDate: self)) + CGFloat(userCalendar.component(.Minute, fromDate: self))/60
                return timeNowInDecimal
            }
        }
    }
    

    Fitting them horizontally was tough if it was 24 hours so I only go from the first hour to the latest hour with data. 7am -> 10pm did fit.

    Then to get the distance from the left edge (for a constraint)...

    let oneHourWidth = containerView.width/(lateHour - earlyHour)
    let distanceToLeftEdge = oneHourWidth * (eventTime.hourInDecimal - earlyHour)
    

    Note that some of the above changes for Swift 3.