简体   繁体   中英

weird behavior on UItableView that is not inside UITableViewController

I have a UIViewController, and when a user clicks a button, I want to show a UITableView. I receive the data from the server.

The problem is that there is something weird happening. Sometimes, not all the cells are updated. In other words, in my prototype cell, there are two buttons, with default text, but when I load the data from server not all the buttons text are updated, instead when I scroll the table, they become updated. Also when I **scroll* the table, sometimes the buttons go back to the default text.

Here are my code: (really easy)

class CusinePreferencesTableView: NSObject, UITableViewDelegate, UITableViewDataSource {

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(CellIdentefiers.oneCusinePreferencesCell.rawValue, forIndexPath: indexPath) as! OneCusinePreferencesTableViewCell
        var row = indexPath.row
        print("row = \(row)")
        let oneCusineDataLeft = Preferences2ViewController.cusines![row]
        cell.leftButton.titleLabel?.text = oneCusineDataLeft
        row = row + 1
        if row < Preferences2ViewController.cusines!.count{
            let oneCusineDataRight = Preferences2ViewController.cusines![row]
            cell.rightButton.titleLabel?.text = oneCusineDataRight
        }else {
            //I should hide the right button
        }
        return cell
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let cusines = Preferences2ViewController.cusines {
            if cusines.count % 2 == 0 {
                return cusines.count/2
            }else {
                var count = cusines.count/2
                count = count + 1
                return count
            }
        }else {
            return 0
        }
    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

}

And this is the table view (in case you want it) 在此处输入图片说明

If you see that you didn't get the description of my problem, I can make a video for you

Update 1

I already call the reloadData as you see here (where I get the data from the server)

 func loadCusiens(){
        let url = NSURL(string: ConstantData.getWebserviceFullAddress()+"preferences/cusines")
        let request = NSMutableURLRequest(URL: url!)
        request.HTTPMethod = "POST"
        let session = NSURLSession.sharedSession()
        let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error ) in
            if let error = error {
                print("error = \(error)")
            }

            if let data = data {
                do{
                    let jsonArray = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! NSArray
                    var results = [String]()
                    for oneJSON in jsonArray {
                        let name = oneJSON["name"] as! String
                        results.append(name)
                    }
                    dispatch_async(dispatch_get_main_queue(), {
                        Preferences2ViewController.cusines = results
                        self.cusineTableView.reloadData()
                    })
                } catch{
                    print("This exception happened = \(error)")
                }
            }
        })
        task.resume()
    }

Update

Now in the else part, i set the text of the right button to "sometext" this is a video about the problem

http://www.mediafire.com/watch/k2113ovebdvj46d/IMG_0911.MOV

Looks like a reuse problem. Basically if you follow the examples in Apple and other places, it uses a dequeue function. What this means is that iOS will look for an already existing cell that is no longer being shown and give you that to draw the contents of a new cell. But of course if the old cell had a bunch of stuff set, you will see the old stuff. The solution is to always assign everything to the correct new value.

This can get you easily if your cells have variable information, for instance if there's a picture you set if a certain entry has it, but isn't set if it doesn't have it. What will happen then is when you get an entry with no picture your code might not set it the image to nil, and you'll see the picture of an old cell that was scrolled off.

Please let me explain the reuse of UITableViewCells works

Assume you have a very huge cells and you can see only 3 cells on you screen. Assume now you see on your screen the following cells:

A
B
C

On the screen... and your datasource is: A B C C B A .

Now you are scrolling down for 1 cell exactly:

So the cell A is going from the Up (it was first cell) down and becoming your last visible cell

So now on the screen you will see

B
C
A   // While you want to draw a cell `C` here according to the dataSource

What happens now in cellForRowAtIndexPath...

You are getting the same Cell A from the beginning.

Assume your cell A had only 1 button and a Cell C need to show 2 buttons Because you have already draw the cell A you draw it with 1 button (lets assume you set the right button hidden property to YES ).

So now you want to draw your cell C on the reused cell A (Assume you set TAGS for the cell types to know what type of cell is popped up to you in the cellForRowAtIndexPath )

And now you can query the tag of the cell

if(myCell.tag == `A`/*(Assume you have an Enum for tags)*/)
{
    //So here you will want to Unhide the Right button of the cell (which is currently still cell A
    //After you have done with all needed adjustments you will want to set the appropriate tag for this cell to be ready for the next reuse -> so set it's tag to C
    //Now next time this cell will get reused it will be drawn as cell `C` and it will think it is a cell `C`  
}

Update

The main problem is not cell reuse, nor the layout problem I explain below, but the use of:

cell.leftButton.titleLabel?.text = oneCusineDataLeft

to set the button text. You must use:

cell.leftButton.setTitle(oneCusineDataLeft, forState: .Normal)

instead. (In essence, a UIButton keeps track of the title text it should display when it is in different "states" - normal, selected, etc. Although the your code changes the text that is currently displayed, as soon as the button changes state, it sets the text back to the stored value. I assume the button state is reset when the cells are displayed. The setTitle method updates the stored value).

Original

Setting aside the cell reuse problem, I don't think your code will achieve quite what you want. As currently coded, the layout would be something like this:

Row 0: left: cuisine 0, right: cuisine 1 
Row 1: left: cuisine 1, right: cuisine 2 
Row 2: left: cuisine 2, right: cuisine 3

I'm guessing you actually want this:

Row 0: left: cuisine 0, right: cuisine 1
Row 1: left: cuisine 2, right: cuisine 3
Row 2: left: cuisine 4, right: cuisine 5

If that's the case, amend your code as follows:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier(CellIdentefiers.oneCusinePreferencesCell.rawValue, forIndexPath: indexPath) as! OneCusinePreferencesTableViewCell
    var row = indexPath.row
    print("row = \(row)")
    let oneCusineDataLeft = Preferences2ViewController.cusines![2*row]
    cell.leftButton.titleLabel?.text = oneCusineDataLeft
    if (2*row+1) < Preferences2ViewController.cusines!.count{
        let oneCusineDataRight = Preferences2ViewController.cusines![2*row+1]
        cell.rightButton.titleLabel?.text = oneCusineDataRight
    }else {
        //I should hide the right button
        cell.rightButton.titleLabel?.text = ""
    }
    return cell
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if let cusines = Preferences2ViewController.cusines {
        if cusines.count % 2 == 0 {
            return cusines.count/2
        }else {
            return (cusines.count+1)/2
        }
    }else {
        return 0
    }
}

I should also note that you would probably be better off using a UICollectionView rather than a UITableView , since the former will provide the multiple column flow much more easily and intuitively.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM