UIScrollView setNeedsDisplay 不工作,还是别的啥?

Posted

技术标签:

【中文标题】UIScrollView setNeedsDisplay 不工作,还是别的啥?【英文标题】:UIScrollView setNeedsDisplay not working, or something else?UIScrollView setNeedsDisplay 不工作,还是别的什么? 【发布时间】:2012-02-07 18:17:07 【问题描述】:

问题下方给出了这个问题的最终解决方案

我的目标是创建一个图像选择器,它最初在滚动视图内创建每个图像视图,然后将该图像视图的引用传递给一个方法,该方法将使用适当的图像异步更新 imageview.image .

这很好用。

我遇到的问题是滚动视图只显示图像视图在方法结束时批量显示,而我希望它们按原样逐个打印已创建(图像可用与否)。

如果我触摸滚动视图进行滚动,它会按照我想要的方式工作,但如果我完全不触摸,屏幕会保持白色,直到所有图像,而不仅仅是图像视图,已完成加载。

我尝试将 setNeedsDisplay 放置在任何地方,适用于每个视图,包括 self.view、scrollview 和每个单独的 imageview。怎么回事?

- (void)viewDidLoad 
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
     dispatch_async(queue, ^
          [self setGridView];
     );


- (void)setGridView 
    // begin loading images
    NSString * retina = ([[MVProject sharedInstance] settings_retina]) ? @"2" : @"";

    CGRect scrollframe = CGRectMake(0, 0, 300, 275);
    UIScrollView * newscrollview;
    newscrollview = [[UIScrollView alloc] initWithFrame:scrollframe];
    newscrollview.scrollEnabled = YES;
    newscrollview.delegate = self;
    [newscrollview setContentSize:CGSizeMake(300, (((ceilf([[NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"Flickr_Dictionary"]] count]/4)) * (58+15)) + 15 + 58 + 15))];
    [picsMVContentCell.contentView addSubview:newscrollview];

    float currentx = 11;
    float currenty = 17;
    NSInteger index = 0;

    for (NSDictionary * d in [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"Flickr_Dictionary"]]) 

        // create button
        UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.frame = CGRectMake(currentx, currenty, 58, 58);
        button.imageView.backgroundColor = [UIColor colorWithRed:(241/255.0) green:(241/255.0) blue:(241/255.0) alpha:1];
        button.imageView.contentMode = UIViewContentModeScaleAspectFit;
        button.imageView.layer.cornerRadius = 6;
        button.imageView.layer.masksToBounds = YES;
        button.imageView.accessibilityLabel = [d objectForKey:@"id"];
        button.imageView.accessibilityValue = [d objectForKey:@"full"];
        button.imageView.tag = index++;
        [button addTarget:self action:@selector(imageClick:) forControlEvents:UIControlEventTouchUpInside];

        UIImage * image = [MVImage imageWithImage:[UIImage imageNamed:@""] covertToWidth:58.0f covertToHeight:58.0f];
        [button setImage:image forState:UIControlStateNormal];

        [newscrollview addSubview:button];
        [newscrollview bringSubviewToFront:button];
        [newscrollview setNeedsDisplay];

        // calculate next image view position
        currentx += (button.imageView.frame.size.width+15);
        if (currentx >= 289) 
            currentx = 11;
            currenty += (button.imageView.frame.size.height+15);
        

        // set image
        NSString * nsuserdefault = [NSString stringWithFormat:@"Settings_SFThumbs%@_%@", retina, [d objectForKey:@"id"]];
        if ([NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:nsuserdefault]]) 
            MVImage * thumb = [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:nsuserdefault]];
            [button setImage:[MVImage imageWithImage:[[UIImage alloc] initWithData:thumb.data] covertToWidth:58.0f covertToHeight:58.0f] forState:UIControlStateNormal];
         else 
            [MVProject asynchronousImageLoad:button.imageView urlpath:[d objectForKey:@"thumb"] nsuserdefaultpath:nsuserdefault];
        
    

于 2012 年 2 月 7 日下午 3:26 编辑

这些更改修复了与问题中给出的代码相关的所有问题

感谢@Eugene 为我指明了正确的方向

/* ---------- ---------- ---------- ---------- ---------- */

- (void)viewDidLoad 
     [super viewDidLoad];

     [[MVProject sharedInstance] syncLoadSF:0];

    self.newscrollview = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 300, 275)];
    self.newscrollview.scrollEnabled = YES;
    self.newscrollview.delegate = self;
    [self.newscrollview setContentSize:CGSizeMake(300, (((ceilf(([[NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"Flickr_Dictionary"]] count]-1)/4)) * (58+15)) + 15 + 58 + 15))];
    [picsMVContentCell.contentView addSubview:self.newscrollview];

    [self setGridView];


/* ---------- ---------- ---------- ---------- ---------- */

- (void)setGridView 
    NSString * retina = ([[MVProject sharedInstance] settings_retina]) ? @"2" : @"";
    [self.newscrollview setNeedsDisplay];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
    dispatch_async(queue, ^
        NSInteger index = 0;
        for (NSDictionary * d in [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:@"Flickr_Dictionary"]]) 
            // create button
            UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];               
            button.frame = CGRectMake((11 + ((index%4)*(58+15))), (17 + ((floorf(index/4))*(58+15))), 58, 58);
            button.imageView.backgroundColor = [UIColor colorWithRed:(241/255.0) green:(241/255.0) blue:(241/255.0) alpha:1];
            button.imageView.contentMode = UIViewContentModeScaleAspectFit;
            button.imageView.layer.cornerRadius = 3;
            button.imageView.layer.masksToBounds = YES;
            button.imageView.accessibilityLabel = [d objectForKey:@"id"];
            button.imageView.accessibilityValue = [d objectForKey:@"full"];
            button.imageView.tag = index++;
            [button addTarget:self action:@selector(imageClick:) forControlEvents:UIControlEventTouchUpInside];

            dispatch_sync(dispatch_get_main_queue(), ^ 
                [button setImage:[MVImage imageWithImage:[UIImage imageNamed:@""] covertToWidth:58.0f covertToHeight:58.0f] forState:UIControlStateNormal];
                [self.newscrollview addSubview:button];
                [self.newscrollview bringSubviewToFront:button];
                [self.newscrollview setNeedsDisplay];

                // set image
                NSString * nsuserdefault = [NSString stringWithFormat:@"Settings_SFThumbs%@_%@", retina, [d objectForKey:@"id"]];
                if ([NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:nsuserdefault]]) 
                    MVImage * thumb = [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:nsuserdefault]];
                    [button setImage:[MVImage imageWithImage:[[UIImage alloc] initWithData:thumb.data] covertToWidth:58.0f covertToHeight:58.0f] forState:UIControlStateNormal];
                 else 
                    [MVProject asynchronousImageLoad:button.imageView urlpath:[d objectForKey:@"thumb"] nsuserdefaultpath:nsuserdefault];
                
            );
        
    );
    [self.newscrollview setNeedsDisplay];

【问题讨论】:

【参考方案1】:

您不能在后台线程上进行绘图。每次添加子视图时,都应该在主线程中执行此操作。所以你的救命之恩是在同步块中使用你的绘图代码,它在主队列上运行

  dispatch_sync(dispatch_get_main_queue(), ^ 
    [newscrollview addSubview:button];
    [newscrollview bringSubviewToFront:button];
    [newscrollview setNeedsDisplay];

    // calculate next image view position
    currentx += (button.imageView.frame.size.width+15);
    if (currentx >= 289) 
      currentx = 11;
      currenty += (button.imageView.frame.size.height+15);
    

    // set image
    NSString * nsuserdefault = [NSString stringWithFormat:@"Settings_SFThumbs%@_%@", retina, [d objectForKey:@"id"]];
    if ([NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:nsuserdefault]]) 
      MVImage * thumb = [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:nsuserdefault]];
      [button setImage:[MVImage imageWithImage:[[UIImage alloc] initWithData:thumb.data] covertToWidth:58.0f covertToHeight:58.0f] forState:UIControlStateNormal];
     else 
      [MVProject asynchronousImageLoad:button.imageView urlpath:[d objectForKey:@"thumb"] nsuserdefaultpath:nsuserdefault];
    
  );

【讨论】:

【参考方案2】:

您遇到的问题是设置图像。您尝试做的是将所有图像容器添加到滚动视图。然后当图像准备好时将它们加载到它们的容器中。有两种方法可以做到这一点。

首先将所有容器添加到滚动视图。 (您可能需要一些加载指示器或默认图像来显示其加载)

1) 创建一个后台线程来准备图像。当图像准备好后,将其添加到其容器中。 (我发现这种方式工作量更大,而且有点痛苦)

2) 创建自定义容器,让自定义容器在镜像准备好后添加自己的镜像。

例如,我正在从服务器加载图像。所以我创建了 AsyncImage 类,它是 UIImageView 的自定义类。我创建了一个函数,我将图像 url 发送到它并处理其余部分。它创建一个 NSURLConnection 并在加载数据时调用self.image = [UIImage imageWithData:data];

【讨论】:

谢谢,但您所描述的已经通过这条线完成:[MVProject asynchronousImageLoad:button.imageView urlpath:[d objectForKey:@"thumb"] nsuserdefaultpath:nsuserdefault]; 这是我目前正在尝试处理的容器的创建。我不希望 button.imageViews 批量加载(是否带有活动指示器),我希望它们在创建时一个一个地绘制到屏幕上。请参阅上面接受的答案^^

以上是关于UIScrollView setNeedsDisplay 不工作,还是别的啥?的主要内容,如果未能解决你的问题,请参考以下文章

UIScrollView里面的UIScrollView:滚动

问题:UIScrollview 弹跳使父 UIScrollview 弹跳

UIScrollView 嵌入其他 UIScrollView

分页 UIScrollView 内的 UIScrollView

UIScrollView

UIscrollView