UICollectionViewCell Constraint Issues - ios

Look hear for edits
So I have a comments feature in my app that users use to communicate. Each comment has a pic, some text and button that lets you either flag or reply to comments. As seen in the picture below
However as seen in the picture there is a clear problem. If you add too much text it seems to run right over the button. Is there anyway I can prevent this from happening I think it looks really bad.
Below is my sorce code for reference I'm thinking it might be my constraints but I am not entirely sure.
import Foundation
import UIKit
import Firebase
protocol CommentCellDelegate: class {
func optionsButtonTapped(cell: CommentCell)
func handleProfileTransition(tapGesture: UITapGestureRecognizer)
}
class CommentCell: UICollectionViewCell {
weak var delegate: CommentCellDelegate? = nil
override var reuseIdentifier : String {
get {
return "cellID"
}
set {
// nothing, because only red is allowed
}
}
var didTapOptionsButtonForCell: ((CommentCell) -> Void)?
var comment: CommentGrabbed?{
didSet{
guard let comment = comment else{
return
}
// print("apples")
// textLabel.text = comment.content
//shawn was also here
profileImageView.loadImage(urlString: comment.user.profilePic!)
// print(comment.user.username)
let attributedText = NSMutableAttributedString(string: comment.user.username!, attributes: [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 14)])
attributedText.append(NSAttributedString(string: " " + (comment.content), attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)]))
textView.attributedText = attributedText
}
}
let textView: UITextView = {
let textView = UITextView()
textView.font = UIFont.systemFont(ofSize: 14)
textView.isScrollEnabled = false
textView.isEditable = false
return textView
}()
lazy var profileImageView: CustomImageView = {
let iv = CustomImageView()
iv.clipsToBounds = true
iv.isUserInteractionEnabled = true
iv.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleProfileTransition)))
iv.contentMode = .scaleAspectFill
return iv
}()
lazy var flagButton: UIButton = {
let flagButton = UIButton(type: .system)
flagButton.setImage(#imageLiteral(resourceName: "icons8-Info-64"), for: .normal)
flagButton.addTarget(self, action: #selector(optionsButtonTapped), for: .touchUpInside)
return flagButton
}()
#objc func optionsButtonTapped (){
didTapOptionsButtonForCell?(self)
}
#objc func onOptionsTapped() {
delegate?.optionsButtonTapped(cell: self)
}
#objc func handleProfileTransition(tapGesture: UITapGestureRecognizer){
delegate?.handleProfileTransition(tapGesture: tapGesture)
// print("Tapped image")
}
override init(frame: CGRect){
super.init(frame: frame)
addSubview(textView)
addSubview(profileImageView)
addSubview(flagButton)
textView.anchor(top: topAnchor, left: profileImageView.rightAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 4, paddingLeft: 4, paddingBottom: 4, paddingRight: 4, width: 0, height: 0)
profileImageView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 8, paddingLeft: 8, paddingBottom: 0, paddingRight: 0, width: 40, height: 40)
profileImageView.layer.cornerRadius = 40/2
flagButton.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 4, width: 40, height: 40)
flagButton.addTarget(self, action: #selector(CommentCell.onOptionsTapped), for: .touchUpInside)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

You need to assign textView's right anchor to the button's left one so they got aligned on sides. This would be the correct layout setup for the textView:
textView.anchor(top: topAnchor, left: profileImageView.rightAnchor, bottom: bottomAnchor, right: flagButton.leftAnchor, paddingTop: 4, paddingLeft: 4, paddingBottom: 4, paddingRight: 4, width: 0, height: 0)

Related

How do I get a UILabel to show multiline and word wrapping?

I am trying to get my itemLabel UILabel to be multiline and do word wrapping, but I can't figure out where I'm going wrong here. I have numberOfLines = 0 and .byWordWrapping set. I'm sure it's a simple line of code I messed up, but I can't find it.
import UIKit
import Foundation
protocol ListItemCellDelegate {
func setChecked(cell: ListItemCell)
}
class ListItemCell: UITableViewCell {
var delegate: ListItemCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override func prepareForReuse() {
super.prepareForReuse()
self.delegate = nil
}
let cellView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.white
view.setCellShadow()
return view
}()
let checkButton: UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "UnChecked"), for: .normal)
button.setImage(UIImage(named: "Checked"), for: .selected)
button.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
return button
}()
let priorityButton: UIButton = {
let button = UIButton(type: .custom)
button.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
button.setTitleColor(.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.borderWidth = 2
button.layer.borderColor = UIColor.black.cgColor
button.titleLabel?.font = UIFont.init(name: "Avenir Next", size: 24)
return button
}()
let itemLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.black
label.font = UIFont.init(name: "Avenir Next", size: 16)
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupCell()
checkButton.addTarget(self, action: #selector(setChecked), for: .touchUpInside)
itemLabel.translatesAutoresizingMaskIntoConstraints = false
itemLabel.numberOfLines = 0
itemLabel.lineBreakMode = .byWordWrapping
}
func setupCell() {
addSubview(cellView)
cellView.addSubview(checkButton)
cellView.addSubview(priorityButton)
cellView.addSubview(itemLabel)
cellView.setAnchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 4, paddingLeft: 8, paddingBottom: 4, paddingRight: 8)
checkButton.setAnchor(top: nil, left: cellView.leftAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 8, paddingBottom: 0, paddingRight: 0, width: 50, height: 50)
checkButton.centerYAnchor.constraint(equalTo: cellView.centerYAnchor).isActive = true
priorityButton.setAnchor(top: nil, left: checkButton.rightAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 8, paddingBottom: 0, paddingRight: 0, width: 40, height: 40)
priorityButton.centerYAnchor.constraint(equalTo: checkButton.centerYAnchor).isActive = true
itemLabel.setAnchor(top: nil, left: priorityButton.rightAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 20, paddingBottom: 0, paddingRight: 20)
itemLabel.centerYAnchor.constraint(equalTo: priorityButton.centerYAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc private func setChecked() {
self.delegate?.setChecked(cell: self)
}
}
I expect the UILabel to wrap the text and take up as much space as needed to fit the whole label text in the cell. Right now it is not displaying the entire message, only what the size will allow. But, I want it to resize based on the length of the string. I did this easily in Storyboard, but I'm trying to get it to work programmatically.
I have done that before like in UITableView() there was an array of Strings and the length of the String was not fixed it can be in 3 lines or more.
So for that, i have given the constraints to UILabel() like top, bottom,leading, trailing all constraint to 0.
In UITableView(), there is a method called :
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Try this one it will definitly help you i guess.
In my case, part of the solution was what Varsha Shivhare suggested, and the other was setting the heightAnchor constraint to >= 60 which is what I'm using right now. Thank you!

UIStackViews Autolayout throws when either spacing or contentEdgeInsets of UIButton is enabled

I got a UIStackView ParticipateButtonStackView managing three buttons of the same class: ParticipateButton
These buttons only contain an image. To have some padding at the top and bottom, I set UIEdgeInsets. But this and the .spacing = 1 property seem to not work simultaneously. Why?
Stackview:
class ParticipateButtonStackView: UIStackView {
//MARK: - Properties & Variables
var delegate: participateButtonStackViewDelegate?
//MARK: - GUI Objects
var buttons: [ParticipateButton]!
let sureButton = ParticipateButton(type: .sure)
let maybeButton = ParticipateButton(type: .maybe)
let nopeButton = ParticipateButton(type: .nope)
//MARK: - Init & View Loading
override init(frame: CGRect) {
super.init(frame: frame)
self.addBackground(color: UIColor.gray)
buttons = [sureButton, maybeButton, nopeButton]
setupStackView()
setupButtons()
}
//MARK: - Setup
func setupStackView() {
self.axis = .horizontal
self.spacing = 1
self.alignment = .leading
self.distribution = .fillEqually
}
func setupButtons() {
for button in buttons {
addArrangedSubview(button)
button.addTarget(self, action: #selector (self.buttonClick(sender:)), for: .touchUpInside)
}
}
}
ParticipateButton
class ParticipateButton: UIButton {
var typeOfButton: participateLists!
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = LebensfitSettings.Colors.buttonBG
self.tintColor = LebensfitSettings.Colors.basicTintColor
self.imageView?.contentMode = .scaleAspectFit
self.contentEdgeInsets = UIEdgeInsets(top: 7, left: 0, bottom: 7, right: 0)
}
convenience init(type: participateLists) {
self.init()
self.typeOfButton = type
setImage(type: type)
}
func setImage(type: participateLists) {
var buttonImage: UIImage?
switch type {
case .sure:
buttonImage = UIImage(named: "pb_sure")?.withRenderingMode(.alwaysTemplate)
[...]
}
self.setImage(buttonImage, for: .normal)
}
}
This is the error that gets thrown:
Will attempt to recover by breaking constraint
NSLayoutConstraint:0x600001adb020 'UISV-spacing' H: [LebensfitFirebase.ParticipateButton:0x7f9899c5eaa0]-(1)-[LebensfitFirebase.ParticipateButton:0x7f9899c5fad0] (active)>
Edit:
participateButtonStackView.leftAnchor = view.leftAnchor
participateButtonStackView.rightAnchor = view.rightAnchor
participateButtonStackView.bottomAnchor = view.bottomAnchor
participateButtonStackView.heightAnchor = equalToConstant(50)
I was not able to solve this problem so i built this workaround:
ParticipateButton
func setBordersLeftRight() {
let borderLeft = UIView()
let borderRight = UIView()
borderLeft.backgroundColor = .gray
borderRight.backgroundColor = .gray
addSubview(borderLeft)
addSubview(borderRight)
borderLeft.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0.5, height: 0)
borderRight.anchor(top: topAnchor, left: nil, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0.5, height: 0)
}

CollectionView cells not appearing correctly using mmPlayerView

I have a collectionview that contains a video, a caption, profile image and a few other labels in each cell. When I load the collection view the first cell loads perfectly. Everything is all showing in the correct position. The rest however is loaded incorrectly for example sometimes the video does not fit properly within the imageview so it looks like its been cut out or sometimes the caption doesn't show at all.
But when I scroll down more a cell might randomly show everything in the correct positions again. I am using a library MMPlayerView to load my videos into the cell. I have been trying to figure out why the first cell loads perfectly but then the rest doesn't?
I am trying to create a collectionview that just plays videos like Instagram. I've also build out the collectionview programmatically if someone needs to know.
viewController
lazy var mmPlayerLayer: MMPlayerLayer = {
let l = MMPlayerLayer()
l.cacheType = .memory(count: 5)
l.coverFitType = .fitToPlayerView
l.videoGravity = AVLayerVideoGravityResizeAspectFill
l.replace(cover: CoverA.instantiateFromNib())
return l
}()
#IBOutlet weak var newsfeedCollectionView: UICollectionView!
var videoPosts = [videoPost]()
override func viewDidLoad() {
super.viewDidLoad()
newsfeedCollectionView?.register(videoListCollectionViewCell.self, forCellWithReuseIdentifier: cellId)
newsfeedCollectionView.addObserver(self, forKeyPath: "contentOffset", options: [.new], context: nil)
newsfeedCollectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 20, right:0)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.updateByContentOffset()
self.startLoading()
}
fetchAllPosts()
}
fileprivate func landscapeAction() {
// just landscape when last result was finish
if self.newsfeedCollectionView.isDragging || self.newsfeedCollectionView.isTracking || self.presentedViewController != nil {
return
}
if UIDevice.current.orientation.isLandscape {
let full = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "FullScreenViewController") as! FullScreenViewController
MMLandscapeWindow.shared.makeKey(root: full, playLayer: self.mmPlayerLayer, completed: {
print("landscape completed")
})
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "contentOffset" {
self.updateByContentOffset()
NSObject.cancelPreviousPerformRequests(withTarget: self)
self.perform(#selector(startLoading), with: nil, afterDelay: 0.3)
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
func fetchAllPosts() {
//posts are loaded from firebase
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return videoPosts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! videoListCollectionViewCell
let postModelInfo = videoPosts[indexPath.item]
cell.post = postModelInfo
return cell
}
fileprivate func updateByContentOffset() {
let p = CGPoint(x: newsfeedCollectionView.frame.width/2, y: newsfeedCollectionView.contentOffset.y + newsfeedCollectionView.frame.width/2)
if let path = newsfeedCollectionView.indexPathForItem(at: p),
self.presentedViewController == nil {
self.updateCell(at: path)
}
}
fileprivate func updateCell(at indexPath: IndexPath) {
if let cell = newsfeedCollectionView.cellForItem(at: indexPath) as? videoListCollectionViewCell {
cell.delegate = self
cell.videoDelegate = self
// this thumb use when transition start and your video dosent start
mmPlayerLayer.thumbImageView.image = cell.photoImageView.image
// set video where to play
if !MMLandscapeWindow.shared.isKeyWindow {
mmPlayerLayer.playView = cell.photoImageView
}
// set url prepare to load
mmPlayerLayer.set(url: URL(string:(cell.post?.videoUrl)!), state: { (status) in
switch status {
case .failed(let err):
let alert = UIAlertController(title: "error", message: err.description, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
case .ready:
print("Ready to Play")
case .playing:
print("Playing")
case .pause:
print("Pause")
case .end:
print("End")
default: break
}
})
cell.layoutIfNeeded()
}
}
#objc fileprivate func startLoading() {
if self.presentedViewController != nil {
return
}
// start loading video
mmPlayerLayer.startLoading()
self.landscapeAction()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let post = videoPosts[indexPath.item]
let width = (view.frame.width)
let imageWidth = post.imageWidth?.floatValue,
imageHeight = post.imageHeight?.floatValue
let oldHeight = CGFloat(imageWidth! / imageHeight!)
let height = CGFloat(width / oldHeight + 138)
let rect = NSString(string: post.postCaption!).boundingRect(with: CGSize(width:view.frame.width, height:1000), options: NSStringDrawingOptions.usesFontLeading.union(NSStringDrawingOptions.usesLineFragmentOrigin), attributes: [NSFontAttributeName : UIFont.systemFont(ofSize: 14)], context: nil)
return CGSize(width:view.frame.width, height:rect.height + height)
}
CollectionViewCell
protocol mmPlayerDelegate{
func startLoading(for cell: videoListCollectionViewCell)
}
class videoListCollectionViewCell: UICollectionViewCell {
var delegate: PostCellDelegate?
var videoDelegate: videoDelegate?
var mmPlayerDelegate: mmPlayerDelegate?
var post: videoPost? {
didSet {
guard let postImageUrl = post?.imageUrl else { return }
self.photoImageView.sd_setImage(with: URL(string: postImageUrl), placeholderImage: UIImage(named: "blurLogo"))
self.userProfileImageView.sd_setImage(with: URL(string: post?.profilePic), placeholderImage: UIImage(named: "blurLogo"))
usernameLabel.text = post?.fullName
captionLabel.text = post!.postCaption
}
override func prepareForReuse() {
super.prepareForReuse()
post = nil
}
let userProfileImageView: CustomImageView = {
var iv = CustomImageView()
iv.image = UIImage(named:"blurLogo")
iv.contentMode = .scaleAspectFill
iv.translatesAutoresizingMaskIntoConstraints = false
iv.backgroundColor = UIColor.darkGray
iv.clipsToBounds = true
return iv
}()
var photoImageView: CustomImageView = {
var iv = CustomImageView()
iv.image = UIImage(named:"blurLogo")
iv.contentMode = .scaleAspectFill
iv.backgroundColor = UIColor.clear
iv.translatesAutoresizingMaskIntoConstraints = false
iv.clipsToBounds = true
iv.layer.masksToBounds = true
return iv
}()
let usernameLabel: UILabel = {
let label = UILabel()
label.text = ""
label.textColor = UIColor.white
label.font = UIFont.boldSystemFont(ofSize: 14)
return label
}()
let postDate: UILabel = {
let label = UILabel()
label.text = ""
label.font = UIFont.boldSystemFont(ofSize: 10)
label.textColor = UIColor.gray
return label
}()
let likeCount: UILabel = {
let label = UILabel()
label.text = "0"
label.textColor = UIColor.white
label.font = UIFont.systemFont(ofSize: 14)
return label
}()
let optionsButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("•••", for: .normal)
button.setTitleColor(.white, for: .normal)
return button
}()
lazy var likeButton: UIButton = {
let button = UIButton(type: .system)
let stencil = imageLiteral(resourceName: "like_unselected").withRenderingMode(.alwaysTemplate)
button.setImage(stencil, for: .normal)
button.tintColor = UIColor.white
button.addTarget(self, action: #selector(handleLike), for: .touchUpInside)
return button
}()
lazy var commentButton: UIButton = {
let button = UIButton(type: .system)
let stencil = imageLiteral(resourceName: "comment").withRenderingMode(.alwaysTemplate)
button.setImage(stencil, for: .normal)
button.tintColor = UIColor.white
button.addTarget(self, action: #selector(handleComment), for: .touchUpInside)
return button
}()
let captionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = UIColor.white
label.font = UIFont.systemFont(ofSize: 14)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(userProfileImageView)
addSubview(usernameLabel)
addSubview(optionsButton)
addSubview(photoImageView)
userProfileImageView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 8, paddingLeft: 8, paddingBottom: 0, paddingRight: 0, width: 40, height: 40)
userProfileImageView.layer.cornerRadius = 40 / 2
usernameLabel.anchor(top: topAnchor, left: userProfileImageView.rightAnchor, bottom: photoImageView.topAnchor, right: optionsButton.leftAnchor, paddingTop: 0, paddingLeft: 8, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
photoImageView.anchor(top: userProfileImageView.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 16, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
addSubview(postDate)
postDate.anchor(top: topAnchor, left: nil, bottom: photoImageView.topAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 8, width: 0, height: 0)
optionsButton.anchor(top: photoImageView.bottomAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 15, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 44, height: 0)
addSubview(fullScreen)
fullScreen.anchor(top: nil, left: nil , bottom: photoImageView.bottomAnchor, right: photoImageView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 15, paddingRight: 15, width: 30, height: 30)
addSubview(activityIndicatorView)
activityIndicatorView.anchor(top: photoImageView.centerYAnchor, left: photoImageView.centerXAnchor , bottom: nil, right: nil, paddingTop: -15, paddingLeft: -15, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
setupActionButtons()
addSubview(likeCountButton)
likeCountButton.anchor(top: photoImageView.bottomAnchor, left: likeButton.rightAnchor, bottom: nil, right: nil, paddingTop: 16, paddingLeft: -18, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
addSubview(captionLabel)
captionLabel.anchor(top: likeButton.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 8, paddingBottom: 6, paddingRight: 8, width: 0, height: 0)
addSubview(viewCount)
viewCount.anchor(top: captionLabel.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 10, paddingLeft: 8, paddingBottom: 6, paddingRight: 8, width: 0, height: 0)
}
fileprivate func setupActionButtons() {
let stackView = UIStackView(arrangedSubviews: [likeButton, likeCount,commentButton, commentCount])
stackView.distribution = .fillProportionally
addSubview(stackView)
stackView.anchor(top: photoImageView.bottomAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 190, height: 50)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Couple of things which might need changing in your code to achieve what you need:
Dont use sizeForItemAt to derive the height of custom cell.
Instead use estimatedItemSize in your viewDidLoad.
Compute the height for the cell depending on the content for the cell in didSet function for post.

CollectionVIewCell Text Rendering Issues

So I have a comments feature in my app that users use to communicate. Each comment has a pic, some text,a button that lets you either flag or reply to comments, and the how long ago the comment was posted. As seen in the picture below
However as seen in the picture there is a clear problem. Depending on the amount of text typically anything that spans one line or more. The time label does not show.
import Foundation
import UIKit
import Firebase
protocol CommentCellDelegate: class {
func optionsButtonTapped(cell: CommentCell)
func handleProfileTransition(tapGesture: UITapGestureRecognizer)
}
class CommentCell: UICollectionViewCell {
weak var delegate: CommentCellDelegate? = nil
override var reuseIdentifier : String {
get {
return "cellID"
}
set {
// nothing, because only red is allowed
}
}
var didTapOptionsButtonForCell: ((CommentCell) -> Void)?
weak var comment: CommentGrabbed?{
didSet{
guard let comment = comment else{
return
}
// print("apples")
// textLabel.text = comment.content
//shawn was also here
profileImageView.loadImage(urlString: comment.user.profilePic!)
// print(comment.user.username)
let attributedText = NSMutableAttributedString(string: comment.user.username!, attributes: [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 14)])
attributedText.append(NSAttributedString(string: " " + (comment.content), attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)]))
attributedText.append(NSAttributedString(string: "\n\n", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 4)]))
let timeAgoDisplay = comment.creationDate.timeAgoDisplay()
attributedText.append(NSAttributedString(string: timeAgoDisplay, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 12), NSAttributedStringKey.foregroundColor: UIColor.gray]))
textView.attributedText = attributedText
}
}
lazy var textView: UITextView = {
let textView = UITextView()
textView.font = UIFont.systemFont(ofSize: 14)
textView.isScrollEnabled = false
textView.textContainer.maximumNumberOfLines = 2
textView.textContainer.lineBreakMode = .byCharWrapping
textView.isEditable = false
return textView
}()
lazy var profileImageView: CustomImageView = {
let iv = CustomImageView()
iv.clipsToBounds = true
iv.isUserInteractionEnabled = true
iv.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleProfileTransition)))
iv.contentMode = .scaleAspectFill
return iv
}()
lazy var flagButton: UIButton = {
let flagButton = UIButton(type: .system)
flagButton.setImage(#imageLiteral(resourceName: "icons8-Info-64"), for: .normal)
flagButton.addTarget(self, action: #selector(optionsButtonTapped), for: .touchUpInside)
return flagButton
}()
#objc func optionsButtonTapped (){
didTapOptionsButtonForCell?(self)
}
#objc func onOptionsTapped() {
delegate?.optionsButtonTapped(cell: self)
}
#objc func handleProfileTransition(tapGesture: UITapGestureRecognizer){
delegate?.handleProfileTransition(tapGesture: tapGesture)
// print("Tapped image")
}
override init(frame: CGRect){
super.init(frame: frame)
addSubview(textView)
addSubview(profileImageView)
addSubview(flagButton)
textView.anchor(top: topAnchor, left: profileImageView.rightAnchor, bottom: bottomAnchor, right: flagButton.leftAnchor, paddingTop: 4, paddingLeft: 4, paddingBottom: 4, paddingRight: 4, width: 0, height: 0)
profileImageView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 8, paddingLeft: 8, paddingBottom: 0, paddingRight: 0, width: 40, height: 40)
profileImageView.layer.cornerRadius = 40/2
flagButton.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 4, width: 40, height: 40)
flagButton.addTarget(self, action: #selector(CommentCell.onOptionsTapped), for: .touchUpInside)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I figured it was my constraints but it doesn't seem like it is. Does anyone have any idea why this would happen?
Autosizing works only when maximumNumberOfLines is zero.
textView.textContainer.maximumNumberOfLines = 0
Refer: Docs

TextView Not Wrapping

I have a textview instead of a textfield so I can autowrap text. However, the text itself is not auto wrapping. When the line gets too long the following text just disappears. If I go to an earlier point in the line and start deleting text, then the text that wasn't visible begins to appear.It should autoresize itself butit does not.
import UIKit
protocol CommentInputAccessoryViewDelegate {
func handleSubmit(for comment: String?)
}
class CommentInputAccessoryView: UIView, UITextViewDelegate {
var delegate: CommentInputAccessoryViewDelegate?
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
fileprivate let submitButton: UIButton = {
let submitButton = UIButton(type: .system)
submitButton.setTitle("Submit", for: .normal)
submitButton.setTitleColor(.black, for: .normal)
submitButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14)
submitButton.addTarget(self, action: #selector(handleSubmit), for: .touchUpInside)
//submitButton.isEnabled = false
return submitButton
}()
lazy var commentTextView: UITextView = {
let textView = UITextView()
// textView.placeholder = "Add a comment"
textView.delegate = self
textView.isScrollEnabled = false
textView.backgroundColor = .red
textView.font = UIFont.boldSystemFont(ofSize: 12)
textView.textContainer.lineBreakMode = .byWordWrapping
// textView.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
return textView
}()
override init(frame: CGRect) {
super.init(frame: frame)
// backgroundColor = .red
//1
autoresizingMask = .flexibleHeight
addSubview(submitButton)
submitButton.anchor(top: topAnchor, left: nil, bottom: bottomAnchor, right:rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 12, width: 50, height: 0)
addSubview(commentTextView)
//3
if #available(iOS 11.0, *){
commentTextView.anchor(top: topAnchor, left: leftAnchor, bottom: safeAreaLayoutGuide.bottomAnchor, right: submitButton.leftAnchor, paddingTop: 0, paddingLeft: 12, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
}else{
//fallback on earlier versions
}
setupLineSeparatorView()
}
//2
override var intrinsicContentSize: CGSize{
return .zero
}
fileprivate func setupLineSeparatorView(){
let lineSeparatorView = UIView()
lineSeparatorView.backgroundColor = UIColor.rgb(red: 230, green: 230, blue: 230)
addSubview(lineSeparatorView)
lineSeparatorView.anchor(top:topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0.5)
}
#objc func handleSubmit(){
guard let commentText = commentTextView.text else{
return
}
delegate?.handleSubmit(for: commentText)
}
#objc func textFieldDidChange(_ textField: UITextView) {
let isCommentValid = commentTextView.text?.count ?? 0 > 0
if isCommentValid {
submitButton.isEnabled = true
}else{
submitButton.isEnabled = false
}
}
func clearCommentTextField(){
commentTextView.text = nil
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I'm thinking it is the anchors but I'm not entirely sure.

Resources