Swift 3 - NSLayoutConstraint CollectionView 附加到另一个视图

Posted

技术标签:

【中文标题】Swift 3 - NSLayoutConstraint CollectionView 附加到另一个视图【英文标题】:Swift 3 - NSLayoutConstraint CollectionView Attaching to another view 【发布时间】:2017-02-10 16:12:23 【问题描述】:

我正在使用此代码Swift 3 - CollectionView data source did not return a valid cell 更新自 TERENCE 答案: 在 viewDidLoad 我放了

collectionView?.translatesAutoresizingMaskIntoConstraints = false
messageInputContainerView.translatesAutoresizingMaskIntoConstraints = false
view.addConstraintsWithFormat(format: "H:|-0-[v0]-0-|", views: messageInputContainerView)
view.addConstraintsWithFormat(format: "H:|-0-[v0]-0-|", views: collectionView!)
let constraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[v1]-0-[v0(48)]", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0":messageInputContainerView, "v1": collectionView!])
constraints[2].identifier = "heightConstraint"
view.addConstraints(constraints)
bottomConstraint = NSLayoutConstraint(item: messageInputContainerView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
view.addConstraint(bottomConstraint!)

使用bottomconstraint我将messageInputContainerView修改为在键盘出现时上升

messageInputContainerView.addConstraintsWithFormat(format: "H:|-8-[v0(30)]-8-[v1][v2(60)]|", views: sendPicButton, inputTextView, sendTextButton)

messageInputContainerView.addConstraintsWithFormat(format: "V:|-6-[v0]|", views: inputTextView)
messageInputContainerView.addConstraintsWithFormat(format: "V:[v0]-6-|", views: sendTextButton)
messageInputContainerView.addConstraintsWithFormat(format: "V:[v0]-14-|", views: sendPicButton)
messageInputContainerView.addConstraintsWithFormat(format: "H:|[v0]|", views: topBorderView)
messageInputContainerView.addConstraintsWithFormat(format: "V:|[v0(0.5)]", views: topBorderView)

在第一个屏幕上,最后一条消息和 messageInputContainerView 之间有空格。如何解决?

在第二个屏幕上 messageInputContainerView 已经在集合视图上方

我正在修改 textViewDidChange 方法中的constraints[2].identifier = "heightConstraint" 以在键盘出现时更改 messageInputContainerView 的位置

如何修复它以附加,因为现在它超过了消息(collectionView)?

【问题讨论】:

您的日志控制台是否显示约束冲突警告消息?好吧,我很困惑,您的收藏视图不应该在您的文本输入视图之上吗?但你的第一个约束是相反的。 无冲突。我编辑了我的问题并添加了视图的所有约束。是的,现在我的 textInputView 位于集合视图之上。我想附加到collectionView。 您是否将集合视图的 translatesAutoresizingMaskIntoConstraints 设置为 false?你的前四个限制在那里......也许你应该尝试“V:[v1] [v0(48)]” 当我设置 collectionView?.translatesAutoresizingMaskIntoConstraints = false 时屏幕变黑,当我将 [v1] 设置为 v0 之前,我修改的 messageInputContainerView 的高度限制不起作用。 我猜你的视图约束没有完成。显然,您应该去检查自动约束如何正常工作。 【参考方案1】:

也许你可以试试这个:容器添加这些 "V:|-0-[collectionView]-0-[inputview(>=48)]-0-|""H:|-0-[collectionView]-0-|""H:|-0-[inputview]-0-|" 除非您的容器也需要它,否则容器不需要将自动调整大小掩码设置为 false。但是集合视图和输入视图都需要将其设置为 false 才能使自动约束起作用。

【讨论】:

view.addConstraintsWithFormat(格式:“H:|-0-[v0]-0-|”,视图:messageInputContainerView)view.addConstraintsWithFormat(格式:“H:|-0-[v0] -0-|",视图:collectionView!) 让约束 = NSLayoutConstraint.constraints(withVisualFormat:"V:|-0-[v1]-0-[v0(>=48)]-0-|",选项:NSLayoutFormatOptions (),指标:无,视图:[“v0”:messageInputContainerView,“v1”:collectionView!])collectionView? 在viewdidappear后检查这两个view的frame以及容器的frame是否布局正确 (0.0, 0.0, 375.0, 0.0) 集合 (46.0, 6.0, 269.0, 100.0) 输入,我看不到集合视图 inputtextView 卡在视图底部 那完全不对,你的容器里还有别的东西吗?,它们的宽度甚至都不一样,如果你设置它是不可能的,我不知道你发生了什么.也许您添加了其他影响最终大小的约束。 在 viewdidload 中设置约束后我收到的帧 (0.0, 0.0, 375.0, 667.0) (0.0, 0.0, 0.0, 0.0)【参考方案2】:

一种方法是将 ChatLogController 从 UICollectionViewController 的子类更改为普通 UIViewController,然后将 CollectionView 添加为子视图,将 MessageInputContainerView 添加为子视图,然后将 Collection 视图的底部固定到顶部输入视图。

这是 ChatLogViewController.swift 类的修改版本...它来自该示例应用程序第 7 步 (https://www.letsbuildthatapp.com/course_video?id=152) 的代码。您应该能够将它几乎原样放入您的项目中......只需将加载行更改为:

    let controller = ChatLogController(collectionViewLayout: layout)

    let controller = MyChatLogController()

另请注意:这没有可变高度文本字段...但是如果您以与您在版本中相同的方式实现它,它应该可以正常工作(请记住,集合的底部视图现在将被“固定”到输入容器视图的顶部)。

编辑:自最初发布以来,我做了一些更改 - 现在支持自动高度调整输入字段。

import UIKit

class MyChatLogController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UITextViewDelegate 

    fileprivate var collectionView: UICollectionView?

    fileprivate let cellId = "cellId"

    var friend: Friend? 
        didSet 
            navigationItem.title = friend?.name

            messages = friend?.messages?.allObjects as? [Message]

            messages = messages?.sorted(by: $0.date!.compare($1.date! as Date) == .orderedAscending)
        
    

    var messages: [Message]?

    let messageInputContainerView: UIView = 
        let view = UIView()
        view.backgroundColor = UIColor.white
        return view
    ()

    let inputTextView: UITextView = 
        let textView = UITextView()
        textView.font = UIFont.systemFont(ofSize: 18)
        return textView
    ()

    let sendButton: UIButton = 
        let button = UIButton(type: .system)
        button.setTitle("Send", for: UIControlState())
        let titleColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 1)
        button.setTitleColor(titleColor, for: UIControlState())
        button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
        return button
    ()

    var bottomConstraint: NSLayoutConstraint?
    var heightConstraint: NSLayoutConstraint?

    override func viewDidLoad() 
        super.viewDidLoad()

        tabBarController?.tabBar.isHidden = true

        let layout = UICollectionViewFlowLayout()

        collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)

        // make sure collectionView creation was successful
        guard  let cv = collectionView else 
            return
        

        cv.delegate = self
        cv.dataSource = self
        cv.translatesAutoresizingMaskIntoConstraints = false

        cv.backgroundColor = UIColor.white

        view.addSubview(cv)

        cv.register(MyChatLogMessageCell.self, forCellWithReuseIdentifier: cellId)

        view.addSubview(messageInputContainerView)
        view.addConstraintsWithFormat("H:|[v0]|", views: messageInputContainerView)
        view.addConstraintsWithFormat("H:|[v0]|", views: cv)
        view.addConstraintsWithFormat("V:|[v0]-(-32)-[v1]", views: cv, messageInputContainerView)

        bottomConstraint = NSLayoutConstraint(item: messageInputContainerView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
        view.addConstraint(bottomConstraint!)

        heightConstraint = NSLayoutConstraint(item: messageInputContainerView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 60)
        view.addConstraint(heightConstraint!)

        setupInputComponents()

        inputTextView.delegate = self
        inputTextView.isScrollEnabled = false

        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    

    override func viewDidAppear(_ animated: Bool) 
        super.viewDidAppear(animated)
        let indexPath = IndexPath(item: self.messages!.count - 1, section: 0)
        self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)
    

    func handleKeyboardNotification(_ notification: Notification) 

        if let userInfo = notification.userInfo 

            let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue

            let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow

            bottomConstraint?.constant = isKeyboardShowing ? -keyboardFrame!.height : 0

            UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: 

                self.view.layoutIfNeeded()

            , completion:  (completed) in

                if isKeyboardShowing 
                    let indexPath = IndexPath(item: self.messages!.count - 1, section: 0)
                    self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)
                

            )
        
    

    func textViewDidChange(_ textView: UITextView)  //Handle the text changes here

        let sz = textView.sizeThatFits(CGSize(width: textView.frame.size.width, height: CGFloat.greatestFiniteMagnitude))

        heightConstraint?.constant = max(60, sz.height)

        UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: 

            self.view.layoutIfNeeded()

        , completion:  (completed) in

            let indexPath = IndexPath(item: self.messages!.count - 1, section: 0)
            self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)

        )

    

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
        inputTextView.endEditing(true)
    

    fileprivate func setupInputComponents() 
        let topBorderView = UIView()
        topBorderView.backgroundColor = UIColor(white: 0.75, alpha: 1.0)

        messageInputContainerView.addSubview(inputTextView)

        messageInputContainerView.addSubview(sendButton)
        messageInputContainerView.addSubview(topBorderView)

        messageInputContainerView.addConstraintsWithFormat("H:|-8-[v0][v1(60)]|", views: inputTextView, sendButton)
        messageInputContainerView.addConstraintsWithFormat("V:|[v0]|", views: inputTextView)

        messageInputContainerView.addConstraintsWithFormat("V:|[v0]|", views: sendButton)

        messageInputContainerView.addConstraintsWithFormat("H:|[v0]|", views: topBorderView)
        messageInputContainerView.addConstraintsWithFormat("V:|[v0(0.5)]", views: topBorderView)
    

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        if let count = messages?.count 
            return count
        
        return 0
    

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! MyChatLogMessageCell

        cell.messageTextView.text = messages?[indexPath.item].text

        if let message = messages?[indexPath.item], let messageText = message.text, let profileImageName = message.friend?.profileImageName 

            cell.profileImageView.image = UIImage(named: profileImageName)

            let size = CGSize(width: 250, height: 1000)
            let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
            let estimatedFrame = NSString(string: messageText).boundingRect(with: size, options: options, attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 18)], context: nil)

            if message.isSender == nil || !message.isSender!.boolValue 
                cell.messageTextView.frame = CGRect(x: 48 + 8, y: 0, width: estimatedFrame.width + 16, height: estimatedFrame.height + 20)

                cell.textBubbleView.frame = CGRect(x: 48 - 10, y: -4, width: estimatedFrame.width + 16 + 8 + 16, height: estimatedFrame.height + 20 + 6)

                cell.profileImageView.isHidden = false

                //                cell.textBubbleView.backgroundColor = UIColor(white: 0.95, alpha: 1)
                cell.bubbleImageView.image = MyChatLogMessageCell.grayBubbleImage
                cell.bubbleImageView.tintColor = UIColor(white: 0.95, alpha: 1)
                cell.messageTextView.textColor = UIColor.black

             else 

                //outgoing sending message

                cell.messageTextView.frame = CGRect(x: view.frame.width - estimatedFrame.width - 16 - 16 - 8, y: 0, width: estimatedFrame.width + 16, height: estimatedFrame.height + 20)

                cell.textBubbleView.frame = CGRect(x: view.frame.width - estimatedFrame.width - 16 - 8 - 16 - 10, y: -4, width: estimatedFrame.width + 16 + 8 + 10, height: estimatedFrame.height + 20 + 6)

                cell.profileImageView.isHidden = true

                //                cell.textBubbleView.backgroundColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 1)
                cell.bubbleImageView.image = MyChatLogMessageCell.blueBubbleImage
                cell.bubbleImageView.tintColor = UIColor(red: 0, green: 137/255, blue: 249/255, alpha: 1)
                cell.messageTextView.textColor = UIColor.white
            

        

        return cell
    

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 

        if let messageText = messages?[indexPath.item].text 
            let size = CGSize(width: 250, height: 1000)
            let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
            let estimatedFrame = NSString(string: messageText).boundingRect(with: size, options: options, attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 18)], context: nil)

            return CGSize(width: view.frame.width, height: estimatedFrame.height + 20)
        

        return CGSize(width: view.frame.width, height: 100)
    

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets 
        return UIEdgeInsetsMake(8, 0, 0, 0)
    



class MyChatLogMessageCell: BaseCell 

    let messageTextView: UITextView = 
        let textView = UITextView()
        textView.font = UIFont.systemFont(ofSize: 18)
        textView.text = "Sample message"
        textView.backgroundColor = UIColor.clear
        return textView
    ()

    let textBubbleView: UIView = 
        let view = UIView()
        view.layer.cornerRadius = 15
        view.layer.masksToBounds = true
        return view
    ()

    let profileImageView: UIImageView = 
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.layer.cornerRadius = 15
        imageView.layer.masksToBounds = true
        return imageView
    ()

    static let grayBubbleImage = UIImage(named: "bubble_gray")!.resizableImage(withCapInsets: UIEdgeInsetsMake(22, 26, 22, 26)).withRenderingMode(.alwaysTemplate)
    static let blueBubbleImage = UIImage(named: "bubble_blue")!.resizableImage(withCapInsets: UIEdgeInsetsMake(22, 26, 22, 26)).withRenderingMode(.alwaysTemplate)

    let bubbleImageView: UIImageView = 
        let imageView = UIImageView()
        imageView.image = MyChatLogMessageCell.grayBubbleImage
        imageView.tintColor = UIColor(white: 0.90, alpha: 1)
        return imageView
    ()

    override func setupViews() 
        super.setupViews()

        addSubview(textBubbleView)
        addSubview(messageTextView)

        addSubview(profileImageView)
        addConstraintsWithFormat("H:|-8-[v0(30)]", views: profileImageView)
        addConstraintsWithFormat("V:[v0(30)]|", views: profileImageView)
        profileImageView.backgroundColor = UIColor.red

        textBubbleView.addSubview(bubbleImageView)
        textBubbleView.addConstraintsWithFormat("H:|[v0]|", views: bubbleImageView)
        textBubbleView.addConstraintsWithFormat("V:|[v0]|", views: bubbleImageView)
    


【讨论】:

编辑注释 - 此示例现在包括自动调整多行文本输入。

以上是关于Swift 3 - NSLayoutConstraint CollectionView 附加到另一个视图的主要内容,如果未能解决你的问题,请参考以下文章

xcode 8.3.3 是不是运行 swift 3.2

swift swift_optional3.swift

swift swift_optional3.swift

swift takasek / CodePiece.swiftのSwift 3.1版

我可以在 Swift 3 项目中使用 Swift 2.3 框架吗?

将 swift 2.3 转换为 swift 3 错误