Swift 3 - NSLayoutConstraint CollectionView Attaching to another view - ios

I am using this code Swift 3 - CollectionView data source did not return a valid cell
UPDATED FROM TERENCE ANSWER:
In viewDidLoad I put
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!)
with bottomconstraint I modify the messageInputContainerView to goes up when the keyboard appear
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)
On the first screen I have space between last message and messageInputContainerView. How to fix it?
On the second screen messageInputContainerView is already over the collection view
I am modifying constraints[2].identifier = "heightConstraint" in textViewDidChange method to change the position of the messageInputContainerView when keyboard appear
How to fix it to be attached, because now its over the mesagess(collectionView) ?

Maybe you can try this: container add these "V:|-0-[collectionView]-0-[inputview(>=48)]-0-|" and "H:|-0-[collectionView]-0-|" with "H:|-0-[inputview]-0-|"
where container does not necessary set auto resizing mask false unless your container also need it. But both collection view and input view need to set it false to make auto constraints work.

One approach would be to change the ChatLogController from a subclass of UICollectionViewController to a plain UIViewController, and then add the CollectionView as a subview, add the MessageInputContainerView as a subview, and then pin the bottom of the Collection view to the top of the Input view.
Here is a modified version of the ChatLogViewController.swift class... it's from the code at Step 7 (https://www.letsbuildthatapp.com/course_video?id=152) of that sample app. You should be able to drop it into your project pretty much as-is... just change the loading line from:
let controller = ChatLogController(collectionViewLayout: layout)
to
let controller = MyChatLogController()
Also note: this does not have the variable-height textfield... but if you implement it in the same way as you did in your version, it should work just fine (remember, the bottom of the Collection view will now be "pinned" to the top of the Input container view).
Edit: I made a few changes since my original post - this now supports the auto-height-adjusting input field.
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)
}
}

Related

Swift - access instance of UIView from another function

In my project the user can create a UIView ("CustomWishlistView") like this:
func createCustomWishlistView() -> CustomWishlistView {
let v = CustomWishlistView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .darkGray
v.layer.cornerRadius = 30
return v
}
#IBAction func createListButtonTapped(_ sender: Any) {
// "Liste erstellen" button was tapped
self.appDidEnterBackgroundHandler()
if let txt = listNameTextfield.text {
self.newListTextfield.resignFirstResponder()
// append user-entered text to the data array
self.theData.append(txt)
self.imageData.append(self.image!)
let theCustomWishlistView = createCustomWishlistView()
self.view.addSubview(theCustomWishlistView)
// constrain CustomWishlistView
theCustomWishlistView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 120.0).isActive = true
theCustomWishlistView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
theCustomWishlistView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 30.0).isActive = true
theCustomWishlistView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -30.0).isActive = true
theCustomWishlistView.wishlistImage.image = self.image
theCustomWishlistView.wishlistLabel.text = txt
theCustomWishlistView.transform = CGAffineTransform(translationX: 0, y: 1000)
self.view.bringSubviewToFront(containerView)
// reload the collection view
theCollectionView.reloadData()
theCollectionView.performBatchUpdates(nil, completion: {
(result) in
// scroll to make newly added row visible (if needed)
let i = self.theCollectionView.numberOfItems(inSection: 0) - 1
let idx = IndexPath(item: i, section: 0)
self.theCollectionView.scrollToItem(at: idx, at: .bottom, animated: true)
// close (hide) the "New List" view
self.closeButtonTappedNewList(nil)
})
}
}
I transform that view so it is not visible at first. It should appear after the user clicks on a cell inside a UICollectionView. My code for this (inside cellForItemAt) looks like this:
if indexPath.item <= theData.count {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ContentCell", for: indexPath) as! ContentCell
cell.testLabel.text = theData[indexPath.item - 1]
cell.buttonView.setImage(imageData[indexPath.item - 1], for: .normal)
cell.customWishlistTapCallback = {
// let CustomWishlistView appear
// UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn, animations: {
// theCustomWishlistView.transform = CGAffineTransform(translationX: 0, y: 0)
// })
// let welcomeText disappear
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut, animations: {
self.welcomeTextLabel.transform = CGAffineTransform(translationX: 0, y: 0)
})
}
return cell
}
My problem is that I can not let my customWishlistView appear because I can not access it. Is there a way to fix this problem? :)
You need to make theCustomWishlistView an instance variable like below. You don't show the entire class code, so my solution is as complete as I can make it without seeing the rest of your code
class CustomViewController: UIViewController, UICollectionViewDataSource {
var theCustomWishlistView: UIView?
func createCustomWishlistView() -> CustomWishlistView {
let v = CustomWishlistView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .darkGray
v.layer.cornerRadius = 30
return v
}
#IBAction func createListButtonTapped(_ sender: Any) {
// "Liste erstellen" button was tapped
self.appDidEnterBackgroundHandler()
if let txt = listNameTextfield.text {
self.newListTextfield.resignFirstResponder()
// append user-entered text to the data array
self.theData.append(txt)
self.imageData.append(self.image!)
let wishlistView = createCustomWishlistView()
self.view.addSubview(wishlistView)
// constrain wishlistView
wishlistView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 120.0).isActive = true
wishlistView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
wishlistView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 30.0).isActive = true
wishlistView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -30.0).isActive = true
wishlistView.wishlistImage.image = self.image
wishlistView.wishlistLabel.text = txt
wishlistView.transform = CGAffineTransform(translationX: 0, y: 1000)
// retain wishlist view in instance variable
self.theCustomWishlistView = wishlistView
self.view.bringSubviewToFront(containerView)
// reload the collection view
theCollectionView.reloadData()
theCollectionView.performBatchUpdates(nil, completion: {
(result) in
// scroll to make newly added row visible (if needed)
guard let i = self.theCollectionView.numberOfItems(inSection: 0) else { return }
let idx = IndexPath(item: i - 1, section: 0)
self.theCollectionView.scrollToItem(at: idx, at: .bottom, animated: true)
// close (hide) the "New List" view
self.closeButtonTappedNewList(nil)
})
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return theData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let _cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ContentCell", for: indexPath)
guard let cell = _cell as? ContentCell else { return _cell }
cell.testLabel.text = theData[indexPath.item - 1]
cell.buttonView.setImage(imageData[indexPath.item - 1], for: .normal)
cell.customWishlistTapCallback = {
// let CustomWishlistView appear
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn, animations: {
self.theCustomWishlistView?.transform = CGAffineTransform(translationX: 0, y: 0)
})
// let welcomeText disappear
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut, animations: {
self.welcomeTextLabel.transform = CGAffineTransform(translationX: 0, y: 0)
})
}
return cell
}
}

Issue updating constraint in an custom UICollectionView cell

I have an issue updating the with anchor of a constraints inside my collectionView cell. I have two views representing bars as percentages of e.g. the total amount of goals scored for the home and for the away team see the following picture for clarification.
When I look into the statistics the for the first time, everything works fine and I get the right print statements in my console (e.g. Width HomeCell: 139.0 and Width AwayCell: 27.0 for index 0). When I go back to my pitchViewController and add some more goals, I get an error and both bars disappear.
I already tried to call layoutIfNeeded() or setNeedsLayout() on both bar views. But it didn't worked so far.
Here is my console output, relevant code underneath:
Width HomeCell: 83.0
Width AwayCell: 83.0
[LayoutConstraints] 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.
(
"<NSLayoutConstraint:0x604000287440 UIView:0x7f870f461d50.width == 1 (active)>",
"<NSLayoutConstraint:0x60c00009a810 UIView:0x7f870f461d50.width == 83 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60c00009a810 UIView:0x7f870f461d50.width == 83 (active)>
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.
My Custom Cell
import UIKit
class GameStatisticCell: BaseCell {
let statisticTitleLabel: UILabel = {
let label = UILabel()
label.text = "Schüsse aufs Tor"
label.textColor = ColorCodes.darkGray
label.textAlignment = .center
label.font = UIFont(name: "HelveticaNeue-Medium", size: 12)
return label
}()
let homeTeamStatistic: UILabel = {
let label = UILabel()
label.text = String(12)
label.textColor = ColorCodes.darkGray
label.textAlignment = .right
label.font = UIFont(name: "HelveticaNeue-CondensedBold", size: 20)
return label
}()
let awayTeamStatistic: UILabel = {
let label = UILabel()
label.text = String(2)
label.textColor = ColorCodes.darkGray
label.textAlignment = .left
label.font = UIFont(name: "HelveticaNeue-CondensedBold", size: 20)
return label
}()
var homeTeamStatisticBar: UIView = {
let view = UIView()
view.backgroundColor = UIColor.lightGray
return view
}()
var awayTeamStatisticBar: UIView = {
let view = UIView()
view.backgroundColor = UIColor.darkGray
return view
}()
let barCenter = pitchWidth! / 2
var barWidthHome: CGFloat = 10.0
var barWidthAway: CGFloat = 10.0
var statistic: Statistic? {
didSet {
guard let statisticName = statistic?.name else { return }
guard let homeValue = statistic?.home else { return }
guard let awayValue = statistic?.away else { return }
guard let homeBar = statistic?.homeBar else { return }
guard let awayBar = statistic?.awayBar else { return }
statisticTitleLabel.text = statisticName
homeTeamStatistic.text = homeValue.description
awayTeamStatistic.text = awayValue.description
barWidthHome = homeBar
barWidthAway = awayBar
print("Width HomeCell: \(barWidthHome)")
print("Width AwayCell: \(barWidthAway)")
homeTeamStatisticBar.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: barCenter, width: barWidthHome, height: 16)
awayTeamStatisticBar.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 4, paddingLeft: barCenter, paddingBottom: 0, paddingRight: 0, width: barWidthAway, height: 16)
self.homeTeamStatisticBar.layoutIfNeeded()
self.awayTeamStatisticBar.layoutIfNeeded()
}
}
override func setupCell() {
super.setupCell()
self.setNeedsLayout()
backgroundColor = .white
addSubview(statisticTitleLabel)
addSubview(homeTeamStatistic)
addSubview(awayTeamStatistic)
addSubview(homeTeamStatisticBar)
addSubview(awayTeamStatisticBar)
statisticTitleLabel.anchor(top: nil, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 4, paddingRight: 0, width: 0, height: 0)
addConstraint(NSLayoutConstraint(item: statisticTitleLabel, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0))
homeTeamStatistic.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 20, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
awayTeamStatistic.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
}
}
extension UIView {
func anchor(top: NSLayoutYAxisAnchor?, left: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, right: NSLayoutXAxisAnchor?, paddingTop: CGFloat, paddingLeft: CGFloat, paddingBottom: CGFloat, paddingRight: CGFloat, width: CGFloat, height: CGFloat) {
self.translatesAutoresizingMaskIntoConstraints = false
if let top = top {
self.topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
}
if let left = left {
self.leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
}
if let bottom = bottom {
self.bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
}
if let right = right {
self.rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
}
if width != 0 {
self.widthAnchor.constraint(equalToConstant: width).isActive = true
}
if height != 0 {
self.heightAnchor.constraint(equalToConstant: height).isActive = true
}
}
}
My Statistics CollectionView
import UIKit
class GameStatistics: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let blackView = UIView()
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.white
return cv
}()
let cellId = "cellId"
let sectionHeader = "sectionHeader"
let sectionFooter = "sectionFooter"
let cellHeight: CGFloat = 40
let headerHeight: CGFloat = 80
let footerHeight: CGFloat = 50
let cellSpacing: CGFloat = 0
var statistics = [Statistic(name: "Tore", home: 0, away: 0, homeBar: 1.0, awayBar: 1.0), //Goals
Statistic(name: "Schüsse aufs Tor", home: 0, away: 0, homeBar: 1.0, awayBar: 1.0), //Shots on Target
Statistic(name: "Schüsse neben das Tor", home: 0, away: 0, homeBar: 1.0, awayBar: 1.0), //Shots off Target
Statistic(name: "Freistöße", home: 0, away: 0, homeBar: 1.0, awayBar: 1.0), //Free Kicks
Statistic(name: "Eckbälle", home: 0, away: 0, homeBar: 1.0, awayBar: 1.0), //Corner Kicks
Statistic(name: "Fouls", home: 0, away: 0, homeBar: 1.0, awayBar: 1.0), //Fouls
Statistic(name: "Abseits / Mittellinie", home: 0, away: 0, homeBar: 1.0, awayBar: 1.0), //Offside / Centerline
Statistic(name: "Strafen", home: 0, away: 0, homeBar: 1.0, awayBar: 1.0)] //Cautions
var barWidthHome: CGFloat = 1.0
var barWidthAway: CGFloat = 1.0
var statisticValueSum: Int = 1
var valueHomeTeam: Int = 1
var valueAwayTeam: Int = 1
func updateGoals() {
valueHomeTeam = UserDefaults.standard.integer(forKey: "homegoals")
valueAwayTeam = UserDefaults.standard.integer(forKey: "awaygoals")
statisticValueSum = valueHomeTeam + valueAwayTeam
barWidthHome = CGFloat((Int(pitchWidth! / 2) - 40) * valueHomeTeam / statisticValueSum)
barWidthAway = CGFloat((Int(pitchWidth! / 2) - 40) * valueAwayTeam / statisticValueSum)
statistics[0] = Statistic(name: "Tore", home: valueHomeTeam, away: valueAwayTeam, homeBar: barWidthHome, awayBar: barWidthAway)
}
func showStatistics() {
if let window = UIApplication.shared.keyWindow {
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
window.addSubview(blackView)
window.addSubview(collectionView)
// Dynamic Height of Collection View
let value: CGFloat = CGFloat(statistics.count)
let height: CGFloat = value * cellHeight + (value - 1) * cellSpacing + headerHeight + footerHeight
let y = window.frame.height - height
blackView.frame = window.frame
collectionView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: height)
blackView.alpha = 0
UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.blackView.alpha = 1
self.collectionView.frame = CGRect(x: 0, y: y, width: self.collectionView.frame.width, height: self.collectionView.frame.height)
}, completion: nil)
}
}
#objc func handleDismiss() {
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.collectionView.frame = CGRect(x: 0, y: window.frame.height, width: self.collectionView.frame.width, height: self.collectionView.frame.height)
}
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return statistics.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! GameStatisticCell
cell.statistic = statistics[indexPath.item]
cell.layoutIfNeeded()
//dump(statistics)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: cellHeight)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return cellSpacing
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return cellSpacing
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let supplementaryView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: sectionHeader, for: indexPath)
return supplementaryView
case UICollectionElementKindSectionFooter:
let supplementaryView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: sectionFooter, for: indexPath)
return supplementaryView
default:
fatalError("Unexpected element kind")
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: headerHeight)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: footerHeight)
}
override init() {
super.init()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(GameStatisticCell.self, forCellWithReuseIdentifier: cellId)
collectionView.register(GameStatisticHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: sectionHeader)
collectionView.register(GameStatisticFooter.self, forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: sectionFooter)
}
}
You should not add new constraints as those will obviously conflict with already existing ones (different padding). You should rather hold a reference to the dynamic constraints created in the setupCell and only update them in the statistic didSet {}
Solved it with two helper functions where I can set the constraints and update them in my statistic didSet. Took some time, but finally the above comment took me on the right track. Thank you.
func updateHomeBar() {
homeWidth?.constant = barWidthHome
homeTeamStatisticBar.setNeedsLayout()
}
func homeBarConstraints() {
homeTeamStatisticBar.translatesAutoresizingMaskIntoConstraints = false
var homeConstraints: [NSLayoutConstraint] = [
homeTeamStatisticBar.topAnchor.constraint(equalTo: topAnchor, constant: 4),
homeTeamStatisticBar.rightAnchor.constraint(equalTo: rightAnchor, constant: -barCenter),
homeTeamStatisticBar.heightAnchor.constraint(equalToConstant: 16)]
homeWidth = homeTeamStatisticBar.widthAnchor.constraint(equalToConstant: barWidthHome)
homeConstraints.append(homeWidth!)
NSLayoutConstraint.activate(homeConstraints)
}

UIButton Array - Each Button is pointing tag 0 in CollectionView swift

I'm designing my UI programmatically, I'm creating some buttons and then showing in UICollectionView. after showing in UICollectionView when I checked, each button is pointing to tag 0.
Here's my complete code code.
class CollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate{
var buttonsArray = [UIButton]()
let buttonsName = ["Start Trade", "Wallet","Profile", "My Portfolio", "Dashboard","My Transactions","My Blotter" ,"My Reports", "Forum"]
let buttonImages = ["1","2","3", "4", "5", "6", "7","8", "9"]
var collectionview: UICollectionView!
var cellId = "Cell"
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<9 {
let btn = UIButton()
btn.tag = i
btn.frame = CGRect(x: btn.frame.width/2, y:btn.frame.width/2, width: 106, height: 97)
btn.backgroundColor = UIColor(red: 0.15, green: 0.22, blue: 0.68, alpha: 0.86)
btn.layer.cornerRadius = 5
btn.titleLabel?.font = UIFont.setFont(of: 12)
btn.addTarget(self, action: #selector(pressedAction(_:)), for: .touchUpInside)
btn.setTitleColor(UIColor(red: 0.82, green: 0.56, blue: 0.23, alpha: 1), for: .normal)
btn.setTitle(buttonsName[i], for: .normal)
btn.translatesAutoresizingMaskIntoConstraints = false
buttonsArray.append(btn)
}
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: 106, height: 97)
collectionview = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionview.dataSource = self
collectionview.delegate = self
collectionview.register(MainDashBoardCollectionViewCell.self, forCellWithReuseIdentifier: cellId)
collectionview.showsVerticalScrollIndicator = false
collectionview.backgroundColor = view.backgroundColor
self.view.addSubview(collectionview)
}
#objc func pressedAction(_ sender: UIButton) {
// do your stuff here
sender.animateButton(sender: sender)
print(sender.tag)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return buttonsArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
let cell = collectionview.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! MainDashBoardCollectionViewCell
cell.images.image = buttonImages[indexPath.item]
cell.button = buttonsArray[indexPath.item]
cell.Label0.text = buttonsName[indexPath.item]
return cell
}
Here's My CollectionViewCell class
import UIKit
class MainDashBoardCollectionViewCell: UICollectionViewCell {
var button :UIButton = {
let btn = UIButton()
btn.frame = CGRect(x: btn.frame.width/2, y:btn.frame.width/2, width: 106, height: 97)
btn.backgroundColor = UIColor(red: 0.15, green: 0.22, blue: 0.68, alpha: 0.86)
btn.layer.cornerRadius = 5
btn.addTarget(self, action: #selector(pressedAction(_:)), for: .touchUpInside)
btn.titleLabel?.font = UIFont.setFont(of: 12)
btn.setTitleColor(UIColor(red: 0.82, green: 0.56, blue: 0.23, alpha: 1), for: .normal)
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
var images: UIImageView = {
let imgV = UIImageView()
imgV .translatesAutoresizingMaskIntoConstraints = false
return imgV
}()
let Label0: UILabel = {
let label = UILabel()
label.frame = CGRect(x: 0, y: 0, width: 60, height: 15)
label.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
label.text = ""
label.font = UIFont.setFont(of: 12)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(images)
addSubview(button)
addSubview(Label0)
shared()
}
#objc func pressedAction(_ sender: UIButton) {
// do your stuff here
print("clicked")
print("you clicked on button \(sender.tag)")
}
func shared() {
self.contentView.addSubview(button)
self.contentView.addSubview(images)
NSLayoutConstraint.activate([
button.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0),
button.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
button.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0),
button.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0),
])
images.widthAnchor.constraint(equalToConstant: 26).isActive = true
images.heightAnchor.constraint(equalToConstant: 28).isActive = true
images.centerXAnchor.constraint(equalTo: button.centerXAnchor).isActive = true
images.centerYAnchor.constraint(equalTo: button.centerYAnchor, constant: -10).isActive = true
Label0.centerXAnchor.constraint(equalTo: images.centerXAnchor).isActive = true
Label0.centerYAnchor.constraint(equalTo: button.centerYAnchor, constant: 20).isActive = true
}
required init?(coder aDecoder: NSCoder) {
//super.init(aDecoder)
fatalError("init(coder:) has not been implemented")
}
}
is anything wrong with my code? Please help me to fix it.
Thanks in advance.
By default every UI component have tag 0, so you have to change the tag of every button.
Try this in cellForItem
cell.button.tag = indexPath.row

add individual amount of buttons to cell programmatically

I want to build the following programmatically:
but I want to add the custom buttons programmatically. Here I have no idea, how the tell the buttons to respect the space between them and how to resize the height of the cell if the next button didn't fits into the same "row".
i tried with constraints:
import Material
class ChipButton: Button {
override func prepare() {
super.prepare()
cornerRadiusPreset = .cornerRadius5
backgroundColor = UIColor.lightGray
titleColor = Color.darkText.primary
pulseAnimation = .none
contentEdgeInsets = EdgeInsets(top: 0, left: 12, bottom: 0, right: 12)
isUserInteractionEnabled = false
titleLabel?.font = RobotoFont.regular
isOpaque = true
let constraintTop = NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: superview, attribute: .top, multiplier: 1, constant: 4)
let constraintLeading = NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: superview, attribute: .leading, multiplier: 1, constant: 4)
superview?.addConstraint(constraintTop)
superview?.addConstraint(constraintLeading)
}
}
and I add the buttons like the following:
for tag in item.tags {
let chip = ChipButton()
chip.title = tag.text
cell!.layout(chip).edges(top: 4, left: 4, bottom: 4, right: 4)
}
but the declaration of constrainTop and constrainLeading throws an error and without the constraints the buttons r on top of each other the and the size of the buttons r false.
The comment of #Palpatim inspired to do it like the following:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: UITableViewCell?
if let item: TagItem = items[indexPath.section][indexPath.row] as? TagItem {
if (item.tags.count > 0) {
// show all tags as a Chip
cell = TableViewCell()
cell?.isUserInteractionEnabled = false
var hStackView = UIStackView()
hStackView.axis = .horizontal
hStackView.spacing = 8
hStackView.alignment = .fill
hStackView.distribution = .fill
let vStackView = UIStackView()
vStackView.axis = .vertical
vStackView.spacing = 8
vStackView.alignment = .top
var tagsWidth: CGFloat = 0
for tag in item.tags {
let chip = ChipButton()
chip.title = tag.text
chip.sizeToFit()
if (tagsWidth + chip.bounds.width < (cell?.bounds.width)!) {
tagsWidth += chip.bounds.width
hStackView.addArrangedSubview(chip)
}
else {
vStackView.addArrangedSubview(hStackView)
tagsWidth = chip.bounds.width
hStackView = UIStackView()
hStackView.axis = .horizontal
hStackView.spacing = 8
hStackView.alignment = .fill
hStackView.distribution = .fill
hStackView.addArrangedSubview(chip)
}
}
vStackView.addArrangedSubview(hStackView)
cell!.layout(vStackView).edges(left: 16, right: 16).centerVertically()
return cell!
}
else {
cell = TableViewCell(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 40))
// show a label
let infoLabel = UILabel()
infoLabel.text = "no tags"
cell!.layout(infoLabel).centerVertically().edges(left: 16)
return cell!
}
}
cell = TableViewCell()
return cell!
}
and the result is:

How to animate keyboard scrolling with keyboardDismissMode = .interactive

Im creating chat part of application and i have problem with keyboard animation when user is draging scrollview up and down. I am using keyboardDismissMode = .Interactive and i cant find notification working with it.
This is defaul state. Here i have UIView() user as container for Textview and Button.
and this is my problem when user just slowly scrolling down through the keyboard. I need to move with containerView when keyboard start moving.
I tried UIkeyboardwillChangeFrame but it didnt notificate.
he re sample of my code that i believe is usefull for you.
import UIKit
struct Message {
var reciever: String?
var sender: String?
var text: String?
var time: String?
}
class ChatMessagesVC: UIViewController, UITextViewDelegate,UIScrollViewDelegate {
var chatID: String?
var recieverName: String?
var recieverId: String?
var recieverPhoto: UIImage?
let scrollView: UIScrollView = UIScrollView()
let textView: UITextView = UITextView()
let sendButton: UIButton = UIButton()
var bottomConstraint: NSLayoutConstraint!
var lastMessageFrom: String = ""
var keyboardRect: CGRect!
let SENDER_BACKGROUND_COLOR: UIColor = .whiteColor()
let SENDER_TEXT_COLOR: UIColor = .blackColor()
let SENDER_FONT: UIFont = UIFont(name: "OpenSans", size: 13)!
let RECIEVER_BACKGROUND_COLOR: UIColor = .yellowColor()
let RECIEVER_TEXT_COLOR: UIColor = .blackColor()
let RECIEVER_FONT: UIFont = UIFont(name: "OpenSans", size: 13)!
// place values
var messageX: CGFloat = 75.0
var messageY: CGFloat = 26.0
var imageX: CGFloat = 10
override func viewDidLoad() {
super.viewDidLoad()
if self.recieverName != nil {
self.title = self.recieverName
}
if self.chatID != nil {
DataModel.instance.CHAT.childByAppendingPath(self.chatID).observeEventType(.ChildAdded, withBlock: {snap in
var message = Message(reciever: nil, sender: nil, text: nil, time: snap.key)
if let text = snap.value["text"] as? String {
message.text = text
}
if let sender = snap.value["from"] as? String {
message.sender = sender
}
if let reciever = snap.value["to"] as? String {
message.reciever = reciever
}
if let _ = snap.value["unread"] as? String {
if message.sender != currentUser.id {
DataModel.instance.CHAT.childByAppendingPath(self.chatID).childByAppendingPath(message.time).childByAppendingPath("unread").removeValue()
}
}
self.addMessage(message)
})
}
// notifications about keyboard
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.keyboardWillShow), name:UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.keyboardWillHide), name:UIKeyboardWillHideNotification, object: nil)
//BG
let backgroundView = UIImageView(frame: self.view.bounds)
let image = UIImage(named: "radarTable")
backgroundView.image = image
self.view.addSubview(backgroundView)
self.textView.delegate = self
self.view.addSubview(self.scrollView)
self.scrollView.backgroundColor = .clearColor()
self.scrollView.delegate = self
self.scrollView.scrollEnabled = true
self.scrollView.keyboardDismissMode = .Interactive
self.textView.font = UIFont(name: "OpenSans", size: 15)
self.textView.textColor = .whiteColor()
self.textView.text = NSLocalizedString("chat.placeholder", comment: "")
self.textView.backgroundColor = .clearColor()
self.textView.returnKeyType = .Send
self.sendButton.setTitleColor(.blackColor(), forState: .Normal)
self.sendButton.setTitle(NSLocalizedString("chat.send", comment: ""), forState: .Normal)
self.sendButton.titleLabel?.font = UIFont(name: "OpenSans", size: 15)
self.sendButton.backgroundColor = .orangeColor()
self.sendButton.addTarget(self, action: #selector(self.sendMessage), forControlEvents: .TouchUpInside)
// divider
let divider = UIView()
divider.backgroundColor = .yellowColor()
self.view.addSubview(divider)
// container
let containerView = UIView()
containerView.addSubview(self.textView)
containerView.addSubview(self.sendButton)
self.view.addSubview(containerView)
// layout
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.textView.translatesAutoresizingMaskIntoConstraints = false
self.sendButton.translatesAutoresizingMaskIntoConstraints = false
divider.translatesAutoresizingMaskIntoConstraints = false
containerView.translatesAutoresizingMaskIntoConstraints = false
let binding = ["scroll": self.scrollView, "text": self.textView, "button": self.sendButton, "div":divider, "container": containerView]
// horizontal constraints
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[scroll]|", options: NSLayoutFormatOptions(), metrics: nil, views: binding))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[div]|", options: NSLayoutFormatOptions(), metrics: nil, views: binding))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[container]|", options: NSLayoutFormatOptions(), metrics: nil, views: binding))
containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[text]-5-[button(100)]-10-|", options: NSLayoutFormatOptions(), metrics: nil, views: binding))
// vertical constraints
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-50-[scroll]-0-[div(1)]-0-[container(50)]", options: NSLayoutFormatOptions(), metrics: nil, views: binding))
containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[text]|", options: NSLayoutFormatOptions(), metrics: nil, views: binding))
containerView.addConstraint(NSLayoutConstraint(item: containerView, attribute: .CenterY, relatedBy: .Equal, toItem: self.sendButton, attribute: .CenterY, multiplier: 1, constant: 0))
containerView.addConstraint(NSLayoutConstraint(item: self.sendButton, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 30))
self.bottomConstraint = NSLayoutConstraint(item: containerView, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1, constant: 0)
self.view.addConstraint(self.bottomConstraint)
}
//MARK: - textview Methods
func textViewDidBeginEditing(textView: UITextView) {
if textView.text == NSLocalizedString("chat.placeholder", comment: "") {
textView.text = nil
}
}
func textViewDidEndEditing(textView: UITextView) {
if textView.text.isEmpty {
textView.text = NSLocalizedString("chat.placeholder", comment: "")
}
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
self.sendMessage()
return false
}
return true
}
//MARK: - scrollview functions
// func scrollViewDidScroll(scrollView: UIScrollView) {
// let location = scrollView.panGestureRecognizer.locationInView(self.view)
// if self.keyboardRect != nil {
// let start = UIScreen.mainScreen().bounds.height - self.keyboardRect.height
// if location.y > start {
// self.bottomConstraint.constant = -self.keyboardRect.height - (start - location.y)
//
// }
// }
// }
//MARK: - keyboard notifications
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
self.keyboardRect = keyboardSize
UIView.animateWithDuration(0.3, delay: 0.0, options: .CurveEaseInOut, animations: { () -> Void in
self.bottomConstraint.constant = -keyboardSize.height
self.view.layoutIfNeeded()
}, completion: nil)
let scroll = CGPointMake(0, self.scrollView.contentSize.height - (self.scrollView.bounds.height))
self.scrollView.setContentOffset(scroll, animated: true)
}
}
func keyboardWillHide(notification: NSNotification) {
self.keyboardRect = nil
UIView.animateWithDuration(0.3, delay: 0.0, options: .CurveEaseInOut, animations: { () -> Void in
self.bottomConstraint.constant = 0
self.view.layoutIfNeeded()
}, completion: nil)
}
//MARK: - chat functions
func addMessage(message: Message) {
let theWidth = UIScreen.mainScreen().bounds.width
var messagesSpace:CGFloat = 2
if self.lastMessageFrom != message.sender {
//TODO: Dopln cas - neivem ako sa to mas presne spravat a zorbrazovat tak neimplementujem
messagesSpace += 25
}
let messageLbl : UILabel = UILabel()
messageLbl.frame = CGRectMake(0, 0, self.scrollView.frame.size.width - 100, 0)
messageLbl.lineBreakMode = NSLineBreakMode.ByWordWrapping
messageLbl.textAlignment = NSTextAlignment.Left
messageLbl.numberOfLines = 0
messageLbl.text = message.text
messageLbl.sizeToFit()
messageLbl.frame.origin.y = self.messageY + messagesSpace + 5
let frame = UIView()
if message.sender == currentUser.id {
messageLbl.backgroundColor = self.SENDER_BACKGROUND_COLOR
messageLbl.textColor = self.SENDER_TEXT_COLOR
messageLbl.font = self.SENDER_FONT
messageLbl.frame.origin.x = (self.scrollView.frame.size.width - self.messageX) - messageLbl.frame.width
} else {
messageLbl.backgroundColor = self.RECIEVER_BACKGROUND_COLOR
messageLbl.textColor = self.RECIEVER_TEXT_COLOR
messageLbl.font = self.SENDER_FONT
messageLbl.frame.origin.x = self.messageX
}
// if should add photo
if self.lastMessageFrom != message.sender {
let img:UIImageView = UIImageView()
img.frame = CGRectMake(self.imageX, self.messageY + messagesSpace, 50, 50)
self.lastMessageFrom = message.sender!
if message.sender == currentUser.id {
img.frame.origin.x = (self.scrollView.frame.size.width - self.imageX) - img.frame.size.width
img.image = currentUser.photo
} else {
img.image = self.recieverPhoto
}
img.layer.cornerRadius = img.frame.size.width/2
img.clipsToBounds = true
self.scrollView.addSubview(img)
}
let bounds = messageLbl.frame
frame.frame = CGRectMake(bounds.minX - 10, bounds.minY - 5, bounds.width + 14, bounds.height + 10)
frame.backgroundColor = messageLbl.backgroundColor
if message.sender == currentUser.id {
frame.roundCorners([.TopLeft, .BottomRight, .BottomLeft], radius: 10)
} else {
frame.roundCorners([.TopRight, .BottomRight, .BottomLeft], radius: 10)
}
self.scrollView.addSubview(frame)
self.scrollView.addSubview(messageLbl)
self.messageY += frame.frame.size.height + messagesSpace
self.scrollView.contentSize = CGSizeMake(theWidth, self.messageY + messagesSpace)
let bottomOfset:CGPoint = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height)
self.scrollView.setContentOffset(bottomOfset, animated: true)
}
func scrollViewTapped() {
self.textView.resignFirstResponder()
}
func sendButtonPressed() {
textView.resignFirstResponder()
self.sendMessage()
}
func sendMessage() {
if self.textView.text != NSLocalizedString("chat.placeholder", comment: "") && !self.textView.text.isBlank {
let time = NSDate().ToUTCStringWithFormat("yyyy-MM-dd'T'HH:mm:ss")
let result = ["from":currentUser.id, "to": self.recieverId, "text": self.textView.text, "unread": "true"]
DataModel.instance.CHAT.childByAppendingPath(self.chatID).childByAppendingPath(time).setValue(result)
self.textView.text = nil
DataModel.instance.USERS.childByAppendingPath(currentUser.id).childByAppendingPath("chats").childByAppendingPath(self.chatID).setValue(self.recieverId)
DataModel.instance.USERS.childByAppendingPath(self.recieverId).childByAppendingPath("chats").childByAppendingPath(chatID).setValue(currentUser.id)
}
}
If I understand your question correctly, I think what you probably want here is to make your _containerView an inputAccessoryView as described in the Apple docs here:
https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/InputViews/InputViews.html
Once you implement this, you'll get the keyboard behavior you describe because your view will be "attached" to the keyboard.

Resources