UICollectionView 重新加载数据无法更新 CollectionView

Posted

技术标签:

【中文标题】UICollectionView 重新加载数据无法更新 CollectionView【英文标题】:UICollectionView Reload Data Not Working to update CollectionView 【发布时间】:2016-01-09 18:54:09 【问题描述】:

更新:

我确实忘记提到 CollectionView 在拆分视图控制器的右侧,而在左侧,我有 tableView。选择行时,我通过了新的馈送地址,并在右侧进行刷新方法。

我有一个从 XML 解析数据的集合视图。当按下某个按钮时,我希望它清除 CollectionView,并从不同的 XML 加载数据,这两者都位于在线。我使用 ASIHTTPRequest 和 GDATAXML 类来帮助我解决这个问题。这是我的代码。我可以通过日志验证 NSMutableArray 已被清除,并成功填充了不同的 XML,但 reloadData 永远不会发生,因为 cellForItem 不会被第二次调用。我哪里搞砸了?

@interface RightViewController ()
@property (nonatomic, strong) IBOutlet UICollectionView *collectionView;

@end

@implementation RightViewController

@synthesize allEntries = _allEntries;
@synthesize feeds = _feeds;
@synthesize queue = _queue;
@synthesize webViewController = _webViewController;
@synthesize activity;
@synthesize webViewController2 = _webViewController2;



@synthesize nameit2;
@synthesize nowplaying;
@synthesize entryofmp3 = _entryofmp3;
@synthesize nameit;
@synthesize progress;
@synthesize lastSelection;
@synthesize backgroundTaskIdentifier, theurl;
@synthesize thetable, thepath, downloadlabel, receivedData, progressbutton;


-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 
    return 1;

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 
    return [_allEntries count];

- (void)loadFirstTime 
       self.allEntries = [NSMutableArray array];

        self.queue = [[NSOperationQueue alloc] init];


    if (_feedAddress == nil) 
        _feedAddress = @"http://www.316apps.com/AIMAPPLETV/AIMSeries.xml";
    
    self.feeds = [NSArray arrayWithObjects:_feedAddress,
                  nil];
    for (NSString *feed in _feeds) 
        NSLog(@"Running Fine");
        NSURL *url = [NSURL URLWithString:feed];
        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
        [request setDelegate:self];
        [_queue addOperation:request];
    
    NSLog(@"Still Good");
    //[self.collectionView reloadData];



-(void)changeFeed 
    [_allEntries removeAllObjects];
    _feedAddress = @"http://www.316apps.com/AIMAPPLETV/AIMCON.xml";
    [self loadFirstTime];
   // self.queue = nil;
    //_request.delegate = nil;
 //   _request.delegate = nil;

  //  [_collectionView reloadData];

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath 
  // [self performSegueWithIdentifier:@"playMovies" sender:self];
     RSSEntry *entry = [_allEntries objectAtIndex:indexPath.row];
    NSString *wifiStreamAddress = entry.articleUrl;
    AVPlayer *player = [[AVPlayer alloc] initWithURL: [NSURL URLWithString: wifiStreamAddress] ];
    AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init];
    playerViewController.player = player;

    // Keep pointers to player and controller
    self.player = player;
    self.playerViewController = playerViewController;



    [self presentViewController: playerViewController animated: true completion: ^
        [self.player play];
    ];




- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

    // Make sure your segue name in storyboard is the same as this line
    if ([[segue identifier] isEqualToString:@"playMovies"])
    
        PlayMovies *userViewController = [segue destinationViewController];

        //if you need to pass data to the next controller do it here
    

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 


    RSSEntry *entry = [_allEntries objectAtIndex:indexPath.row];

    static NSString *cellIdentifier = @"cvCell";

    CVCell *cell = (CVCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];

        [cell.theimage setImageWithURL:[NSURL URLWithString:entry.articleImage] placeholderImage:[UIImage imageNamed:@"icon@2x.png"]];

    cell.theimage.adjustsImageWhenAncestorFocused = YES;
    cell.theimage.clipsToBounds = false;




    return cell;


- (void)viewDidLoad 
        [super viewDidLoad];

    self.allEntries = [NSMutableArray array];
    self.queue = [[NSOperationQueue alloc] init];
        [self loadFirstTime];


-(void) viewWillAppear:(BOOL)animated 



- (void)parseRss:(GDataXMLElement *)rootElement entries:(NSMutableArray *)entries 

    NSArray *channels = [rootElement elementsForName:@"channel"];
    for (GDataXMLElement *channel in channels) 

        NSString *blogTitle = [channel valueForChild:@"title"];

        NSArray *items = [channel elementsForName:@"item"];
        for (GDataXMLElement *item in items) 
            NSString *articleTitle = [item valueForChild:@"title"];
            NSString *articleUrl = [[[[item elementsForName: @"enclosure"] lastObject] attributeForName: @"url"] stringValue];
            NSString *articleDateString = [item valueForChild:@"pubDate"];
            NSString *picture = [[[[item elementsForName: @"itunes:image"] lastObject] attributeForName: @"href"] stringValue];
            NSDate *articleDate = [NSDate dateFromInternetDateTimeString:articleDateString formatHint:DateFormatHintRFC822];
            RSSEntry *entry = [[RSSEntry alloc] initWithBlogTitle:blogTitle
                                                      articleTitle:articleTitle
                                                        articleUrl:articleUrl
                                                       articleDate:articleDate
                                                      articleImage:picture];


            [entries addObject:entry];
            NSLog(@"%@", entries);
        
    
     [self.collectionView reloadData];


- (void)parseFeed:(GDataXMLElement *)rootElement entries:(NSMutableArray *)entries 

    if ([rootElement.name compare:@"rss"] == NSOrderedSame) 
        [self parseRss:rootElement entries:entries];
     else if ([rootElement.name compare:@"feed"] == NSOrderedSame) 
       // [self parseAtom:rootElement entries:entries];
     else 
        NSLog(@"Unsupported root element: %@", rootElement.name);
    


- (void)requestFinished:(ASIHTTPRequest *)request 
    NSLog(@"Request Finished");
    [_queue addOperationWithBlock:^

        NSError *error;
        GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:[request responseData]
                                                               options:0 error:&error];
        if (doc == nil) 
            NSLog(@"Failed to parse %@", request.url);
         else 

            NSMutableArray *entries = [NSMutableArray array];
            [self parseFeed:doc.rootElement entries:entries];

            [[NSOperationQueue mainQueue] addOperationWithBlock:^

                for (RSSEntry *entry in entries) 

                    int insertIdx = [_allEntries indexForInsertingObject:entry sortedUsingBlock:^(id a, id b) 
                        RSSEntry *entry1 = (RSSEntry *) a;
                        RSSEntry *entry2 = (RSSEntry *) b;
                        return [entry1.articleDate compare:entry2.articleDate];
                    ];
                                       [_allEntries insertObject:entry atIndex:insertIdx];


                

            ];

        
    ];



- (void)requestFailed:(ASIHTTPRequest *)request 
    NSError *error = [request error];
    NSLog(@"Error: %@", error);
    [self loadFirstTime];



- (void)didReceiveMemoryWarning

    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.


@end

第二次更新:

我正在慢慢取得进展。认为这是因为我没有设置 Segue。我从左侧的TableView 的Cell 到Collection View 的View Controller 设置了一个segue 并设置为ShowDetail,并命名它。然后在 LeftViewController 中:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    if (indexPath.row == 1) 
        [self performSegueWithIdentifier:@"doingIt" sender:self];

    

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

    // Make sure your segue name in storyboard is the same as this line
    if ([[segue identifier] isEqualToString:@"doingIt"])
    

        self.rightViewController = [[RightViewController alloc] init];
                [self.rightViewController refresheyc];

        //if you need to pass data to the next controller do it here
    

在 RightViewController 我有:

-(void)refresheyc 
    NSLog(@"This Sucks");
    [self.allEntries removeAllObjects];
    [self.collectionView reloadData];
    self.allEntries = nil;
    self.queue = nil;
    self.allEntries = [NSMutableArray array];

    self.queue = [[NSOperationQueue alloc] init];




    self.feeds = [NSArray arrayWithObjects:@"http://www.316apps.com/AIMAPPLETV/AIMCON.xml",
                  nil];
    for (NSString *feed in _feeds) 
        NSURL *url = [NSURL URLWithString:feed];
        NSLog(@"URL%@", url);
        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
        [request setDelegate:self];
        [_queue addOperation:request];
    


CollectionView 中的单元格立即消失,但几秒钟后,它们都恢复原样,根本没有更改 XML,但我明白了:

2016-01-09 14:37:31.542 AIM-TV[7965:522091] This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes.  This will cause an exception in a future release.
 Stack:(
    0   CoreFoundation                      0x0000000109fa6ff5 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010d45cdeb objc_exception_throw + 48
    2   CoreFoundation                      0x0000000109fa6f2d +[NSException raise:format:] + 205
    3   Foundation                          0x000000010aa96341 _AssertAutolayoutOnMainThreadOnly + 79
    4   Foundation                          0x000000010a8f6d6e -[NSISEngine withBehaviors:performModifications:] + 31
    5   UIKit                               0x000000010c804eb8 -[UIView(AdditionalLayoutSupport) _withAutomaticEngineOptimizationDisabledIfEngineExists:] + 58
    6   UIKit                               0x000000010c804854 __57-[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:]_block_invoke + 646
    7   UIKit                               0x000000010c804ec1 -[UIView(AdditionalLayoutSupport) _withAutomaticEngineOptimizationDisabledIfEngineExists:] + 67
    8   UIKit                               0x000000010c804589 -[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:] + 242
    9   UIKit                               0x000000010c80405d -[UIView(AdditionalLayoutSupport) _initializeHostedLayoutEngine] + 649
    10  UIKit                               0x000000010c7f652a -[UIView(UIConstraintBasedLayout) _initializeLayoutEngine] + 152
    11  UIKit                               0x000000010c7f652a -[UIView(UIConstraintBasedLayout) _initializeLayoutEngine] + 152
    12  UIKit                               0x000000010c804e24 -[UIView(AdditionalLayoutSupport) _layoutEngineCreateIfNecessary] + 62
    13  UIKit                               0x000000010c7f732b -[UIView(UIConstraintBasedLayout) _tryToAddConstraint:roundingAdjustment:mutuallyExclusiveConstraints:] + 126
    14  UIKit                               0x000000010c7f75ef -[UIView(UIConstraintBasedLayout) _addConstraint:] + 274
    15  UIKit                               0x000000010c7fd247 -[UIView(UIConstraintBasedLayout) _updateContentSizeConstraints] + 779
    16  UIKit                               0x000000010c806270 -[UIView(AdditionalLayoutSupport) updateConstraints] + 224
    17  UIKit                               0x000000010c80559b -[UIView(AdditionalLayoutSupport) _internalUpdateConstraintsIfNeededAccumulatingViewsNeedingSecondPassAndViewsNeedingBaselineUpdate:forSecondPass:] + 550
    18  UIKit                               0x000000010c805866 -[UIView(AdditionalLayoutSupport) _updateConstraintsIfNeededAccumulatingViewsNeedingSecondPassAndViewsNeedingBaselineUpdate:forSecondPass:] + 198
    19  UIKit                               0x000000010c8054aa -[UIView(AdditionalLayoutSupport) _internalUpdateConstraintsIfNeededAccumulatingViewsNeedingSecondPassAndViewsNeedingBaselineUpdate:forSecondPass:] + 309
    20  UIKit                               0x000000010c805866 -[UIView(AdditionalLayoutSupport) _updateConstraintsIfNeededAccumulatingViewsNeedingSecondPassAndViewsNeedingBaselineUpdate:forSecondPass:] + 198
    21  UIKit                               0x000000010c8054aa -[UIView(AdditionalLayoutSupport) _internalUpdateConstraintsIfNeededAccumulatingViewsNeedingSecondPassAndViewsNeedingBaselineUpdate:forSecondPass:] + 309
    22  UIKit                               0x000000010c804ec1 -[UIView(AdditionalLayoutSupport) _withAutomaticEngineOptimizationDisabledIfEngineExists:] + 67
    23  UIKit                               0x000000010c80583a -[UIView(AdditionalLayoutSupport) _updateConstraintsIfNeededAccumulatingViewsNeedingSecondPassAndViewsNeedingBaselineUpdate:forSecondPass:] + 154
    24  UIKit                               0x000000010c805e9e __60-[UIView(AdditionalLayoutSupport) updateConstraintsIfNeeded]_block_invoke + 98
    25  UIKit

【问题讨论】:

您指定collectionView 委托RightViewController 吗? 是的,在我的 Storyboard 中,我将我在顶部声明的 collectionView 的出口拖到了 CollectionView,然后在引用 Outlets drug over for dataSource 和 delegate 下。它第一次加载collectionView就很好,但是之后试图让它重新加载时什么都没有发生。 @RatulSharker @user717452: 你的refreshfree 方法在哪里? @MidhunMP 我的错。这就是我之前命名的,我在我的帖子中重命名了该方法,但不是我调用它的地方。我已经更新了我的 OP 以反映这一点。 refresh free 和 loadFirstTime 是一样的。 我已经尝试了几天,但无济于事。我确实忘记提到 CollectionView 在拆分视图控制器的右侧,而在左侧,我有 tableView。选择一行时,我通过了新的馈送地址,并在右侧运行刷新方法。 span> 【参考方案1】:

问题真的很奇怪,但它对我有用,我在动画块中添加了更新约束代码,

UIView.animate(withDuration: 0.5, animations: () -> Void in

    //update constraints here

    self.view.layoutIfNeeded() 
    self.collectionView.layoutIfNeeded()

)

self.collectionView.reloadData()

【讨论】:

【参考方案2】:

您无法在后台线程中更新 UI 元素。 [self.collectionView reloadData] 从 parseRss 调用,它本身称为 parseFeed 表单,由 _queue 上的操作调用。如果 _queue 没有碰巧被主线程处理,你会在 collectionView 中得到不可预知的结果,并且更有可能崩溃。

您应该将对 [self.collectionView reloadData] 的调用分派到主线程。

dispatch_async(dispatch_get_main_queue(),  self.collectionView.reloadData() )

(这是Swift语法,我对Obj-C不熟悉,抱歉)

【讨论】:

我试过了,但对我没用。我正在使用 Swift 3。 在 swift 中我遇到了类似的奇怪问题,但它的一些工作原理 我在这里发布了我的问题:CollectionViewController.reloadData() with data from a Parse Server【参考方案3】:

正如Alain 的回答所述,这是因为 UI 更改需要由 主线程 处理。我看到这个问题是使用 Objective C 发布的,并用 Swift 回答,所以我想我会发布 Objective C 版本以防万一有人需要它。

dispatch_async(dispatch_get_main_queue(), ^ 
    [self.collectionView reloadData];
);

【讨论】:

【参考方案4】:

除了@Alain T. 提到的对 reloadData() 的调用,请确保您的项目高度设置正确。当 itemHeight 为零或某个非常大的值时,您可能会看到 collectionView(numberOfItemsInSection) and numberOfSections() 按预期调用但不是你的 collectionView( cellForItemAt)

因此,请确保您已通过调用设置项目高度:

collectionView(collectionView, layout, sizeForItemAt)

【讨论】:

以上是关于UICollectionView 重新加载数据无法更新 CollectionView的主要内容,如果未能解决你的问题,请参考以下文章

重新加载后无法取消选择 UICollectionView 单元格

无法在按钮点击时重新加载 UICollectionView

tableview 单元格内的集合视图重新加载数据

UICollectionView:重新加载数据,不删除单元内动画

UICollectionView如何从委托中重新加载数据

UICollectionView 重新加载数据不会更新单元格