Screen refresh problem

Discussion in 'iOS Programming' started by larswik, Jan 4, 2012.

  1. larswik macrumors 68000

    Joined:
    Sep 8, 2006
    #1
    Hey, My app is coming along but I hit a snag with number overlapping on a UILabel. I included 2 photos and you can see on one the number 2 in the green dot looks clear, but in the other is is a blend of 2 numbers.

    I click on the TableView row, it opens a new ViewController where I add a new job which increments the count to 3 and write the result to a property list. I then navigate back to the the first viewController and reload the data from the Property list. But now instead of the number 3 it is a blend of the numbers 2 and 3 as you can see in the photo.

    When I launch the program everything loads and displays just fine, it's only when I make a change. I think my problem area is in the TableView cellForRowAtIndexPath:. This is where it loads the data and sets up the cells.

    Code:
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *CellIdentifier = @"Cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
            
        }
        NSDictionary *tempDict = [[NSDictionary alloc] initWithDictionary:[clientListForTable objectAtIndex:indexPath.row]];
        NSLog(@"Temp Dict has %@", tempDict);
    
        cell.textLabel.text = [tempDict objectForKey:@"client"];
        cell.textLabel.font = [UIFont fontWithName:@"Helvetica" size:18.0];
        cell.textLabel.font = [UIFont boldSystemFontOfSize:16.0];
        cell.textLabel.textColor = [UIColor whiteColor];
        
        cell.detailTextLabel.text = [tempDict objectForKey:@"businessName"];
        cell.detailTextLabel.textColor = [UIColor whiteColor];
        
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        
        NSString *jobNum = [NSString stringWithFormat:@"%@",[tempDict objectForKey:@"numberOfJobs"]]; //NSNumbers convert to NSString
        
        if (![jobNum isEqualToString:@"0"]) { //First check to see if there are jobs in the array
            
                    
            CGRect boxRect = CGRectMake(0, 0, 44, 44); 
            UIImage *newImage = [UIImage imageNamed:@"greenSquare.png"]; //load the image
            UILabel *numLabel = [[UILabel alloc] initWithFrame:boxRect]; //create the Label
    
            NSLog(@"UILabel: %@", jobNum); //test wha tthe UILabel number should be
            
            numLabel.text = jobNum;
            numLabel.textColor = [UIColor blackColor];
            numLabel.backgroundColor = [UIColor clearColor];
            numLabel.textAlignment = UITextAlignmentCenter;
            numLabel.font = [UIFont boldSystemFontOfSize:15.0];
            
            cell.imageView.image = newImage; // adds the green square png to the view
            
            cell.imageView.layer.masksToBounds = YES;
            cell.imageView.layer.cornerRadius = 20.0;
            
            [numLabel removeFromSuperview]; //Removes the UILabel from the view, trying to fix problem
            [cell.imageView addSubview:numLabel]; // adds the UILabel back with the new number
            
        }
    
        return cell;
    }
     

    Attached Files:

  2. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #2
    You need to find a way to get a reference to the previously-added UILabel, not the just-created one, before you remove it from the superview. I would do this by setting the tag property for it and then retrieving it using UIView's viewWithTag: method.
     
  3. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #3
    Hi Dejo, I have used the Tag property before when I worked with TextFields in a different project to retrieve the text.

    In this case I am trying to understand why I need to retrieve the UILabel first, instead of just removing it and adding it as a subView again?

    Am I looking to remove a UILabel with a specific Tag ID property? Is this like the same problem that you helped me with before where the numLabel pointer is pointing to the same object in memory?
     
  4. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #4
    Actually, thinking about this more, you don't need to remove it from the superview. You can add it as a subview in the "init" portion of the method (the code within the "if (cell == nil)" logic) and then simply retrieve a reference to the UILabel outside that block to set its text, assuming all cells in the table will operate with this label over top the image. I don't know why I didn't suggest this before as this is the common approach when programmatically customizing a table view cell. This is covered in the Table View Programming Guide. Did you read that all yet?
     
  5. jonnymo5 macrumors 6502

    Joined:
    Jan 21, 2008
    Location:
    Texas
    #5
    Right here

    Code:
    UILabel *numLabel = [[UILabel alloc] initWithFrame:boxRect]; //create the Label
    You are making a new label. So all your code after that using numLabel is not referencing your old label.

    Like dejo said, you should set the tag property on it and then you can look for it before you alloc a new label. Then you can update the text instead of putting a new label on top of it.

    *additional*
    I usually create a custom tableviewcell for more complicated things like this. Then I can just grab the tag with viewByTag and update it. Keep the update cell calls as light as you can so you don't get jerky scrolling.
     
  6. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #6
    I kind of see what is happening now. I am over alloc'ing the UILabel and when I change the value it adds it to a new UILabel and places it on top of the old one. By using Tag Properties I can get a hold of the existing one and change it's value.

    So psudo code would be something like..
    If UILabel with Tag Property value exists
    Get UILabel with Tag Property
    Set UILabel with Tag Property to new value
    else
    Create new UIlabel with Property Tag and set UILabel string to @"aValue"

    dejo- I did read the TableView Doc's you listed. The problem that I have is that there is so much to remember. when I finally have an "Ah-Ha" moment with code them it becomes second nature and I don't think about it. The only way I can have those "Ah-Ha" moments is by doing, learning and asking questions. I have not hit the "Ah-Ha" yet with tableviews, but I am learning.

    Thanks again for all your help guys!
     
  7. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #7
    Each cell is going to have this UILabel over UIImageView, right? So, you don't need check if the "UILabel with Tag Property" exists. If you add it as a subview whenever you init the cell, it is guaranteed to exist. So, to me, the less-pseudo-code would be something like:

    Code:
    if (cell == nil) {
        ...
        UILabel *numLabel = [[UILabel alloc] initWithFrame:...];
        numLabel.tag = 99;
        [cell addSubview:numLabel];
    }
    
    ...
    
    UILabel *numLabel = (UILabel *)[cell.imageView viewWithTag:99];
    // do whatever you need with the numLabel now
     
  8. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #8
    Ok, I am starting to have that "Ah-Ha" moment. This line of code you kindly provided for me (thanks by the way)
    Code:
    UILabel *numLabel = (UILabel *)[cell.imageView viewWithTag:99];
    
    Is saying create a new UILabel pointer variable called numLabel that points to the UILabel pointer (UILabel *) that points to the correct memory location that the cell with the Tag Property of 99 is located?

    Sorry for the consistent questions but I am happy to learn this. At fist I thought this (UILable *) was casting it.
     
  9. jonnymo5 macrumors 6502

    Joined:
    Jan 21, 2008
    Location:
    Texas
    #9
    Basically right. The (UILabel *) is not an additional pointer it is just typecasting the pointer returned from the viewWithTag call. It promises the compiler you know what you are doing and the view returned will really be a UILabel.
     
  10. North Bronson macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #10
    Remember Model-View-Controller?

    Creating multiple subviews for your cell in your controller class is not the best approach.

    Create a custom subclass of UITableViewCell. Create your subviews there. This will clean things up and give you a better design.

    Do not retrieve the UILabel by setting tags. Retrieve the UILabel by using an accessor method (or property) of the custom cell class. This is going to be a cleaner approach.
     
  11. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #11
    MVC the subViews (cells). So if I understand.... Create a new class. Make this class a subClass of UITableViewCell (like you said) and call it 'TableCell' for example.

    Then when the method tableView cellForRowAtIndexPath: is called, it instantiates the Object from the TableCell class and pass the arguments such as name, company and the number of jobs (for my UILabel) to the new object. Then the TableCell object creates the cell andreturns a new cell that gets inserted into the TableView.

    So the benefit to this approach is that it always creates new cells when ever I enter this TableViewController. Also I don't need to go find the UILabel with Tag Property to change the Labels value, a new cell is created with a new UILabel that reflects the correct value in the Label.

    Did I get that right?
     
  12. North Bronson macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #12
    How would Apple solve this problem? You want to create a table cell that is flexible enough to be used for every row in the table. You want to display an image and a text string.

    There are two main approaches.

    The first approach would be to send the table cell a text string and an image. It is a black box exactly how the table cell goes about drawing those on the view. The table cell might use an image view and a label. The table cell might be using Core Graphics. Your controller does not really care. Your controller just gives the table cell a string and an image and lets the cell do its thing. This is the approach Apple used for the 2.0 OS.

    The second approach would be to let the table cell expose an image view and a label. The controller does not send a string directly to the table cell. The controller asks the table cell for its label. The controller sends a string to that label. The same process works for the image view. This is the approach Apple has used since the 3.0 OS.

    What do you feel are the advantages and disadvantages of each approach? What do you feel is best for your application?
     
  13. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #13
    Thanks, I spent the better part of last night rewriting it and creating the TableViewCell class. It seems like what I wrote should work and I am not getting any errors but the cells are not showing up in the tableview, it remains empty.
    My TableCell header class. I did not end up using the Instance variables since in the method I did not seem to need them.
    Code:
    @interface TableCell : UITableViewCell{
        NSString *clientName;
        NSString *companyName;
        NSString *jobNumber; 
    }
    -(UITableViewCell *) makeNewCellItem:(NSString *) client withCompanyName:(NSString *) company andCurrentJobs:(NSString *) jobNum;
    
    @end
    Here is the TableCell implementation
    Code:
    -(UITableViewCell *) makeNewCellItem:(NSString *) [COLOR="Red"]client [/COLOR]withCompanyName:(NSString *) [COLOR="Red"]company[/COLOR] andCurrentJobs:(NSString *) [COLOR="Red"]jobNum[/COLOR]{
        UITableViewCell *cell;
        
        cell.textLabel.text = [COLOR="Red"]client;[/COLOR]
        cell.textLabel.font = [UIFont fontWithName:@"Helvetica" size:18.0];
        cell.textLabel.font = [UIFont boldSystemFontOfSize:16.0];
        cell.textLabel.textColor = [UIColor whiteColor];
        
        cell.detailTextLabel.text = [COLOR="Red"]company;[/COLOR]
        cell.detailTextLabel.textColor = [UIColor whiteColor];
        
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        
        CGRect boxRect = CGRectMake(0, 0, 44, 44);
        UILabel *numLabel = [[UILabel alloc] initWithFrame:boxRect];
        numLabel.backgroundColor = [UIColor clearColor];
        numLabel.textAlignment = UITextAlignmentCenter;
        numLabel.font = [UIFont boldSystemFontOfSize:15.0];
        numLabel.text = [COLOR="Red"]jobNum;[/COLOR]
        
        UIImage *newImage = [UIImage imageNamed:@"greenSquare.png"]; //load the image
        cell.imageView.image = newImage;
        [cell addSubview:numLabel];
    
        return cell;
    }
    
    Here is my Method for my TableView

    Code:
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSDictionary *tempDict = [[NSDictionary alloc] initWithDictionary:[clientListForTable objectAtIndex:indexPath.row]];
        NSString *jobNumber = [NSString stringWithFormat:@"%@",[tempDict objectForKey:@"numberOfJobs"]]; //NSNumbers convert to NSString
        
        static NSString *CellIdentifier = @"Cell";
        
        TableCell *newCell = [[TableCell alloc] init];
        
        //UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        
        UITableViewCell *cell = [newCell makeNewCellItem:[tempDict objectForKey:@"client"] withCompanyName:[tempDict objectForKey:@"businessName"] andCurrentJobs:jobNumber]; 
        
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        }
        return cell;
    }
    
     
  14. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #14
    I finally got it working. I took a few days to reread TableView doc's and research. Now at the if (cell == nil) I have it JUST create the items and adjust the properties and when it exits the if statement I then use the code that dejo showed me to retrieve the tag for the UILabels and add the values.

    So now when it iterates thru cellForRowAtIndex it will add the text even if the cell is coming from the reuse or a new one is created. This solved my blending problem. In case anyone else has this problem too here is the code I used.

    Code:
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSDictionary *tempDict = [[NSDictionary alloc] initWithDictionary:[clientListForTable objectAtIndex:indexPath.row]];
        NSString *jobNumber = [NSString stringWithFormat:@"%@",[tempDict objectForKey:@"numberOfJobs"]]; //NSNumbers convert to NSString
    
        static NSString *CellIdentifier = @"Cell";
        
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    
        if (cell == nil) {
    
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
            
            if (![jobNumber isEqualToString:@"0"]) {
                UIImage *newImage = [UIImage imageNamed:@"greenSquare.png"]; //load the image
                cell.imageView.image = newImage;
            }
           
            CGRect boxRect = CGRectMake(0, 0, 44, 44);
            UILabel *numLabel = [[UILabel alloc] initWithFrame:boxRect];
            numLabel.backgroundColor = [UIColor clearColor];
            numLabel.textAlignment = UITextAlignmentCenter;
            numLabel.font = [UIFont boldSystemFontOfSize:15.0];
            numLabel.tag = 1;
            
            //UIImage *newImage = [UIImage imageNamed:@"greenSquare.png"]; //load the image
            //cell.imageView.image = newImage;
            [cell.imageView addSubview:numLabel];
            
            cell.textLabel.font = [UIFont fontWithName:@"Helvetica" size:18.0];
            cell.textLabel.font = [UIFont boldSystemFontOfSize:16.0];
            cell.textLabel.textColor = [UIColor whiteColor];
            cell.textLabel.tag = 2;
            
            cell.detailTextLabel.textColor = [UIColor whiteColor];
            cell.detailTextLabel.tag = 3;
            
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;   
        }  
    
        UILabel *txtView = (UILabel*)[cell viewWithTag:2];
        UILabel *txtView2 = (UILabel*)[cell viewWithTag:3];
        UILabel *txtView3 = (UILabel*)[cell viewWithTag:1];
        txtView.text = [tempDict objectForKey:@"client"];
        txtView2.text = [tempDict objectForKey:@"businessName"];
        txtView3.text = jobNumber;
        return cell;
    }
    
    Thanks for the help folks!
     
  15. North Bronson macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #15
    Suppose that you have this table working. You have this view controller and everything is running smoothly. You decide to incorporate these table cells in another view controller of your application. The controller needs to be written differently for some reason. You now have two controller classes that need to display the same table cell.

    You could copy all of this code from one controller and paste it in another controller. Do you see the trouble with that approach?

    Do you see how creating a custom table cell would be a better approach? In what way would creating a custom table cell be a worse approach?
     
  16. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #16
    txtView seems like a confusing name for a UILabel, especially given there are also UITextViews. Also, you already have references to cell.textLabel and cell.detailTextLabel.
     
  17. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #17
    I am cleaning up the code tonight with better names. When I test things I tend to give them quick names intending on deleting it and trying again. To my surprise the cellForIndexAtPath method became clear to me last night and I could now see what was happening.

    But North, I see what you are saying but what still baffles me and how to make / create / approach a custom UITableViewCell. I thought I was on the right track when I started to write a Class called TableCell.

    Code:
    @interface TableCell : UITableViewCell{
        NSString *clientName;
        NSString *companyName;
        NSString *jobNumber; 
    }
    -(UITableViewCell *) makeNewCellItem:(NSString *) client withCompanyName:(NSString *) company andCurrentJobs:(NSString *) jobNum;
    
    @end
    For some reason I am struggling with how I go about creating a custom UITableViewCell. But I do see your point about copying an pasting code.
     
  18. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #18
    Review this code. Try to understand why it's returning an instance of type UITableViewCell and not TableCell.
     
  19. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #19
    I have been looking at it for a while now. I think I might know. First I instantiate a new object.
    Code:
    TableCell *newCell = [[TableCell alloc] init];
    The next part I think is where the problem might be. There is a pointer variable created for a UITableViewCell but no memory has been allocated or initializing has happened. I then use the method from my 'newCell' object which I tried to assign to the pointer.

    Code:
    UITableViewCell *cell = [newCell makeNewCellItem:[tempDict objectForKey:@"client"] withCompanyName:[tempDict objectForKey:@"businessName"] andCurrentJobs:jobNumber]; 
    if (cell == nil) at that point it instatiates the cell with the reuse identifier which is not my TableCell object. Then this cell is returned.

    I hope I got that right?
     
  20. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #20
    Dejo - I was really hoping for the answer in this. I was wondering if I figured out the question you asked.
     
  21. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #21
    Have you done anything to confirm your suspicions about how you believe the code is running? If so, what?
     
  22. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #22
    The code for my project has changed and it is working now. The code snippet that you showed me, that I wrote early, is not used any more and deleted. But your question I think will help me understand what I was doing wrong since it is not returning TableCell which is what I originally thought it was doing.

    So to answer your question, I have not tested it since it is been deleted. But spent some time reading it line by line to see what is happening. and followed up with the response. The problem is obvious to you but is not to me. So I was wondering if my response is some what correct?

    Thanks.
     
  23. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #23
    Actually, upon further review, the problem wasn't entirely obvious to me, but I had my suspicions. I was hoping that having you debug it further might confirm them as well as hopefully make you more comfortable with the debugging process and confirming through testing what you think code should be doing. But if you've deleted it all and found another solution and don't want to dwell on this, that's fine. If you do want to dwell, you could easily set up a test project and reuse the code you've supplied us with here to further investigate. Also, I wonder if you might inform the community of your current solution.
     
  24. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #24
    I see. Thanks though. I did post the code above but I will post it again to conclude this thread and thanks to the consistent help from this forum.
    Code:
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSDictionary *tempDict = [[NSDictionary alloc] initWithDictionary:[clientListForTable objectAtIndex:indexPath.row]];
        NSString *jobNumber = [NSString stringWithFormat:@"%@",[tempDict objectForKey:@"numberOfJobs"]]; //NSNumbers convert to NSString
    
        static NSString *CellIdentifier = @"Cell";
        
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    
        if (cell == nil) {
    
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
            
            if (![jobNumber isEqualToString:@"0"]) {
                UIImage *newImage = [UIImage imageNamed:@"greenSquare.png"]; //load the image
                cell.imageView.image = newImage;
            }
           
            CGRect boxRect = CGRectMake(0, 0, 44, 44);
            UILabel *numLabel = [[UILabel alloc] initWithFrame:boxRect];
            numLabel.backgroundColor = [UIColor clearColor];
            numLabel.textAlignment = UITextAlignmentCenter;
            numLabel.font = [UIFont boldSystemFontOfSize:15.0];
            numLabel.tag = 1;
            
            //UIImage *newImage = [UIImage imageNamed:@"greenSquare.png"]; //load the image
            //cell.imageView.image = newImage;
            [cell.imageView addSubview:numLabel];
            
            cell.textLabel.font = [UIFont fontWithName:@"Helvetica" size:18.0];
            cell.textLabel.font = [UIFont boldSystemFontOfSize:16.0];
            cell.textLabel.textColor = [UIColor whiteColor];
            cell.textLabel.tag = 2;
            
            cell.detailTextLabel.textColor = [UIColor whiteColor];
            cell.detailTextLabel.tag = 3;
            
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;   
        }  
    
        UILabel *txtView = (UILabel*)[cell viewWithTag:2];
        UILabel *txtView2 = (UILabel*)[cell viewWithTag:3];
        UILabel *txtView3 = (UILabel*)[cell viewWithTag:1];
        txtView.text = [tempDict objectForKey:@"client"];
        txtView2.text = [tempDict objectForKey:@"businessName"];
        txtView3.text = jobNumber;
        return cell;
    }
     

Share This Page