除非您与 UITableViewController 交互,否则 UIImageView 类别不会更新

Posted

技术标签:

【中文标题】除非您与 UITableViewController 交互,否则 UIImageView 类别不会更新【英文标题】:UIImageView category does not update unless you interact with UITableViewController 【发布时间】:2014-11-07 10:33:12 【问题描述】:

要查看问题的演示和代码,请have a look at the following 60 second video.

Here is a simple project demonstrating the problem。 挑战一下,看看你是否可以在运行时使用我创建的类别使图像出现而无需在运行时触摸 tableviewcontroller。

我创建了一个简单的UIImageView 类别,我在其中公开了一个imageURL 属性,当设置该属性时应该开始异步下载。调用完成块时,我将类的image 属性设置为下载的图像。

然后我有一个继承自 UITableViewController 的类,我在其中导入了我的自定义 UIImageView 类别。我通过设置单元格的 imageview imageURL 属性在 willDisplayCell: 方法中开始下载。图像是异步下载并设置的,但除非我将单元格从表格中滚动出去,或者单击单元格本身,否则单元格的图像视图不会更新。

我尝试过诸如[self setNeedsDisplay]; 之类的方法以及其他一些方法,例如在主线程上执行图像设置,但都无济于事。

有人可以看看,让我知道我应该做什么吗?


一些代码:

UIImageView+AsyncDLImageView.h

#import <UIKit/UIKit.h>
@interface UIImageView (AsyncDLImageView)
@property (nonatomic, retain) NSURL *imageURL;
@end

UIImageView+AsyncDLImageView.m

#import "UIImageView+AsyncDLImageView.h"
@implementation UIImageView (AsyncDLImageView)
- (void)setImageURL:(NSURL *)imageURL
    [self loadImageWithURL:imageURL completionHandler:^(UIImage *image, NSError *error) 
        if(!error) 
            NSLog(@"image has been set: %@", image);
            self.image = image;
        
    ];

-(NSURL *)imageURL
    return self.imageURL;

-(void)loadImageWithURL:(NSURL*)url completionHandler:(void(^)(UIImage *image, NSError *error))completionBlock
    NSLog(@"set image url request called: %@", [url absoluteString]);

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) 
                               if ( !error )
                               
                                   NSLog(@"no error");
                                   UIImage *image = [[UIImage alloc] initWithData:data];
                                   completionBlock(image, nil);
                                else
                                   completionBlock(nil, error);
                                   NSLog(@"error");
                               

                           ];

【问题讨论】:

问题的一部分可能是图像在后台线程中更新,并且与 UI 的任何交互都必须在主线程上? @Flexicoder 一个更新你知道在完成处理程序中它说"image has been set" 添加这个NSLog(@"frame: %@", NSStringFromCGRect(self.frame)); 你会首先看到,框架返回:0, 0, 0, 0,当你与tableviewcontroller,imageview 框架会自动更新到正确的大小...... lol dl.dropboxusercontent.com/u/13214955/Asynch%20DL%20Image.zip 这里是下载项目... 抱歉不喜欢下载项目 - 尝试将 self.image = image; 更改为 dispatch_async(dispatch_get_main_queue(), ^ self.image = image ); 我能问一下为什么吗?这不是完整的项目,只是一个简单的问题重现,只有 2 个类文件,每个类文件只有几行代码。不过,也许你可以看看这个 71 秒的视频呢? dl.dropboxusercontent.com/u/13214955/… 【参考方案1】:

看起来是因为表格单元格上的 imageView 没有初始化,所以图像不会显示。我添加了一个透明的 placeHolder 图像(制作您想要的图像大小)并在等待图像加载时使用它。 (这对我有用)

所以我将代码更改如下...

DisplayXMLImageDataTVC.m

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    return 1;


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    return [_xmlImageURLArray count];


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
     static NSString *cellIdentifier = @"xmlCellIdentifier";

     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
     if(cell == nil)
         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
        cell.imageView.image = [UIImage imageNamed:@"placeHolder"];
     
    [cell.imageView setImageURL:[_xmlImageURLArray objectAtIndex:indexPath.row]];

    return cell;
 

你的类别类回到原来的样子......

- (void)setImageURL:(NSURL *)imageURL


//    objc_setAssociatedObject(self, @selector(imageURL), imageURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self loadImageWithURL:imageURL completionHandler:^(UIImage *image, NSError *error) 
        if(!error) 
            self.image = image;
        
    ];

-(void)loadImageWithURL:(NSURL*)url completionHandler:(void(^)(UIImage *image, NSError *error))completionBlock
    NSLog(@"set image url request called: %@", [url absoluteString]);

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) 
                               if ( !error )
                               
                                   NSLog(@"no error");
                                   UIImage *image2 = [[UIImage alloc] initWithData:data];

                                   completionBlock(image2, nil);
                                else
                                   completionBlock(nil, error);
                                   NSLog(@"error");
                               

                           ];

【讨论】:

【参考方案2】:

问题似乎是 UITableViewCell 使用 imageView 的初始帧来布局单元格。如果没有设置,那么你会得到一个 0x0 帧,即使你稍后设置图像,帧也不会改变。如果您将其设置为某个占位符图像,那么它会继续使用该占位符图像中的帧,这可能不是您想要的。

一个可能的解决方案是使用自定义 UITableViewCell,它会覆盖 layoutSubviews 并根据您拥有的图像设置框架。并从您的 imageView 类别中,设置图像后,调用 [self setNeedsLayout]。这将导致 layoutSubviews 在下一次重绘时被调用。

你也可以看看这个 - http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/

【讨论】:

这仍然不起作用。虽然最终将框架设置为正确的框架,但图像本身不会显示,除非我再次与之交互。 我用的是 mac,我向你保证,我不是 PC。请下载简单的项目。没有任何病毒。 我们可以继续聊天吗? 我也会尝试从类别中添加对 setNeedsDisplay 的调用,尽管我认为这里不需要它。 当然,我们可以在@radical 上继续聊天。【参考方案3】:

原因是设置图像后不重绘单元格。通常我们在cellForRowAtIndexPath中发送一个图片的异步请求,得到图片后,我们调用单元格setNeedsLayout

因此您可以执行以下操作。

- (void)setImageURL:(NSURL *)imageURL

    UITableViewCell *cell = (UITableViewCell *)self.superview.superview;

    [self loadImageWithURL:imageURL  completionHandler:^(UIImage *image, NSError *error) 
        if(!error) 
            NSLog(@"image has been set: %@", image);
            self.image = image;
            [cell setNeedsLayout];
        
    ];

【讨论】:

这感觉很hackish,并且不会使代码可重用于其他领域。您只能在使用 uitableviewcells 的实例中使用 UIImageView 类别类。 然后另一个选项,添加一个imageView作为单元格的子视图,您可以保留类别。 你可以把这个setter方法的内容放到另一个方法中,暴露在头文件中,因为代码是更新image属性,而不是imageURL属性,setter方法应该是干净的。这样您仍然可以使用该类别。

以上是关于除非您与 UITableViewController 交互,否则 UIImageView 类别不会更新的主要内容,如果未能解决你的问题,请参考以下文章

您将 Rails 应用程序放在服务器的啥位置?您与啥用户一起部署?

SSL证书没有绿锁您与此网站建立的连接并非完全安全解决办法

用钉钉可以玩哪些内容软件

如何呈现UICollectionViewController?

UINavigationController 中的仅横向 UIViewController

UIActionSheet 事件未触发