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 附加到另一个视图的主要内容,如果未能解决你的问题,请参考以下文章
swift takasek / CodePiece.swiftのSwift 3.1版