简体   繁体   中英

AutoLayout divide width of view programatically

I have been changing some code over from using frames to using auto layout and I've hit a small snag

for background I have been building a calendar, my issue is in the calendar's Day view which I have been trying to make so that it replicates apple's calendar.

This app has been sat in my hard drive since 2018 so is somewhat out of date, but in 2018 it did the following:

Displayed Events by creating an EventContainerView and placing it onto of a TableView (not as a cell).

events which existed at the same time would shorten their width to prevent the container views from overlapping.

this last part is where my problem lies.

I did this by taking the width of the tableView, dividing it by the number of overlapping events and then multiplying it by a shiftBy variable to shift the event to the right

for the life of me I cannot see a way to implement this with autoLayout.

before AutoLayout

func drawEvent(_ event:Event, overlaps:Int, shiftBy:Int) -> EventContainerView{
....
....


let eventWidth = (tableView.frame.width - 30) / CGFloat(overlaps)
        
        let shift = eventWidth * CGFloat(shiftBy)
        var frame: CGRect
     if(shiftBy > 0){
         frame = CGRect(x: CGFloat(30 + (5*shiftBy)) + shift, y: startPoint, width: eventWidth, height: endpoint)
         
     }else{
         frame = CGRect(x: CGFloat(30) + shift, y: startPoint, width: eventWidth, height: endpoint)
         
     }

After AutoLayout

func drawEvent(_ event:Event, overlaps:Int, shiftBy:Int) -> EventContainerView{
....
....

let eventView = EventContainerView(forEvent: event, today: self)
           eventView.translatesAutoresizingMaskIntoConstraints = false
 var left: NSLayoutConstraint = eventView.leftAnchor.constraint(equalTo: tableView.leftAnchor, constant: +20)
   
    if(shiftBy>0){
        left = ????
    }
    
    tableView.addSubview(eventView)
        let layout = [eventView.topAnchor.constraint(equalTo: tableView.topAnchor, constant: startPoint),
        left,
        eventView.heightAnchor.constraint(equalToConstant: endpoint),
        eventView.widthAnchor.constraint(equalTo: tableView.widthAnchor, multiplier: 1/CGFloat(overlaps), constant: -30)]
    NSLayoutConstraint.activate(layout)
        return eventView

Can anybody offer up any suggestions as to how I might implement this piece of code?

Thanks

EDIT

this is what I'm trying to achieve (I took this on macOS's calendar but it still clearly show's how I want calendar events to appear)

重叠事件彼此相邻堆叠

and this is what I have (The darker yellow between 15 and 16 is another event stacked underneath the "Test Everything" event. I need one of them to shift over (along with any other events that may or may not be added to the same time frame)

应用程序的事件堆叠在彼此之上,而不是相邻

Many different ways to approach this, but to make it close to what you were doing...

You can create equal width views that span a width, using a fixed spacing between them and a specific "padding" on each side, by:

  • constrain the Leading of the First view to the Leading of the superview + padding
  • constrain the Leading of each Successive view to the Trailing of the Previous view + spacing
  • constrain the Trailing of the Last view to the Trailing of the superview - padding

Then, constrain the Width of each view equalTo the width of the previous view.

Here's a quick example:

class AndyViewController: UIViewController {

    let numEvents = 1
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // a var to track the previously added view
        var prevView: UIView!
        
        var startPoint: CGFloat = 100
        
        // create a couple eventViews
        for i in 0..<numEvents {
            let thisView = eventView()
            view.addSubview(thisView)
            
            // set top constraint
            thisView.topAnchor.constraint(equalTo: view.topAnchor, constant: startPoint).isActive = true
            
            // let's pretend each view is 20-pts "later"
            startPoint += 20.0
            
            // for now, make them all 50-pts tall
            thisView.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
            
            // if it's the first view
            if i == 0 {
                // constrain its leading to view leading + 30
                thisView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30.0).isActive = true
            } else {
                // make sure prevView has been set
                guard let pv = prevView else {
                    fatalError("Did something wrong!!!")
                }
                // start this view at the right-end of the previous view + 5
                thisView.leadingAnchor.constraint(equalTo: prevView.trailingAnchor, constant: 5.0).isActive = true
                // make this view width equal to previous view width
                thisView.widthAnchor.constraint(equalTo: pv.widthAnchor).isActive = true
            }
            
            // if it's the last view
            if i == numEvents - 1 {
                // constrain its trailing to view trailing -30
                thisView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
            }
            
            // set prevView to thisView
            prevView = thisView
        }
        
    }

    func eventView() -> UIView {
        let v = UIView()
        v.backgroundColor = UIColor.yellow.withAlphaComponent(0.8)
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }
    
}

The result, using 1, 2, 3 or 4 "events":

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

You should be able to adapt that to your needs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM