Open Recouse opened 5 years ago
Bump
Does anyone have a solution to this problem?
@Recouse, do you have more info on how you got to this result? Were you following a guide to help you get here? Without much more info about what's going on here I can't really give you any useful help.
I used expanding cell code from examples. But I wanted to do this with self-sizing cells.
AuthorDescriptionCell.swift
:
import UIKit
class AuthorDescriptionCell: UICollectionViewCell {
static let insets = UIEdgeInsets(top: 0, left: Global.UI.edgeInset, bottom: 0, right: Global.UI.edgeInset)
static let font = UIFont.systemFont(ofSize: 14)
static var singleLineHeight: CGFloat {
return font.lineHeight
}
var dataSource: Author? {
didSet {
updateData()
}
}
var shouldUpdateSize: Bool = false
let descriptionTextView: ReadMoreTextView = {
let textView = ReadMoreTextView()
textView.textColor = Asset.Colors.dark.color
textView.font = .systemFont(ofSize: 14)
textView.shouldTrim = true
textView.maximumNumberOfLines = 3
textView.contentInset = .zero
let readMoreStyle: [NSAttributedString.Key: Any] = [
.font: UIFont.systemFont(ofSize: 14, weight: .medium),
.foregroundColor: Asset.Colors.clearBlue.color
]
textView.attributedReadMoreText = NSAttributedString(string: " Ko‘proq", attributes: readMoreStyle)
textView.attributedReadLessText = NSAttributedString(string: " Kamroq", attributes: readMoreStyle)
return textView
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(descriptionTextView)
descriptionTextView.snp.makeConstraints {
$0.top.equalToSuperview()
$0.left.right.equalToSuperview().offset(Global.UI.edgeInset).inset(Global.UI.edgeInset)
$0.bottom.equalToSuperview().priority(250)
}
descriptionTextView.readMoreDelegate = self
descriptionTextView.onSizeChange = { [unowned self] _ in
guard self.shouldUpdateSize else { return }
self.shouldUpdateSize = false
self.delegate?.sizeChanged()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
setNeedsLayout()
layoutIfNeeded()
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
var newFrame = layoutAttributes.frame
newFrame.size.height = ceil(size.height)
layoutAttributes.frame = newFrame
return layoutAttributes
}
func updateData() {
guard let author = dataSource else { return }
descriptionTextView.text = author.biography
}
private func updateSize() {
let bounds = contentView.bounds
descriptionTextView.frame = bounds.inset(by: AuthorDescriptionCell.insets)
}
static func textHeight(_ text: String, width: CGFloat) -> CGFloat {
let constrainedSize = CGSize(width: width - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude)
let attributes = [ NSAttributedString.Key.font: font ]
let options: NSStringDrawingOptions = [.usesFontLeading, .usesLineFragmentOrigin]
let bounds = (text as NSString).boundingRect(with: constrainedSize, options: options, attributes: attributes, context: nil)
return ceil(bounds.height) + insets.top + insets.bottom
}
}
extension AuthorDescriptionCell: ReadMoreTextViewDelegate {
func textWasCollapsed() {
shouldUpdateSize = true
}
func textWasExpanded() {
shouldUpdateSize = true
}
}
AuthorDescriptionSectionController.swift
:
import UIKit
import IGListKit
class AuthorDescriptionSectionController: ListSectionController {
var object: KeyedModel<Author>?
override init() {
super.init()
inset = UIEdgeInsets(top: 14, left: 0, bottom: 24, right: 0)
}
override func didUpdate(to object: Any) {
self.object = object as? KeyedModel<Author>
}
override func sizeForItem(at index: Int) -> CGSize {
let width = collectionContext!.containerSize.width
let height = AuthorDescriptionCell.singleLineHeight * 3
return CGSize(width: width, height: height)
}
override func cellForItem(at index: Int) -> UICollectionViewCell {
guard let cell = collectionContext?.dequeueReusableCell(
of: AuthorDescriptionCell.self,
for: self,
at: index
) as? AuthorDescriptionCell else {
fatalError()
}
cell.dataSource = object?.model
cell.delegate = self
return cell
}
func toggle() {
collectionContext?.invalidateLayout(for: self)
}
}
extension AuthorDescriptionSectionController: BookDescriptionCellDelegate {
func sizeChanged() {
toggle()
}
}
Bump
No any progress?
Nope, still don’t know how to solve this.
@Recouse do you have a minimal example that reproduces this problem? It'd help if it's more narrowed down
@joetam I posted an example here https://github.com/Instagram/IGListKit/issues/1345#issuecomment-518505712
@Recouse the animation looks like it's funky because after collectionContext?.invalidateLayout(for: self)
is called in the section controller, the size for the cell is the same as the previous layout (AuthorDescriptionCell.singleLineHeight * 3
) and THEN preferredLayoutAttributesFitting
is triggering a resize.
Here is an IGListKit example of an expanding section controller.
Based on the above example, here are some changes I recommend:
expanded
property in the section controller and toggle this property before calling invalidateLayout
in the section controllersizeForItem
, calculate the desired height for your cell based on the expanded
state. (i.e. height = expanded ? AuthorDescriptionCell.textHeight(text:width:) : AuthorDescriptionCell.singleLineHeight * 3
(this can be cleaned up even further by passing expanded
to textHeight
and doing the calculation in there))invalidateLayout
in an animation blockpreferredLayoutAttributesFitting
(you don't really need this in most cases with IGListKit)ReadMoreTextViewDelegate
(and the cell's shouldUpdateSize
), and instead assign a tap target to "read more" in the collection view cell. when user has tapped read more, directly trigger the cell's delegate to invalidateLayout
from the section controller. descriptionTextView
's maximum number of lines as neededHope the above helps!
New issue checklist
README
and documentationI tried to make cell expanding animation, but got this: https://imgur.com/a/DtCEHis
I saw implementation in examples with updating labels frame in
layoutSubviews
(mine with constraints and self-sizing cells), it works a bit better, but I think there should be another way.I want it to work like in Instagram app in photo descriptions)