当前位置: 首页 > 知识库问答 >
问题:

屏幕外UITableViewCell(用于大小计算)不尊重大小类?

仲孙鸿畴
2023-03-14

我在UITableView中使用Auto Layout和size类,其中单元格根据其内容自调整大小。为此,我使用的方法是,对于每种类型的单元格,您都要保留该单元格的屏幕外实例,并在其上使用systemLayoutSizeFitting Size来确定正确的行高——这种方法在StackOverflow帖子和其他地方得到了很好的解释。

在我开始使用大小班级之前,这种方法非常有效。具体来说,我为常规宽度布局中的文本定义了不同的边距约束常数,因此iPad上的文本周围有更多的空白。这给了我以下结果。

看起来新的约束集正在被满足(有更多的空格),但是行高计算仍然返回与没有应用特定于大小类的约束的单元格相同的值。屏幕外单元格中布局过程的某些部分没有考虑窗口的大小类。

现在我想这可能是因为屏幕外视图没有超级视图或窗口,因此在发生系统LayoutSizeFittingSize调用时,它没有任何大小类特征可以引用(即使它似乎确实使用了调整后的边距约束)。我现在通过在创建UIWindow后将屏幕外大小调整单元格添加为UIWindow的子视图来解决此问题,从而获得所需的结果:

下面是我在代码中执行的操作:

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    let contentItem = content[indexPath.item]

    if let contentType = contentItem["type"] {
        // Get or create the cached layout cell for this cell type.
        if layoutCellCache.indexForKey(contentType) == nil {
            if let cellIdentifier = CellIdentifiers[contentType] {
                if var cachedLayoutCell = dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell {                        
                    UIApplication.sharedApplication().keyWindow?.addSubview(cachedLayoutCell)
                    cachedLayoutCell.hidden = true
                    layoutCellCache[contentType] = cachedLayoutCell
                }
            }
        }

        if let cachedLayoutCell = layoutCellCache[contentType] {
            // Configure the layout cell with the requested cell's content.
            configureCell(cachedLayoutCell, withContentItem: contentItem)

            // Perform layout on the cached cell and determine best fitting content height.
            cachedLayoutCell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), 0);
            cachedLayoutCell.setNeedsLayout()
            cachedLayoutCell.layoutIfNeeded()

            return cachedLayoutCell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
        }
    }

    fatalError("not enough information to determine cell height for item \(indexPath.item).")
    return 0
}

对我来说,在不应该被绘制的窗口中添加视图就像是一种黑客行为。有没有办法让UIViews完全采用窗口的size类,即使它们当前不在视图层次结构中?还是我遗漏了什么?谢了。

共有2个答案

庾勇军
2023-03-14

在转向使用大小类以使iPad与iPhone等上更容易更改字体大小之后,我花了几天时间。

问题的根源似乎是可重用的单元格与身份识别器的取消排队:返回一个没有超级视图的单元格,它从中获取其 UITrait 收集。另一方面,可排序可重用单元格使用标识符:forIndexPath:返回一个单元格,其超级视图是 UITableView包装视图

我向Apple提出了一个错误报告,因为他们没有扩展此方法以支持大小类;似乎没有记录如何在iOS7上处理大小类。当您向UITableView发送消息要求一个单元格时,它应该返回一个反映您将消息发送到的表的大小类的消息。这是dequeueReusableCellWithIdfier: forIndexPath:的情况。

我还注意到,在尝试使用新的自动布局机制时,您经常需要在viewDidAppear中重新加载表格:以使新机制正常工作。如果没有这个,我看到使用iOS7方法时遇到的相同问题。

据我所知,在相同的代码中使用iOS8的自动布局和iOS7的旧机制似乎是不可能的。

现在,我不得不通过添加原型单元格作为表的子视图,计算大小,然后删除它来解决这个问题:

UITableViewCell *prototype=nil;
CGFloat prototypeHeight=0.0;

prototype=[self.tableView dequeueReusableCellWithIdentifier:@"SideMenuCellIdentifier"];

// Check for when the prototype cell has no parent view from 
// which to inherit size class related constraints.
BOOL added=FALSE;
if (prototype.superview == nil){
   [self.tableView addSubview:prototype];
   added=TRUE;
}

<snip ... Setup prototype cell>

[prototype setNeedsLayout];
[prototype layoutIfNeeded];
CGSize size = [prototype.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
prototypeHeight=size.height+1; // Add one for separator

// Remove the cell if added. Leaves it when in iOS7.
if (added){
  [prototype removeFromSuperview];
}

与大小类相关的设置似乎是通过< code>UITraitCollection控制的,该集合是< code>UIViewController的只读属性。对于iOS7向后兼容性,这似乎是由构建系统处理的,作为一些限制的变通办法。也就是说,在iOS7上,您不能访问< code>traitCollection属性,但在iOS8中可以。

鉴于与序列图像板中的视图控制器的紧密耦合以及向后兼容性的工作原理,看起来原型单元必须位于您在 Xcode 中定义的视图控制器的层次结构中。

这里有一个关于这个的讨论:< br > Xcode 6自适应ui如何向后兼容iOS 7和iOS 6?

欧阳英彦
2023-03-14

苹果现在不鼓励覆盖性特征收集。请考虑使用其他解决方法。从文档中:

直接使用特征集合属性。不要覆盖它。不要提供自定义实现。

现有答案很棒。它解释说,问题在于:

  • -可重用单元格与标识符取消排队:返回没有有效单元格的单元格
  • 细胞性状收集只读的

建议的解决方法是将单元格临时添加到表视图中。但是,如果我们处于 -viewDidLoad 中,这不起作用,其中表视图的特征集合,视图控制器的视图,甚至视图控制器本身,尚无效。

在这里,我提出了另一种解决方法,即覆盖单元格的traitCollection。为此:

>

  • 为单元格创建自定义的 UITable 视图单元格子类(您可能已经这样做了)。

    在自定义子类中,添加一个-(UITraitCollection*)traitCollection方法,该方法覆盖traitCollection属性的getter。现在,您可以返回任何您喜欢的有效UITraitCollection。这是一个示例实现:

    // Override getter of traitCollection property
    // https://stackoverflow.com/a/28514006/1402846
    - (UITraitCollection *)traitCollection
    {
        // Return original value if valid.
        UITraitCollection* originalTraitCollection = [super traitCollection];
        if(originalTraitCollection && originalTraitCollection.userInterfaceIdiom != UIUserInterfaceIdiomUnspecified)
        {
            return originalTraitCollection;
        }
    
        // Return trait collection from UIScreen.
        return [UIScreen mainScreen].traitCollection;
    }
    

    或者,您可以返回一个合适的< code>UITraitCollection,该集合是使用它的任何一个create方法创建的,例如:

    + (UITraitCollection *)traitCollectionWithDisplayScale:(CGFloat)scale
    + (UITraitCollection *)traitCollectionWithTraitsFromCollections:(NSArray *)traitCollections
    + (UITraitCollection *)traitCollectionWithUserInterfaceIdiom:(UIUserInterfaceIdiom)idiom
    + (UITraitCollection *)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
    + (UITraitCollection *)traitCollectionWithVerticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
    

    或者,您甚至可以通过以下方式使其更加灵活:

    // Override getter of traitCollection property
    // https://stackoverflow.com/a/28514006/1402846
    - (UITraitCollection *)traitCollection
    {
        // Return overridingTraitCollection if not nil,
        // or [super traitCollection] otherwise.
        // overridingTraitCollection is a writable property
        return self.overridingTraitCollection ?: [super traitCollection];
    }
    

    此解决方法与iOS 7兼容,因为traitCollection属性是在iOS 8中定义的,因此,在iOS 7中,没有人会调用它的getter,因此我们的重写方法也是如此。

  •  类似资料:
    • 嘿,伙计们,我只是想问一下,当我们显示一个背景图片在我们的活动,我们把图片放在可绘制的文件夹与子不同的文件夹,像 并且每个文件夹都有一个特定的密度和尺寸,这样它就可以支持多个设备等。 我想问一下,如果我的图片尺寸是3000*3000,我是否需要为每个文件夹修改我的图片 首先,我必须修改它为720*1280的xhdpi,然后把它放在layout_xhdpi文件夹,然后我修改它为hdpi,即400*8

    • 所以我是一个计算机科学的学生,我已经完成了我的第一年。我想创建一个简单的程序,我意识到我已经厌倦了使用无布局; 给每一个单独的组件添加界限是如此的烦人。嗯,我一直在大量使用组件和,这使我的工作变得更容易了。但我已经厌倦了。 我非常关心我制作的GUI的外观,在开始添加代码的功能之前,我几乎用了一半的时间来编程使GUI看起来很好。通过不使用布局并添加边界,我被迫使用,因为如果更改JFrame的大小,这

    • 我是一个新的android开发和我正在尝试创建和应用程序与网格菜单,菜单。 当前依赖项:compile fileTree(目录:“libs”,include:['*.jar'])testCompile“junit:junit:4.12”编译组:“org.apache.httpcomponents”,名称:“httpclient-android”,版本:“4.3.5.1”编译组:“cz.mseber

    • 我正在尝试以编程方式添加ImageView,但不要在不同的屏幕上制作相同的大小。我尝试了许多比例代码,但没有任何好的结果。 屏幕:屏幕图像 这是我的代码:

    • 我正在使用android gif绘图库在我的android应用程序中显示gif图像。正如您所知,gif图像应该位于文件夹中,而不是位于文件夹中,因此android无法自动在不同大小之间切换。 我如何处理不同屏幕尺寸的问题?

    • 问题内容: 我使用myEclipse Matisse创建了Java GUI。当我的屏幕分辨率为1024x768时,它可以正常工作,但是当我更改分辨率时,我的GUI无法正常工作。我希望我的GUI窗口应根据屏幕分辨率重新调整大小,我正在扩展JFrame以创建主窗口。 这是行不通的,我该怎么做,设置硬编码的大小或使用ToolKit使用的帧大小保持不变。 问题答案: 您正在呼叫,它会更改框架大小,以便恰好