I can't change the text of a UILabel or the image in an ImageView, in a children subV

Discussion in 'iOS Programming' started by aneuryzma, May 9, 2011.

  1. aneuryzma, May 9, 2011
    Last edited by a moderator: May 9, 2011

    macrumors newbie

    Joined:
    May 9, 2011
    #1
    I can't change the text of a UILabel or the image in an ImageView, in a children subView. (I don't get any exception, but I just see the original content as specified in UIBuilder).

    The parent is EventsTableViewController:


    Code:
        - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
        {
            static NSString *CellIdentifier = @"Cell";
            
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
            if (cell == nil) {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
            }
            
            NSArray *photoIds = [[NSArray alloc] initWithObjects:@"2654899252",@"2654072649",@"2654072293",nil];
            
            NSEnumerator *e = [photoIds objectEnumerator];
            id object;
            CGFloat offset = 0.0;
            while ((object = [e nextObject])) {
            
                ImageWithCaptionView* imageWithCaptionView = [[[ImageWithCaptionView alloc] init] retain];
                
                offset += 320.0;   
                  
                UIGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
                
                [(UITapGestureRecognizer *)recognizer setNumberOfTouchesRequired:1];
                
                imageWithCaptionView.userInteractionEnabled = YES;
                [imageWithCaptionView addGestureRecognizer:recognizer];
                recognizer.delegate = self;
                [recognizer release];
                    
                dispatch_queue_t downloadQueue = dispatch_queue_create("Flickr downloader in EventsTableView", NULL);
                    
                dispatch_async(downloadQueue, ^{
                    
                    NSData *image = [FlickrFetcher imageDataFromPhotoId:object]; 
        
                    if(image) {
        
                        //imageWithCaptionView.imageView.image = [UIImage imageWithData: image]; 
        
                        imageWithCaptionView.imageView = [[UIImageView alloc] initWithImage:[UIImage  imageWithData: image]]; 
                        
                    }
                });
                   
                dispatch_release(downloadQueue);
                
                [imageWithCaptionView.label setText:@"testBALBALBAL"]; 
                
                [cell addSubview:imageWithCaptionView];
                [imageWithCaptionView release];
                
            }
            
            [photoIds release];
        
            return cell;
        }


    The subview ImageWithCaptionView:

    Code:
        @interface ImageWithCaptionView : UIView {
            IBOutlet UIImageView *imageView;
            IBOutlet UILabel *label;
        }
        
        @property (retain) IBOutlet UIImageView *imageView;
        @property (retain) IBOutlet UILabel *label;
    
    and

    Code:
       @implementation ImageWithCaptionView
        
        @synthesize imageView, label;
        
        -(id)init
        {
            UINib *nib = [[UINib nibWithNibName:NSStringFromClass([ImageWithCaptionView class]) bundle:nil] retain];
        
            NSArray *myArray = [nib instantiateWithOwner:self options:nil];  
        
            for (id currentObject in myArray) {
                if ([currentObject isKindOfClass:[ImageWithCaptionView class]]) {
                    self = (ImageWithCaptionView *) currentObject;
                    break;
                }
            }
            
            [nib release];
            return self;
        }
    thanks
     
  2. jnoxx, May 9, 2011
    Last edited by a moderator: May 9, 2011

    macrumors 65816

    jnoxx

    Joined:
    Dec 29, 2010
    Location:
    Aartselaar // Antwerp // Belgium
    #2
    Without reading through you're code. just from ur title.
    you're doing a setMethod?
    if it's a property, then u should be able to do
    [whateverview setImageView:UIImagepointer];
     
  3. thread starter macrumors newbie

    Joined:
    May 9, 2011
    #3
    Yeah, but I actually only need to change the text in the UILabel rather than creating a new UILAbel (and having to define a new frame...etc).

    Should I add a property for NSString text ? And then, how it is related to the property ?

    thanks
     
  4. macrumors 603

    Joined:
    Aug 9, 2009
    #4
    This -init method never invokes self = [super init];

    This pattern of searching for an alternate self objects seems very strange to me. Where did you see it, and how do you know it works?

    It seems to be some kind of singleton trickery, but it's not a pattern I'm familiar with, so if there's a reference that explains it, please point to it. If it's something you devised yourself, then please explain what it's intended to do.

    If this is a singleton pattern, then it won't work, because you can't have a single UIView shared by multiple parent views. Adding any view as a subview of another view automatically removes it from any original parent view.
     
  5. thread starter macrumors newbie

    Joined:
    May 9, 2011
    #5
    This is not a singleton pattern.

    I want to build a grid.

    I have a UITableView with several rows and for each row I want to load 3 ImageWithCaptionView.

    An ImageWithCaptionView is loaded by xib (I have designed it with a UIImageView and UILabel), and I need to customize these 2 elements for each child in the row.

    I can currently see the imageViewCaptionView and it responds to gestures, but I can't customize the UIImageView and UILabel (even from imageViewCaptionView itself).
     
  6. macrumors 6502

    Joined:
    Jan 20, 2008
    #6
    Have you made sure that you've connected the UILabel and UIImageView to your IBOutlets in IB?

    Edit: Looking through the code that you posted it looks like you have a few memory leaks...
     
  7. thread starter macrumors newbie

    Joined:
    May 9, 2011
    #7
    yeah they are wired.

    The value I can see when I run the app is the original value (the one I wrote in the UI Builder).

    If I try to wire the file owner to them I can see imageView and label are already checked.
     
  8. macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #8
    You've got some weird code there man.

    Nobody uses objectEnumerator anymore. Use fast enumeration instead.

    There's a chance that you can make that init method work but it's definitely a memory leak as written since you ignore the original object. I would write a Factory Method instead. Something like UIButton buttonWithStyle. Your's could be +(id)imageWithCaptionView Move your nib-loading code there and return the new view.

    Your cellForRowAtIndexPath code is odd.You create new imageViews for some reason. That doesn't make sense if your cell already has the imageViews from the nib. Also you create these new imageViews every time the method is called, even if the cell is dequeued. That's wrong and will end up with an unlimited number of imageViews added to your cell.

    I'm a little unclear on the gcd code. Does the image get set on a background thread? If so that's illegal. You need to set the image on the main thread.
     
  9. aneuryzma, May 10, 2011
    Last edited by a moderator: May 10, 2011

    thread starter macrumors newbie

    Joined:
    May 9, 2011
    #9
    First of all, about the enumeration: I've just googled for "iterate array objective-c" and the first result is this one: http://stackoverflow.com/questions/992901/how-do-i-iterate-over-an-nsarray

    As you suggested I've converted to fast enumeration. Is this correct ?
    Code:
    id object;
    for (object in photoIds) { 
    ...
    }

    You wrote:
    Well, in the init method I'm overwriting self with what I load from xib. I couldn't find a lot of documentation about creating views (and not viewControllers) directly from xib.



    Ok, so should I just set the UIImage inside UIImageView instead ? (the commented line above). Anyway it doesn't work, and I don't understand why, if I'm assigning a new image, I still see the original image of the xib file.



    This is a bit unclear to me. I thought the method is called only for a new row visible on screen. You mean I should include the code inside the if (cell == nil) statement ?




    Yeah it is a background thread. I've modified the code:

    Code:
    dispatch_queue_t callerQueue = dispatch_get_current_queue();
            dispatch_queue_t downloadQueue = dispatch_queue_create("Flickr downloader in EventsTableView", NULL);
                
            dispatch_async(downloadQueue, ^{
                
                NSData *image = [FlickrFetcher imageDataFromPhotoId:object]; 
    
                if(image) {
                    
                    dispatch_async(callerQueue, ^{
                        imageWithCaptionView.imageView.image = [UIImage imageWithData: image]; 
                        
                        //imageWithCaptionView.imageView = [[UIImageView alloc] initWithImage:[UIImage  imageWithData: image]];
                    });
                }   
    
            });
               
            dispatch_release(downloadQueue);
    
    By the way, why would you use a Factory Method pattern in this case ? I only have 1 class to instantiate multiple times
     
  10. macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #10
    Usually more like this

    Code:
    for (id object in photoIds) { 
    or

    Code:
    for (UIView* v in photoIds) { 
    The right way to load a view from a nib uses UINib or loadNibNamed: and gets the appropriate object(s) from the nib by use of an IBOutlet. Not trawling through the objects array like you're doing. Also in your init method you are ignoring self, you're not calling super init. You are just assigning to self. If you use a factory method to load the view from the nib then you avoid these problems. (Although I guess setting up the outlet might be unusual since it's a class method.) It doesn't have to be a factory method but you should avoid all that junk in the cell's init method.

    It is a fundamental part of how tableviews work that table view cells are reused. So yes, code that runs only one time when the cell is created has to be inside the if (cell == nil) code.
     
  11. thread starter macrumors newbie

    Joined:
    May 9, 2011
    #11
    This is exactly what I'm doing.

    the reason why I'm "iterating" through the NSStrings array is because I need multiple sub views per row from the same UIView subclass. I'm instancing each view with UINib as you said (and it works) and then passing the NSString to set the UILabel (this doesn't work).


    Yes, that's because I'm loading it from a nib. Isn't that correct ?




    Yeah, but even if I'm re-using queued cells, I stil need to update its components with the new text, outside the if statement. Correct ?



    In the end, my question is still unanswered: "why isn't the label updated, instead of containing the original content (written in xib in UIBuilder)" ?

    thanks
     
  12. thread starter macrumors newbie

    Joined:
    May 9, 2011
    #12
    mhmmm... I've found an article that says to not use xib files for UITableView cells ativsoftware.com/wordpress/?p=9

    I should probably build it programmatically...
     
  13. PhoneyDeveloper, May 11, 2011
    Last edited: May 11, 2011

    macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #13
    You'll have to point out the IBOutlets that you're using when loading the nib.

    Yes. You only create the subviews one time but you need to set their contents every time.

    As I mentioned there are a lot of problems with the code you show. Is the label nil? What is its frame?

    BTW, if you add subviews to a tableviewcell you add them to the cell.contentView. Also, why don't you just add the imageview and label to a UITableViewCell that's in the nib?

    It certainly works fine if you do it right. There is some opinion that using nibs for table view cells will be slower than building the cell in code.

    What I recommend that you do is to simplify this. Remove all the complicated code that uses gcd and the nib. Load up a normal default tableview cell. Add an image from your bundle and some constant text. When that works add features one by one so that you get them working. IMO, as is it's too complicated to figure out why it isn't working.
     

Share This Page