且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Swift:如何从表格视图中删除重复项?

更新时间:2023-02-02 23:28:50

我建议将博客的唯一标识符保存在数组中,并在每次重新加载tableView时将适当的单元格保存到数组中,该标识符应位于以下类别中正确的部分.

您似乎正在使用 UserDefaults ,但未对其进行任何修改.使用我的方法,唯一需要保存到UserDefaults中并从UserDefaults中加载的数组是跟随的博客列表.其余的默认显示在主列表中,即使出现新博客也是如此.

您将需要一个数组:

var mainArray = [Blog]()
var followedArray = [Blog]()
var followedIdentifiers = [String]()

或标识符将使用的任何数据类型

您也可以使用Set,因为您不想在followedIdentifiers中重复

var followedIdentifiers = Set<String>()

以下是对代码相关部分的修改(我的更改用<----标记):

// Checking whether to import from mainArray or filteredArray to followedArray
if !(searchController.isActive && searchController.searchBar.text != "") {

    self.myTableView.beginUpdates()

    // Save identifier into followedIdentifier array <--------------
    self.followedIdentifiers.insert(mainArray[indexPath.row].blogID)

    // ----- Inserting Cell to followedArray -----
    followedArray.insert(mainArray[indexPath.row], at: 0)
    myTableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .fade)

    // ----- Removing Cell from mainArray -----
    mainArray.remove(at: indexPath.row)
    let rowToRemove = indexPath.row
    self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 1)], with: .fade)

    self.myTableView.endUpdates()

    myTableView.reloadData()

    // After Updating Table, Save the Archived to UserDefaults
    saveUserDefaults()
} else {

    self.myTableView.beginUpdates()

    // Remove identifier into followedIdentifier array <------------
    self.followedIdentifiers.remove(followedArray[indexPath.row].blogID)

    // ----- Inserting Cell to followedArray -----
    let blogObject: Blog = filteredArray[indexPath.row]
    let indexOfObjectInArray = mainArray.index(of: blogObject)

    followedArray.insert(blogObject, at: 0)

    // ----- Removing Cell from filteredArray -----
    filteredArray.remove(at: indexPath.row)
    mainArray.remove(at: indexOfObjectInArray!)
    let rowToRemove = indexPath.row
    self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 0)], with: .fade)

    self.myTableView.endUpdates()

    myTableView.reloadData()

    // After Updating Table, Save the Archived to UserDefaults
    saveUserDefaults()
}

// Retrieving Data from Server
func retrieveDataFromServer() {

    let getDataURL = "http://example.com/receiving.php"
    let url: NSURL = NSURL(string: getDataURL)!

    do {
        let data: Data = try Data(contentsOf: url as URL)
        let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray

        // Clear the arrays      <-------------
        self.followedArray = [Blog]()
        self.mainArray = [Blog()]

        // Looping through jsonArray
        for jsonObject in jsonArray {

            if let blog = Blog.createBlog(from: jsonObject as AnyObject) {
                // Check if identifiers match <------------
                if followedIdentifiers.contains(blog.blogID) {
                    self.followedArray.append(blog)
                } else {
                    self.mainArray.append(blog)
                }

            }
        }
    } catch {
        print("Error: (Retrieving Data)")
    }
    myTableView.reloadData()
}

为了使它在整个会话中都能正常工作,您的saveUserDefaults()

中必须具有与此类似的内容
UserDefaults.standard.setValue(Array(self.followedIdentifiers), forKey: "someName")

,这是您从UserDefaults加载的位置

self.followedIdentifiers = Set(UserDefaults.standard.stringArray(forKey: "someName"))

Problem here is that I'm getting duplicates in my table view and I know why but I don't know how to fix it and implement a different system.

My app is a blog reader that reads from a MYSQL database using PHP to send JSON to my Swift app. My table view has two sections, one for all the objects from the database and second section is for when I click the follow button on the cells, basically move the objects from mainArray to followedArray. Each section is using an array so for example I move all the objects from mainArray to followedArray and I update the table I get all those objects again in mainArray, obviously because the mainarray is empty and the code is just doing its job.

So how can I implement a better system so when the user moves the objects from one section to another (or from mainArray to followedArray) mainArray doesn't get repopulated with the same objects that it had and are now in followedArray.

Here is the code I use.

MainController.swift - Class where Tableview is at

var mainArray = [Blog]()
var followedArray = [Blog]()

// Title for Header
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

    if !(searchController.isActive && searchController.searchBar.text != "") {

        if section == 0 {
            return "Followed Blogs"
        }
        else {
            return "All Blogs"
        }
    }
    return "Filtered Blogs"
}

// Number of Rows in Section
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    if !(searchController.isActive && searchController.searchBar.text != "") {

        if section == 0 {

            return followedArray.count
        }
        else if (section == 1) {

            return mainArray.count
        }
    }
    return filteredArray.count
}

// CellForRowAt indexPath
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let CellIdentifier = "Cell"
    var cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! CustomCell

    if cell != cell {
        cell = CustomCell(style: UITableViewCellStyle.default, reuseIdentifier: CellIdentifier)
    }

    // Configuring the cell
    var blogObject: Blog

    if !(searchController.isActive && searchController.searchBar.text != "") {
        if indexPath.section == 0 {
            blogObject = followedArray[indexPath.row] 
            cell.populateCell(blogObject, isFollowed: true, indexPath: indexPath, parentView: self)
        }
        else if indexPath.section == 1 {
            blogObject = mainArray[indexPath.row] 
            cell.populateCell(blogObject, isFollowed: false, indexPath: indexPath, parentView: self)
        }
    }
    else {
        blogObject = filteredArray[indexPath.row] 
        cell.populateCell(blogObject, isFollowed: false, indexPath: indexPath, parentView: self)
    }

    return cell
}

// Follow Button
@IBAction func followButtonClick(_ sender: UIButton!) {

    // Adding row to tag
    let buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: self.myTableView)
    if let indexPath = self.myTableView.indexPathForRow(at: buttonPosition) {

        // Showing Status Labels
        let cell = self.myTableView.cellForRow(at: indexPath) as! CustomCell
        cell.firstStatusLabel.isHidden = false
        cell.secondStatusLabel.isHidden = false

        // Change Follow to Following
        (sender as UIButton).setImage(UIImage(named: "follow.png")!, for: .normal)
        cell.followButton.isHidden = true
        cell.followedButton.isHidden = false

        // Checking wether to import from mainArray or filteredArray to followedArray
        if !(searchController.isActive && searchController.searchBar.text != "") {

            self.myTableView.beginUpdates()

            // ----- Inserting Cell to followedArray -----
            followedArray.insert(mainArray[indexPath.row], at: 0)
            myTableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .fade)

            // ----- Removing Cell from mainArray -----
            mainArray.remove(at: indexPath.row)
            let rowToRemove = indexPath.row
            self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 1)], with: .fade)

            self.myTableView.endUpdates()

            myTableView.reloadData()

            // After Updating Table, Save the Archived to UserDefaults
            saveUserDefaults()
        }
        else {

            self.myTableView.beginUpdates()

            // ----- Inserting Cell to followedArray -----
            let blogObject: Blog = filteredArray[indexPath.row]
            let indexOfObjectInArray = mainArray.index(of: blogObject)

            followedArray.insert(blogObject, at: 0)

            // ----- Removing Cell from filteredArray -----
            filteredArray.remove(at: indexPath.row)
            mainArray.remove(at: indexOfObjectInArray!)
            let rowToRemove = indexPath.row
            self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 0)], with: .fade)

            self.myTableView.endUpdates()

            myTableView.reloadData()

            // After Updating Table, Save the Archived to UserDefaults
            saveUserDefaults()
        }
    }
}


// Retrieving Data from Server
func retrieveDataFromServer() {

    let getDataURL = "http://example.com/receiving.php"
    let url: NSURL = NSURL(string: getDataURL)!

    do {
        let data: Data = try Data(contentsOf: url as URL)
        let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray

        // Looping through jsonArray
        for jsonObject in jsonArray {

            if let blog = Blog.createGame(from: jsonObject as AnyObject) {

                mainArray.append(blog)
            }
        }
    }
    catch {
        print("Error: (Retrieving Data)")
    }
    myTableView.reloadData()
}

Blog.swift - Handles the blogs objects where I user NSCoder too

import UIKit

class Blog: NSObject, NSCoding {

var blogName: String!
var blogStatus1: String!
var blogStatus2: String!
var blogURL: String!
var blogID: String!
var blogType: String!
var blogDate: String!
var blogPop: String!

static func createBlog(from jsonObject: AnyObject) -> Blog? {

    guard let bID: String = jsonObject.object(forKey: "id") as? String,
        let bName: String = jsonObject.object(forKey: "blogName") as? String,
        let bStatus1: String = jsonObject.object(forKey: "blogStatus1") as? String,
        let bStatus2: String = jsonObject.object(forKey: "blogStatus2") as? String,
        let bURL: String = jsonObject.object(forKey: "blogURL") as? String,
        let bType: String = jsonObject.object(forKey: "blogType") as? String,
        let bDate: String = jsonObject.object(forKey: "blogDate") as? String,
        let bPop: String = jsonObject.object(forKey: "blogPop") as? String

        else {
          print("Error: (Creating Blog Object)")
          return nil
}

let blog = Blog()
    blog.blogName = bName
    blog.blogStatus1 = bStatus1
    blog.blogStatus2 = bStatus2
    blog.blogURL = bURL
    blog.blogID = bID
    blog.blogType = bType
    blog.blogDate = bDate
    blog.blogPop = bPop
    return blog
}

convenience required init?(coder aDecoder: NSCoder) {
    self.init ()
    self.blogName = aDecoder.decodeObject(forKey: "blogName") as! String
    self.blogStatus1 = aDecoder.decodeObject(forKey: "blogStatus1") as! String
    self.blogStatus2 = aDecoder.decodeObject(forKey: "blogStatus2") as! String
    self.blogURL = aDecoder.decodeObject(forKey: "blogURL") as! String
    self.blogID = aDecoder.decodeObject(forKey: "blogID") as! String
    self.blogType = aDecoder.decodeObject(forKey: "blogType") as! String
    self.blogDate = aDecoder.decodeObject(forKey: "blogDate") as! String
    self.blogPop = aDecoder.decodeObject(forKey: "blogPop") as! String
}

func encode(with aCoder: NSCoder) {
    aCoder.encode(blogName, forKey: "blogName")
    aCoder.encode(blogStatus1, forKey: "blogStatus1")
    aCoder.encode(blogStatus2, forKey: "blogStatus2")
    aCoder.encode(blogURL, forKey: "blogURL")
    aCoder.encode(blogID, forKey: "blogID")
    aCoder.encode(blogType, forKey: "blogType")
    aCoder.encode(blogDate, forKey: "blogDate")
    aCoder.encode(blogPop, forKey: "blogPop")
 }
}

Is there a way to check before repopulating mainArray to see whats in followedArray and whatever is missing or added to the database to import and not create duplicates because new blogs will be added and the users will transfer blogs across sections so this is a major issue that I am having.

Would appreciate the help as I am still learning Swift, thank you.

I would recommend saving an unique identifier for the blogs which should be in the followed category into an array and on each reload of the tableView, move the proper cells to the correct section.

You seem to be using UserDefaults but have no modifications to them. Using my method the only array that is required to be saved into and loaded from the UserDefaults is the list of followed blogs. The rest default to the main list, even as new blogs show up.

you will need one more array:

var mainArray = [Blog]()
var followedArray = [Blog]()
var followedIdentifiers = [String]()

or whatever datatype the identifier will be in

You could also use a Set as you want no duplicates in the followedIdentifiers

var followedIdentifiers = Set<String>()

Here are modifications to the relevant parts of your code (My changes marked with <----):

// Checking whether to import from mainArray or filteredArray to followedArray
if !(searchController.isActive && searchController.searchBar.text != "") {

    self.myTableView.beginUpdates()

    // Save identifier into followedIdentifier array <--------------
    self.followedIdentifiers.insert(mainArray[indexPath.row].blogID)

    // ----- Inserting Cell to followedArray -----
    followedArray.insert(mainArray[indexPath.row], at: 0)
    myTableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .fade)

    // ----- Removing Cell from mainArray -----
    mainArray.remove(at: indexPath.row)
    let rowToRemove = indexPath.row
    self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 1)], with: .fade)

    self.myTableView.endUpdates()

    myTableView.reloadData()

    // After Updating Table, Save the Archived to UserDefaults
    saveUserDefaults()
} else {

    self.myTableView.beginUpdates()

    // Remove identifier into followedIdentifier array <------------
    self.followedIdentifiers.remove(followedArray[indexPath.row].blogID)

    // ----- Inserting Cell to followedArray -----
    let blogObject: Blog = filteredArray[indexPath.row]
    let indexOfObjectInArray = mainArray.index(of: blogObject)

    followedArray.insert(blogObject, at: 0)

    // ----- Removing Cell from filteredArray -----
    filteredArray.remove(at: indexPath.row)
    mainArray.remove(at: indexOfObjectInArray!)
    let rowToRemove = indexPath.row
    self.myTableView.deleteRows(at: [IndexPath(row: rowToRemove, section: 0)], with: .fade)

    self.myTableView.endUpdates()

    myTableView.reloadData()

    // After Updating Table, Save the Archived to UserDefaults
    saveUserDefaults()
}

// Retrieving Data from Server
func retrieveDataFromServer() {

    let getDataURL = "http://example.com/receiving.php"
    let url: NSURL = NSURL(string: getDataURL)!

    do {
        let data: Data = try Data(contentsOf: url as URL)
        let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray

        // Clear the arrays      <-------------
        self.followedArray = [Blog]()
        self.mainArray = [Blog()]

        // Looping through jsonArray
        for jsonObject in jsonArray {

            if let blog = Blog.createBlog(from: jsonObject as AnyObject) {
                // Check if identifiers match <------------
                if followedIdentifiers.contains(blog.blogID) {
                    self.followedArray.append(blog)
                } else {
                    self.mainArray.append(blog)
                }

            }
        }
    } catch {
        print("Error: (Retrieving Data)")
    }
    myTableView.reloadData()
}

In order for this to work across sessions you must have something similar to this in your saveUserDefaults()

UserDefaults.standard.setValue(Array(self.followedIdentifiers), forKey: "someName")

and this where you load from UserDefaults

self.followedIdentifiers = Set(UserDefaults.standard.stringArray(forKey: "someName"))