使用大小类计算屏幕外UITableViewCell的高度

时间:2015-04-15 10:27:19

标签: uitableview autolayout

我一直致力于一组允许UITableView及其相关类使用自动布局和动态类型的类。它基于an answer on Stack Overflow,旨在添加自动布局支持。

到目前为止,它运行良好,但在使用大小类时我遇到了一些问题。第一个与计算高度的表直接相关:

当我创建新的UITableViewCell但未将其添加到任何视图时,尺寸等级为Any x Any,所以当我有了根据尺寸等级更改的某些子视图或约束,它们始终像Any x Any情况一样工作。到目前为止,我非常讨厌的解决方案是创建一个新的UIWindow,我将单元格添加到:{/ p>

UIWindow(frame: UIScreen.mainScreen().applicationFrame)

这项功能正常,但我遇到了一些问题:

  • 我现在正在创建一个似乎效率低下的全新UIWindow对象
  • 必须将每个单元格添加到同一窗口
  • 当屏幕旋转时,尺寸等级可能会改变(即iPhone 6 Plus),因此我需要监听应用程序框架的更改并更新我的窗口框架(尚未实现)

是否有一种简单/更有效的方法可以确保UITableViewCell知道其大小等级,而无需创建新的UIWindow,或者更有效地执行此操作?也许我可以添加一个

大多数当前代码都可以通过GitHub page找到,但最相关的方法是:

DynamicTableViewController

private var cachedClassesForCellReuseIdentifiers = [String : UITableViewCell.Type]()
private var cachedNibsForCellReuseIdentifiers = [String : UINib]()
private var offscreenCellRowsForReuseIdentifiers = [String : UITableViewCell]()

private var offScreenWindow: UIWindow = {
    return UIWindow(frame: UIScreen.mainScreen().applicationFrame)
}()

override public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    // This method is called with an NSMutableIndexPath, which is not compatible with an imutable NSIndexPath,
    // so we create an imutable NSIndexPath to be passed to the following methods
    let imutableIndexPath = NSIndexPath(forRow: indexPath.row, inSection: indexPath.section)

    if let reuseIdentifier = self.cellReuseIdentifierForIndexPath(imutableIndexPath) {
        if let cell = self.cellForReuseIdentifier(reuseIdentifier) {
            self.configureCell(cell, forIndexPath: indexPath)

            if let dynamicCell = cell as? DynamicTableViewCell {
                let height = dynamicCell.heightInTableView(tableView)
                return height
            } else {
                // Fallback for non-DynamicTableViewCell cells
                let size = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
                let cellBoundsHeight = CGRectGetHeight(cell.bounds)
                if size.height > 0 && size.height >= cellBoundsHeight {
                    // +1 for the cell separator
                    return size.height + 1
                } else {
                    // In some situations (such as the content view not having any/enough constraints to get a height), the
                    // size from the systemLayoutSizeFittingSize: will be 0. However, because this can _sometimes_ be intended
                    // (e.g., when adding to a default style; see: DynamicSubtitleTableViewCell), we just return
                    // the height of the cell as-is. This may make some cells look wrong, but overall will also prevent 0 being returned,
                    // hopefully stopping some things from breaking.
                    return cellBoundsHeight + 1
                }
            }
        }
    }

    return UITableViewAutomaticDimension
}

private func cellForReuseIdentifier(reuseIdentifier: String) -> UITableViewCell? {
    if self.offscreenCellRowsForReuseIdentifiers[reuseIdentifier] == nil {
        if let cellClass = self.cachedClassesForCellReuseIdentifiers[reuseIdentifier] {
            let cell = cellClass()

            self.offScreenWindow.addSubview(cell)
            self.offscreenCellRowsForReuseIdentifiers[reuseIdentifier] = cell
        } else if let cellNib = self.cachedNibsForCellReuseIdentifiers[reuseIdentifier] {
            if let cell = cellNib.instantiateWithOwner(nil, options: nil).first as? UITableViewCell {
                self.offScreenWindow.addSubview(cell)
                self.offscreenCellRowsForReuseIdentifiers[reuseIdentifier] = cell
            }
        }
    }
    return self.offscreenCellRowsForReuseIdentifiers[reuseIdentifier]
}

DynamicTableViewCell

public func heightInTableView(tableView: UITableView) -> CGFloat {
    var height: CGFloat!
    if self.calculateHeight {
        self.setNeedsUpdateConstraints()
        self.updateConstraintsIfNeeded()

        self.bounds = CGRectMake(0, 0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(self.bounds))

        self.setNeedsLayout()
        self.layoutIfNeeded()

        let size = self.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
        let boundsHeight = CGRectGetHeight(self.bounds)

        if size.height > 0 && size.height >= boundsHeight {
            // +1 for the cell separator
            height = size.height + 1
        } else {
            // In some situations (such as the content view not having any/enough constraints to get a height), the
            // size from the systemLayoutSizeFittingSize: will be 0. However, because this can _sometimes_ be intended
            // (e.g., when adding to a default style; see: DynamicSubtitleTableViewCell), we just return
            // the height of the cell as-is. This may make some cells look wrong, but overall will also prevent 0 being returned,
            // hopefully stopping some things from breaking.
            height = boundsHeight + 1
        }
    } else {
        height = self.cellHeight
    }

    if height < self.minimumHeight && self.minimumHeight != nil {
        return self.minimumHeight!
    } else {
        return height
    }
}

此解决方案适用于iOS 7和8,未来的解决方案也应如此。此限制也删除了UITraitCollection的使用,因此我没有沿着这条路走下去(我甚至不确定它是否有帮助)

1 个答案:

答案 0 :(得分:0)

使用IOS 8和Autolayout您可以使用内容视图设置估计行高并为内部视图设置约束,以便根据其中的内容进行缩放。这是我的回答Which is the best approach among Autolayout or calculating the height using NSAttributedString, to implement dynamic height of an UITableViewCell?

更新:对于IOS7,您需要计算高度即将到达时您可以在 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath委托方法

#define HEIGHT_FOR_ONE_LINE 15; //define it according to your fonts
#define HEIGHT_FOR_SINGLE_ROWCELL 32;
#pragma mark - Table view data source
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *textToView= [self.yourDataSourceArray objectAtIndex:indexPath.row];
    CGFloat screenWidth=[[UIScreen mainScreen]bounds].size.width;
    CGFloat characterPerPoint= 10;
    CGFloat widthTaken= characterPerPoint * textToView.length;
    NSInteger numberOfLines= ceil(widthTaken/screenWidth);
    CGFloat height=numberOfLines* HEIGHT_FOR_ONE_LINE;
    height = height + HEIGHT_FOR_SINGLE_ROWCELL;

    return height;
}