jjzjj

ios - 带有 SupplementaryView 固定在顶部的 JSQMessagesViewController

coder 2024-01-30 原文

我已经覆盖了 collectionView:viewForSupplementaryElementOfKind:atIndexPath: 以快速显示我的 JSQMessagesViewController 子类的标题 View 。这非常有效,但标题 View 会随着聊天滚动,我希望将其固定在顶部。

Apple 在 iOS 9 中引入了 sectionHeadersPinToVisibleBounds 属性,这应该有固定的标题 View ,但由于某些原因,这在 JSQMessagesViewController 子类中不起作用。我已经添加了 self.collectionView.collectionViewLayout.sectionHeadersPinToVisibleBounds = true 查看DidLoad()

我尝试的另一个解决方案(来自这篇文章 How to make Supplementary View float in UICollectionView as Section Headers do in UITableView plain style )是将 JSQMessagesCollectionViewFlowLayout 子类化,这几乎奏效了。向上滑动时,标题 View 固定在顶部,聊天消息被推送到标题 View 下方。但是,当我尝试向下滑动时,当应用程序崩溃时,我在控制台中收到以下错误:

*** Assertion failure in -[UICollectionViewData validateLayoutInRect:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3512.60.12/UICollectionViewData.m:408
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'layout attributes for supplementary item at index path (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}) changed from <JSQMessagesCollectionViewLayoutAttributes: 0x13f65a730> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 414 100); zIndex = 10;  to <JSQMessagesCollectionViewLayoutAttributes: 0x13f679920> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 10; 414 100); zIndex = 1024;  without invalidating the layout'

有没有人以同样的方式为 JSQMessagesViewController 成功修复标题 View ?

我改变的方法是 shouldInvalidateLayoutForBoundsChange: 我返回 YES 的地方,

和 layoutAttributesForElementsInRect: 我改为:

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *attributesInRect = [[super layoutAttributesForElementsInRect:rect] mutableCopy];

//    if (self.springinessEnabled) {
//        NSMutableArray *attributesInRectCopy = [attributesInRect mutableCopy];
//        NSArray *dynamicAttributes = [self.dynamicAnimator itemsInRect:rect];
//        
//        //  avoid duplicate attributes
//        //  use dynamic animator attribute item instead of regular item, if it exists
//        for (UICollectionViewLayoutAttributes *eachItem in attributesInRect) {
//            
//            for (UICollectionViewLayoutAttributes *eachDynamicItem in dynamicAttributes) {
//                if ([eachItem.indexPath isEqual:eachDynamicItem.indexPath]
//                    && eachItem.representedElementCategory == eachDynamicItem.representedElementCategory) {
//                    
//                    [attributesInRectCopy removeObject:eachItem];
//                    [attributesInRectCopy addObject:eachDynamicItem];
//                    continue;
//                }
//            }
//        }
//        
//        attributesInRect = attributesInRectCopy;
//    }
    UICollectionView * const cv = self.collectionView;
    CGPoint const contentOffset = cv.contentOffset;
    NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet];


    for (UICollectionViewLayoutAttributes *layoutAttributes in attributesInRect) {
        if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
            [missingSections removeIndex:layoutAttributes.indexPath.section];
        }
    }

    [attributesInRect enumerateObjectsUsingBlock:^(JSQMessagesCollectionViewLayoutAttributes *attributesItem, NSUInteger idx, BOOL *stop) {
        if (attributesItem.representedElementCategory == UICollectionElementCategoryCell) {
            [self jsq_configureMessageCellLayoutAttributes:attributesItem];
            [missingSections addIndex:attributesItem.indexPath.section];
        }
        else {
            attributesItem.zIndex = -1;
        }
    }];

    [missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
        UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
        [attributesInRect addObject:layoutAttributes];
    }];

    for (UICollectionViewLayoutAttributes *layoutAttributes in attributesInRect)  {
        if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
            NSInteger section = layoutAttributes.indexPath.section;
            NSInteger numberOfItemsInSection = [cv numberOfItemsInSection:section];
            NSIndexPath *firstCellIndexPath = [NSIndexPath indexPathForItem:0 inSection:section];
            NSIndexPath *lastCellIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section];
            NSIndexPath *firstObjectIndexPath = [NSIndexPath indexPathForItem:0 inSection:section];
            NSIndexPath *lastObjectIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section];
            UICollectionViewLayoutAttributes *firstObjectAttrs;
            UICollectionViewLayoutAttributes *lastObjectAttrs;
            if (numberOfItemsInSection > 0) {
                firstObjectAttrs = [self layoutAttributesForItemAtIndexPath:firstObjectIndexPath];
                lastObjectAttrs = [self layoutAttributesForItemAtIndexPath:lastObjectIndexPath];

            } else {
                firstObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:firstObjectIndexPath];
                lastObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter atIndexPath:lastObjectIndexPath];
            }
            CGFloat headerHeight = CGRectGetHeight(layoutAttributes.frame);
            CGPoint origin = layoutAttributes.frame.origin;
            origin.y = MIN(MAX(contentOffset.y + cv.contentInset.top, (CGRectGetMinY(firstObjectAttrs.frame) - headerHeight)), (CGRectGetMaxY(lastObjectAttrs.frame) - headerHeight));
            layoutAttributes.zIndex = 1024;
            layoutAttributes.frame = (CGRect) {
                .origin = origin, .size = layoutAttributes.frame.size
            };
        }
    }



    return attributesInRect;
}

最佳答案

我上面给出的代码是可以的。导致崩溃的原因我认为是因为我在我的 JSQMessagesViewController 子类中覆盖了 collectionView:collectionViewLayout:referenceSizeForHeaderInSection: 函数。我注释掉了那个函数,而是在 JSQMessagesLoadEarlierHeaderViewkJSQMessagesLoadEarlierHeaderViewHeight 常量中设置高度。是的,我劫持了 LoadEarlierHeaderView 以显示我自己的自定义标题 View 。

现在,标题 View 固定在顶部,显示我的自定义标题 View ,并且没有任何崩溃。

关于ios - 带有 SupplementaryView 固定在顶部的 JSQMessagesViewController,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37499713/

有关ios - 带有 SupplementaryView 固定在顶部的 JSQMessagesViewController的更多相关文章

  1. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  2. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  3. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  4. ruby-on-rails - 带有 Zeus 的 RSpec 3.1,我应该在 spec_helper 中要求 'rspec/rails' 吗? - 2

    使用rspec-rails3.0+,测试设置分为spec_helper和rails_helper我注意到生成的spec_helper不需要'rspec/rails'。这会导致zeus崩溃:spec_helper.rb:5:in`':undefinedmethod`configure'forRSpec:Module(NoMethodError)对thisissue最常见的回应是需要'rspec/rails'。但这是否会破坏仅使用spec_helper拆分rails规范和PORO规范的全部目的?或者这无关紧要,因为Zeus无论如何都会预加载Rails?我应该在我的spec_helper中做

  5. ruby - 在 Ruby 中将整数格式化为固定长度的字符串 - 2

    有没有一种简单的方法可以将给定的整数格式化为具有固定长度和前导零的字符串?#convertnumberstostringsoffixedlength3[1,12,123,1234].map{|e|???}=>["001","012","123","234"]我找到了解决方案,但也许还有更聪明的方法。format('%03d',e)[-3..-1] 最佳答案 如何使用%1000而不是进行字符串操作来获取最后三位数字?[1,12,123,1234].map{|e|format('%03d',e%1000)}更新:根据theTinMan的

  6. Ruby:如何使用带有散列的 'send' 方法调用方法? - 2

    假设我有一个类A,里面有一些方法。假设stringmethodName是这些方法之一,我已经知道我想给它什么参数。它们在散列中{'param1'=>value1,'param2'=>value2}所以我有:params={'param1'=>value1,'param2'=>value2}a=A.new()a.send(methodName,value1,value2)#callmethodnamewithbothparams我希望能够通过传递我的哈希以某种方式调用该方法。这可能吗? 最佳答案 确保methodName是一个符号,而

  7. ruby-on-rails - 带有 Pry 的 Rails 控制台 - 2

    当我进入Rails控制台时,我已将pry设置为加载代替irb。我找不到该页面或不记得如何将其恢复为默认行为,因为它似乎干扰了我的Rubymine调试器。有什么建议吗? 最佳答案 我刚发现问题,pry-railsgem。忘记了它的目的是让“railsconsole”打开pry。 关于ruby-on-rails-带有Pry的Rails控制台,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/question

  8. ruby - 为什么不能使用类IO的实例方法noecho? - 2

    print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上

  9. 带有 attr_accessor 的类上的 Ruby instance_eval - 2

    我了解instance_eval和class_eval之间的基本区别。我在玩弄时发现的是一些涉及attr_accessor的奇怪东西。这是一个例子:A=Class.newA.class_eval{attr_accessor:x}a=A.newa.x="x"a.x=>"x"#...expectedA.instance_eval{attr_accessor:y}A.y="y"=>NoMethodError:undefinedmethod`y='forA:Classa.y="y"=>"y"#WHATTT?这是怎么回事:instance_eval没有访问我们的A类(对象)然后它实际上将它添加到

  10. ruby-on-rails - Rails 渲染带有驼峰命名法的 json 对象 - 2

    我在一个简单的RailsAPI中有以下Controller代码:classApi::V1::AccountsControllerehead:not_foundendendend问题在于,生成的json具有以下格式:{id:2,name:'Simpleaccount',cash_flows:[{id:1,amount:34.3,description:'simpledescription'},{id:2,amount:1.12,description:'otherdescription'}]}我需要我生成的json是camelCase('cashFlows'而不是'cash_flows'

随机推荐