程序约束折叠单元格视图

Posted

技术标签:

【中文标题】程序约束折叠单元格视图【英文标题】:Programatic constraints collapsing cell view 【发布时间】:2016-07-05 20:19:38 【问题描述】:

我正在开发一个 UITableViewCell,它以 xib 开头,以编程方式添加视图,并具有动态大小的高度。但是,看起来在添加带有约束的程序视图时,它与最初应用于 xib 的自动调整大小约束冲突,并导致问题。请看下面:

让我的细胞出队:

//Table Delegate/Datasource
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 

    var cell:S360SSessionMatchTableCell? = tableView.dequeueReusableCellWithIdentifier(XIBFiles.SESSIONMATCHTABLECELL + String(indexPath.row)) as? S360SSessionMatchTableCell

    if ((cell == nil))
        tableView.registerNib(UINib(nibName: XIBFiles.SESSIONMATCHTABLECELL, bundle: nil), forCellReuseIdentifier: XIBFiles.SESSIONMATCHTABLECELL + String(indexPath.row))
        cell = tableView.dequeueReusableCellWithIdentifier(XIBFiles.SESSIONMATCHTABLECELL + String(indexPath.row)) as? S360SSessionMatchTableCell
    

    cell!.setupEvents(sessionMatches[indexPath.row]["sessions"]! as! [[String:String]])

    return cell!

在自定义 UITableViewCell 中设置事件方法:

func setupEvents(events:[[String:String]])

    //Set up start and end times
    self.startTimeLbl.text = events[0]["startTime"]!
    self.endTimeLbl.text = events[events.count - 1]["endTime"]!

    //Set up events
    var pastEventView:S360SScheduledEventView? = nil
    var pastEvent:[String:String]? = nil
    for (index, event) in events.enumerate()
        var topAnchor:NSLayoutConstraint!

        //Create event view
        let eventView:S360SScheduledEventView = NSBundle.mainBundle().loadNibNamed(XIBFiles.SCHEDULEDEVENTVIEW, owner: self, options: nil)[0] as! S360SScheduledEventView

        //Deal with first view added
        if pastEvent == nil

            //Top anchor setup for first view
            topAnchor = NSLayoutConstraint(item: eventView, attribute: .Top, relatedBy: .Equal, toItem: toLbl, attribute: .Bottom, multiplier: 1, constant: 10)
        
        else

            //Check for a break
            let timeFormatter:NSDateFormatter = NSDateFormatter()
            timeFormatter.dateFormat = "hh:mm a"
            let startTime = timeFormatter.dateFromString(pastEvent!["endTime"]!)
            let endTime = timeFormatter.dateFromString(event["startTime"]!)
            if startTime != endTime 

                //Create break view
                let breakView = NSBundle.mainBundle().loadNibNamed(XIBFiles.SCHEDULEDBREAKVIEW, owner: self, options: nil)[0] as! S360SScheduledBreakView

                //Setup breakview constraints
                let bTopAnchor = NSLayoutConstraint(item: breakView, attribute: .Top, relatedBy: .Equal, toItem: pastEventView, attribute: .Bottom, multiplier: 1, constant: 0)
                let bLeftAnchor = NSLayoutConstraint(item: breakView, attribute: .Leading, relatedBy: .Equal, toItem: self.contentView, attribute: .LeadingMargin, multiplier: 1, constant: 0)
                let bRightAnchor = NSLayoutConstraint(item: breakView, attribute: .Trailing, relatedBy: .Equal, toItem: self.contentView, attribute: .TrailingMargin, multiplier: 1, constant: 0)
                let bHeightAnchor = NSLayoutConstraint(item: breakView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 30)

                //Add break view and constraints
                self.addSubview(breakView)
                self.addConstraints([bTopAnchor, bLeftAnchor, bRightAnchor, bHeightAnchor])

                //Top anchor setup for subsequent view
                topAnchor = NSLayoutConstraint(item: eventView, attribute: .Top, relatedBy: .Equal, toItem: breakView, attribute: .Bottom, multiplier: 1, constant: 0)
            
            else

                //Top anchor setup for subsequent views
                topAnchor = NSLayoutConstraint(item: eventView, attribute: .Top, relatedBy: .Equal, toItem: pastEventView, attribute: .Bottom, multiplier: 1, constant: 0)
            

        

        //Setup other anchors
        let leftAnchor = NSLayoutConstraint(item: eventView, attribute: .Leading, relatedBy: .Equal, toItem: self.contentView, attribute: .LeadingMargin, multiplier: 1, constant: 0)
        let rightAnchor = NSLayoutConstraint(item: eventView, attribute: .Trailing, relatedBy: .Equal, toItem: self.contentView, attribute: .TrailingMargin, multiplier: 1, constant: 0)
        let heightAnchor = NSLayoutConstraint(item: eventView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 60)

        //Setup event view
        eventView.iconImg.image = Images.get_event_image(event["title"]!)
        eventView.titleLbl.text = event["title"]!
        eventView.courtLbl.text = "court" + event["court"]!
        eventView.timeLbl.text = event["startTime"]! + " to " + event["endTime"]!

        //Add event view and constraints
        self.addSubview(eventView)
        self.addConstraints([topAnchor, leftAnchor, rightAnchor, heightAnchor])

        //Prepare for next iteration
        pastEventView = eventView
        pastEvent = event

        //Set up last cell with bottom bound
        if index == events.count - 1 
            let bottomAnchor = NSLayoutConstraint(item: eventView, attribute: .Bottom, relatedBy: .Equal, toItem: self.contentView, attribute: .BottomMargin, multiplier: 1, constant: 0)
            self.addConstraint(bottomAnchor)
        

    

xib 中的约束:

这是我得到的错误(粘贴一次,但每个单元格都会出现):

   Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2016-07-05 15:13:01.654 Shoot360 Scheduler[32779:642808] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
    (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x7fedd85d5590 h=--& v=--& V:[UITableViewCellContentView:0x7fedda431120(44)]>",
    "<NSLayoutConstraint:0x7fedda43a7e0 V:[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda438b20(60)]>",
    "<NSLayoutConstraint:0x7fedda436590 UITableViewCellContentView:0x7fedda431120.topMargin == UILabel:0x7fedda4312a0'10:00 AM'.top - 15>",
    "<NSLayoutConstraint:0x7fedda436630 UILabel:0x7fedda431c00'to'.top == UILabel:0x7fedda4312a0'10:00 AM'.top>",
    "<NSLayoutConstraint:0x7fedda433b60 V:[UILabel:0x7fedda431c00'to']-(10)-[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda438b20]>",
    "<NSLayoutConstraint:0x7fedda445910 V:[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda4443f0(60)]>",
    "<NSLayoutConstraint:0x7fedda448310 V:[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda438b20]-(0)-[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda4443f0]>",
    "<NSLayoutConstraint:0x7fedda449a00 V:[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda448540(60)]>",
    "<NSLayoutConstraint:0x7fedda4479e0 V:[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda4443f0]-(0)-[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda448540]>",
    "<NSLayoutConstraint:0x7fedda44a100 Shoot360_Scheduler.S360SScheduledEventView:0x7fedda448540.bottom == UITableViewCellContentView:0x7fedda431120.bottomMargin>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fedda436590 UITableViewCellContentView:0x7fedda431120.topMargin == UILabel:0x7fedda4312a0'10:00 AM'.top - 15>

正在将行高设置为动态:

override func viewDidLayoutSubviews() 
        super.viewDidLayoutSubviews()

        //Styling
        showAllBtn.layer.cornerRadius = Numbers.CORNERRADIUS

        sessionsTbl.rowHeight = UITableViewAutomaticDimension
        sessionsTbl.estimatedRowHeight = 500
        sessionsTbl.layer.borderColor = Colors.REALLIGHTGREY.CGColor
        sessionsTbl.layer.borderWidth = Numbers.BORDERREG
        sessionsTbl.layer.cornerRadius = Numbers.CORNERRADIUS
        sessionsTbl.separatorStyle = UITableViewCellSeparatorStyle.None
    

 func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat 
        return UITableViewAutomaticDimension
    

【问题讨论】:

问题是您的表格的行高设置为44pt 而不是动态高度。 以编程方式创建的视图会自动采用来自自动调整大小掩码的布局约束,这可能与您添加的任何约束冲突。通过设置 someView.translatesAutoresizingMaskIntoConstraints = false 在所有视图上禁用此功能。 动态调整大小的另一个潜在问题是水平方向上的所有约束都需要保持不变。例如,您的文本标签应该只在一个方向调整大小。使用固定高度的单行标签水平调整大小,或使用固定宽度垂直调整大小的多行标签。 【参考方案1】:

约束

V:[UITableViewCellContentView:0x7fedda431120(44)

表示您的表格中的rowHeight 设置为默认值44pt,而您希望单元格高度是动态的。您必须将rowHeight 设置为UITableViewAutomaticDimension 并设置estimatedRowHeight

另请注意,单元格会被重复使用,因此每次调用 setupEvents 时都必须删除所有以前添加的视图。

还请注意,您不应从 cellForRow 方法内部调用 tableView.registerNib(...)。注册单元格的好地方是viewDidLoad

【讨论】:

目前我的 rowHeight 设置为 UITableViewAutomaticDimension 并且estimatedRowHeight 为 500,远远超过了容纳视图所需的值。我确实将单元重用标识符更改为对每一行都是唯一的(baseString + index),但这并没有改变我遇到的问题。 再挖一点,44 的高度不是默认值或我设置的任何值,它是一个自动调整大小的约束。可能是添加程序视图之前 xib 内容的高度。 @steventnorris Autoresizing 约束意味着框架高度是从表视图(父视图)设置的,这意味着 rowHeight44 或者您从委托方法返回 44 heightForRow. 不是。我在上面添加了行高方法和调用。不,我在哪里将其设置为 44。该约束未在任何地方明确设置。【参考方案2】:

看来你让自己的生活变得比需要的复杂得多。

如果我们查看您目前拥有的:

    一个包含 1 个部分和许多行的表格 1 个细胞亚类 每一行都有任意数量的动态添加的子视图 每个子视图都通过约束相互固定 每个单元格都为每一行显式实例化,未正确重用 重用单元格时不会删除其子视图

这不适合表格视图,这意味着您要编写大量代码并试图将它们全部塞到一个地方。

查看您的数据,最好有以下内容:

    一个包含多个部分的表格,每个部分包含多行 每个会话 1 个部分 每个“事件”1 行 1 个带有日期标签的节标题类 2 个细胞子类,1 个用于事件,1 个用于休息 没有动态添加视图

在这种情况下,您的约束是微不足道的,并且没有在代码中添加任何约束,您只需设置一些数据,其他一切都可以正常工作。该方案还打破了您的顾虑,并将每个不同部分的代码分成逻辑部分。

与其试图解决您现有的问题,不如退后一步,看看您的方法。

【讨论】:

这很公平。鉴于这是一次重写,需要一点时间才能到位,但如果我可以按照我的意愿进行样式设置,这可能是更简洁的方法。现在,约束问题令人沮丧,所以我仍然想知道这种方法的问题在哪里。但是,如果没有人很快提出限制答案,那么赏金就是您的好先生或女士。 (sir :-) ) 你在哪个版本的 ios 上运行?冲突在于自动布局约束设置的单元格高度与您添加的内容之间。但是,它不是表格视图设置的强制高度。有一个错误:***.com/questions/19132908/… 我目前的目标是 iOS 8.2。 检查到那个错误,似乎是一个 iOS 7 错误。我尝试在 init 中设置 autoresize 掩码,只是想看看这是否是问题所在,但似乎没有任何影响。

以上是关于程序约束折叠单元格视图的主要内容,如果未能解决你的问题,请参考以下文章

在 ios 中展开和折叠表格视图单元格

使用显示/减少按钮展开/折叠表格视图单元格

展开和折叠表格视图单元格

展开和折叠表格视图中的单元格

展开和折叠所有表格视图单元格

具有动态调整单元格的可折叠表格视图