我已经阅读了一些关于向UICollectionView添加标题的帖子。在Swift的iOS 7+应用程序中,我尝试添加一个带有UILabel的标头,其高度应根据UILabel的高度进行调整。 UILabel的行= 0。
我已使用AutoLayout
在IB中设置标题
ViewController实现UICollectionViewDelegate, UICollectionViewDataSource
。我没有为标题设置自定义类,但我正在使用这两个函数:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
//description is a String variable defined in the class
let size:CGSize = (description as NSString).boundingRectWithSize(CGSizeMake(CGRectGetWidth(collectionView.bounds) - 20.0, 180.0), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: UIFont(name: "Helvetica Neue", size: 16.0)], context: nil).size
return CGSizeMake(CGRectGetWidth(collectionView.bounds), ceil(size.height))
}
func collectionView(collectionView: UICollectionView!, viewForSupplementaryElementOfKind kind: String!, atIndexPath indexPath: NSIndexPath!) -> UICollectionReusableView! {
var reusableview:UICollectionReusableView = UICollectionReusableView()
if (kind == UICollectionElementKindSectionHeader) {
//listCollectionView is an @IBOutlet UICollectionView defined at class level, using collectionView crashes
reusableview = listCollectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "ListHeader", forIndexPath: indexPath) as UICollectionReusableView
let label = reusableview.viewWithTag(200) as UILabel //the UILabel within the header is tagged with 200
label.text = description //description is a String variable defined in the class
}
}
return reusableview
}
显示文本似乎有效但高度计算似乎不起作用(见下面的截图)。另外,我也不认为我可以通过collectionView...referenceSizeForHeaderInSection
功能访问UILabel。关于如何正确计算CGSize的任何建议?
答案 0 :(得分:22)
我就这样做了:
let labels = [
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ac lorem enim. Curabitur rhoncus efficitur quam, et pretium ipsum. Nam eu magna at velit sollicitudin fringilla nec nec nisi. Quisque nec enim et ipsum feugiat pretium. Vestibulum hendrerit arcu ut ipsum gravida, ut tincidunt justo pellentesque. Etiam lacus ligula, aliquet at lorem vel, ullamcorper commodo turpis. Nullam commodo sollicitudin mauris eu faucibus.",
"Lorem ipsum dolor",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ac lorem enim. Curabitur rhoncus efficitur quam, et pretium ipsum. Nam eu magna at velit sollicitudin fringilla nec nec nisi. Quisque nec enim et ipsum feugiat pretium."]
基本思想是创建与将在节标题中显示的UILabel
相同的referenceSizeForHeaderInSection
。该标签将用于在label
方法中为标题设置所需的大小。
我的UICollectionReusableView
子类(MyHeaderCollectionReusableView
)中有一个名为override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 3
}
override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
let headerView =
collectionView.dequeueReusableSupplementaryViewOfKind(kind,
withReuseIdentifier: "MyHeader",
forIndexPath: indexPath)
as MyHeaderCollectionReusableView
headerView.label.text = labels[indexPath.section]
return headerView
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
// that -16 is because I have 8px for left and right spacing constraints for the label.
let label:UILabel = UILabel(frame: CGRectMake(0, 0, collectionView.frame.width - 16, CGFloat.max))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
//here, be sure you set the font type and size that matches the one set in the storyboard label
label.font = UIFont(name: "Helvetica", size: 17.0)
label.text = labels[section]
label.sizeToFit()
// Set some extra pixels for height due to the margins of the header section.
//This value should be the sum of the vertical spacing you set in the autolayout constraints for the label. + 16 worked for me as I have 8px for top and bottom constraints.
return CGSize(width: collectionView.frame.width, height: label.frame.height + 16)
}
的标签出口,我将其用于我的部分标题视图,方法是在故事板中进行分配(设置" MyHeader"作为剖面视图的重用标识符)。提到的标签对截面标题边框有水平和垂直空间限制,以便正确自动布局。
{{1}}
答案 1 :(得分:5)
与提问者一样,我有一个UICollectionView,其中包含一个带有单个标签的标题,我想改变它的高度。我创建了UILabel
的扩展名来测量具有已知宽度的多行标签的高度:
public extension UILabel {
public class func size(withText text: String, forWidth width: CGFloat) -> CGSize {
let measurementLabel = UILabel()
measurementLabel.text = text
measurementLabel.numberOfLines = 0
measurementLabel.lineBreakMode = .byWordWrapping
measurementLabel.translatesAutoresizingMaskIntoConstraints = false
measurementLabel.widthAnchor.constraint(equalToConstant: width).isActive = true
let size = measurementLabel.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
return size
}
}
注意:以上是Swift 3语法。
然后我将UICollectionViewDelegateFlowLayout
的标题大小方法实现为:
extension MyCollectionViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
let text = textForHeader(inSection: section)
var size = UILabel.size(withAttributedText: text, forWidth: collectionView.frame.size.width)
size.height = size.height + 16
return size
}
}
计算标题大小的工作委托给上述UILabel
扩展名。 +16
是基于边距的实验导出的固定偏移量(8 + 8),可以通过编程方式获得。
标题回调中所需的只是设置文本:
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionElementKindSectionHeader, let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerIdentifier, for: indexPath) as? MyCollectionHeader {
let text = textForHeader(inSection: section)
headerView.label.text = text
return headerView
}
return UICollectionReusableView()
}
答案 2 :(得分:3)
我们的想法是在内存中有一个模板头实例,以便在创建结果头视图之前计算所需的高度。您应该将节标题视图移动到单独的.nib文件,设置所有自动布局约束并在viewDidLoad方法中实例化模板,如下所示:
class MyViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
@IBOutlet var collectionView : UICollectionView?
private var _templateHeader : MyHeaderView
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "HeaderView", bundle:nil)
self.collectionView?.registerNib(nib, forCellWithReuseIdentifier: "header_view_id")
_templateHeader = nib.instantiateWithOwner(nil, options:nil)[0] as! MyHeaderView
}
}
然后,您将能够在流布局委托方法中计算标题大小(我的示例中的高度):
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
_templateHeader.lblTitle.text = "some title here"
_templateHeader.lblDescription.text = "some long description"
_templateHeader.setNeedsUpdateConstraints();
_templateHeader.updateConstraintsIfNeeded()
_templateHeader.setNeedsLayout();
_templateHeader.layoutIfNeeded();
let computedSize = _templateHeader.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
return CGSizeMake(collectionView.bounds.size.width, computedSize.height);
}
然后一如既往地创建并返回常规标题视图,因为您已经在流布局委托方法中计算了它的大小:
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let headerView = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "header_view_id", forIndexPath: indexPath) as! MyHeaderView
headerView.lblTitle.text = "some title here"
headerView.lblDescription.text = "some long description"
headerView.setNeedsUpdateConstraints()
headerView.updateConstraintsIfNeeded()
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
return headerView
default:
assert(false, "Unexpected kind")
}
}
忘了说一个重要时刻 - 你的标题视图应该在其contentView上有一个自动布局约束,以适应集合视图宽度(加上或减去所需的边距)。
答案 3 :(得分:1)
我们不必像多个答案中所示的那样从代码中重新创建新标签,而是可以简单地使用现有标签来计算拟合大小。
您的UICollectionViewDelegate
的代码:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
// We get the actual header view
let header = self.collectionView(collectionView, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: IndexPath(row: 0, section: section)) as! MyHeaderView
// We ask the label what size it takes (eventually accounting for horizontal margins)
var size = header.myLabel.sizeThatFits(CGSize(width: collectionView.frame.width - horizontalMargins, height: .greatestFiniteMagnitude))
// We eventually account for vertical margins
size.height += verticalMargins
return size
}
适用于iOS 11+的版本。
答案 4 :(得分:0)
我很幸运使用了Vladimir的方法,但是我必须将模板视图的框架设置为与我的集合视图具有相同的宽度。
templateHeader.bounds = CGRectMake(templateHeader.bounds.minX, templateHeader.bounds.minY, self.collectionView.bounds.width, templateHeader.bounds.height)
此外,我的视图有几个可调整大小的组件,并且具有模板视图似乎足够强大,可以处理任何更改。仍然觉得应该有一个更简单的方法。
答案 5 :(得分:0)
在您的单元格中添加以下内容:
fileprivate static let font = UIFont(name: FontName, size: 16)
fileprivate static let insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
同时调整字体和插图。
然后将以下内容添加到您的单元格中
static func textHeight(_ text: String, width: CGFloat) -> CGFloat {
let constrainedSize = CGSize(width: width - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude)
let attributes = [ NSAttributedStringKey.font: font ]
let options: NSStringDrawingOptions = [.usesFontLeading, .usesLineFragmentOrigin]
let bounds = (text as NSString).boundingRect(with: constrainedSize, options: options, attributes: attributes as [NSAttributedStringKey : Any], context: nil)
return ceil(bounds.height) + insets.top + insets.bottom
}
您现在可以使用此功能来计算自动高度
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
let height = ManuallySelfSizingCell.textHeight("Your text", width: view.frame.width)
return CGSize(width: view.frame.width, height: height + 16)
}
答案 6 :(得分:0)
从Swift 4 referenceSizeForHeaderInSection开始需要@objc属性
@objc func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize{
//calculate size
return size
}
答案 7 :(得分:-1)
您必须实施UICollectionViewDelegate
方法referenceSizeForHeaderInSection
。
你必须通过在具有适当属性的字符串上调用boundingRectWithSize:options:context:
来计算高度而不使用标签。