簡體   English   中英

Swift-更新或在表格視圖的部分中插入一行,而無需重新加載所有數據

[英]Swift - Update or insert a row in a section in a tableview without reloading all the data

如何在不重新加載所有數據的情況下在表視圖中更新或插入行?

在ClientsViewController中,以字母順序顯示客戶列表,並用部分(客戶名稱的首字母)分隔。

當我更新客戶端時,表視圖顯示同一客戶端的2個條目(舊條目和新條目)。 當我嘗試添加客戶端時,它崩潰了。

我認為問題出在索引上。

class ClientsViewController:  UITableViewController {

var sortedFirstLetters: [String] = []
var sections: [[Client]] = [[]]
var tableArray = [Client]()
var client: Client?
var refresher: UIRefreshControl!

@IBOutlet var noClientsView: UIView!

@IBAction func unwindToClients(sender: UIStoryboardSegue) {

    if let sourceViewController = sender.source as? ClientViewController,  let client = sourceViewController.client {

        if let selectedIndexPath = tableView.indexPathForSelectedRow {
            // Update an existing client.
            tableArray[selectedIndexPath.row] = client
            tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
        }
        else {
            // Add a client.
            let newIndexPath = IndexPath(row: tableArray.count, section: 0)
            tableArray.append(client)
            tableView.insertRows(at: [newIndexPath], with: .automatic)
        }


        let firstLetters = self.tableArray.map { $0.nameFirstLetter }
        let uniqueFirstLetters = Array(Set(firstLetters))

        self.sortedFirstLetters = uniqueFirstLetters.sorted()
        self.sections = self.sortedFirstLetters.map { firstLetter in
            return self.tableArray
                .filter { $0.nameFirstLetter == firstLetter }
                .sorted { $0.name < $1.name }
        }

//            DispatchQueue.main.async {
//                self.tableView.reloadData()
//            }


    }
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let secondScene = segue.destination as! ClientViewController
    if segue.identifier == "ShowDetail", let indexPath = self.tableView.indexPathForSelectedRow {
        let currentPhoto = sections[indexPath.section][indexPath.row]
        secondScene.client = currentPhoto
    }
    else if segue.identifier == "AddItem" {
        print("add")
    }
    else {
        fatalError("The selected cell is not being displayed by the table")
    }
}

@objc func handleRefresh(_ refreshControl: UIRefreshControl) {
    getClients()
    refreshControl.endRefreshing()
}

}

extension ClientsViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    self.refreshControl?.addTarget(self, action: #selector(ClientsViewController.handleRefresh(_:)), for: UIControlEvents.valueChanged)
    tableView.backgroundView = noClientsView
    getClients() //for only the 1st time ==> when view is created ==> ok ish
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)
    getClients() // not a good idea to make a request to the server everytime the view appears on the screen.
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    if(self.tableArray.count > 0) {
        return sortedFirstLetters[section]
    }
    else {
        return ""
    }
}

override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
    print(sortedFirstLetters)
     return sortedFirstLetters
}

override func numberOfSections(in tableView: UITableView) -> Int {
    return sections.count
}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let name = sections[indexPath.section][indexPath.row]
    let cell = tableView.dequeueReusableCell(withIdentifier: "ClientCell", for: indexPath)
    cell.textLabel?.text = name.name
    cell.detailTextLabel?.text = name.city + " - " + name.province
    return cell
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if(sections[section].count > 0) {
        tableView.backgroundView = nil
    }
    return sections[section].count
}

func getClients() {

    makeRequest(endpoint: "api/clients/all",
                parameters: [:],
                completionHandler: { (container : ApiContainer<Client>?, error : Error?) in
                    if let error = error {
                        print("error calling POST on /getClients")
                        print(error)
                        return
                    }
                    self.tableArray = (container?.result)!

                    self.prepareData()
                    DispatchQueue.main.async {
                        self.tableView.reloadData()
                    }
    } )
}

//sorts and makes the index
func prepareData() {
    let firstLetters = self.tableArray.map { $0.nameFirstLetter }
    let uniqueFirstLetters = Array(Set(firstLetters))

    self.sortedFirstLetters = uniqueFirstLetters.sorted()
    self.sections = self.sortedFirstLetters.map { firstLetter in
        return self.tableArray
            .filter { $0.nameFirstLetter == firstLetter }
            .sorted { $0.name < $1.name }
    }
}

}

波紋管是ClientViewController和Client結構。

class ClientViewController: UITableViewController, UIPickerViewDelegate, UIPickerViewDataSource {

@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var addressTextField: UITextField!
@IBOutlet weak var cityTextField: UITextField!
@IBOutlet weak var provinceTextField: UITextField!
@IBOutlet weak var postalCodeTextField: UITextField!
@IBOutlet weak var contactsLabel: UILabel!

let numberOfRowsAtSection: [Int] = [4, 2]
var client: Client?
var selectedProvince: String?

override func viewWillAppear(_ animated: Bool) {

    self.title = "New"
    if (client?.client_id) != nil {
        self.title = "Edit"
        nameTextField.text = client?.name
        provinceTextField.text = client?.province
        cityTextField.text = client?.city
        addressTextField.text = client?.address
        postalCodeTextField.text = client?.postal_code
        selectedProvince = client?.province
    }
}


@objc func save(sender: UIButton!) {
    let name = nameTextField.text ?? ""
    let address = addressTextField.text ?? ""
    let city = cityTextField.text ?? ""
    let province = selectedProvince ?? ""
    let postal_code = postalCodeTextField.text ?? ""
    var endPoint: String

    if (client?.client_id) != nil {
        endPoint = "api/clients/update"
    } else {
        endPoint = "api/clients/add"
    }

    client = Client(name:name, client_id: client?.client_id, postal_code: postal_code, province: province, city: city, address: address)
    let requestBody = makeJSONData(client)

    makeRequestPost(endpoint: endPoint,
                    requestType: "POST",
                    requestBody: requestBody,
                    view: view,
                    completionHandler: { (response : ApiContainer<Client>?, error : Error?) in
                        if let error = error {
                            print("error calling POST on /todos")
                            print(error)
                            return
                        }
                        let b = (response?.meta)!
                        let a = (response?.result[0])
                        let client_id = a?.client_id
                        self.client?.client_id = client_id

                        if(b.sucess == "yes") {

                            //change message and use the custom func like on error.
                            let alert = UIAlertController(title: "Success!", message: "All good", preferredStyle: .alert)
                            let OKAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {
                                (_)in
                                self.performSegue(withIdentifier: "unwindToClients", sender: self)
                            })

                            alert.addAction(OKAction)
                            DispatchQueue.main.async(execute: {
                                self.present(alert, animated: true, completion: nil)                                    
                            })


                        }
                        else
                        {
                            self.showAlert(title: "Error", message: "Error Creating Client")
                            //return
                        }
    } )
}


override func viewDidLoad() {
    super.viewDidLoad()
    tableView.sectionHeaderHeight = 50.0;

    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Save", style: .plain, target: self, action: #selector(save))


    let thePicker = UIPickerView()
    provinceTextField.inputView = thePicker
    thePicker.delegate = self

    // ToolBar
    let toolBar = UIToolbar()
    toolBar.barStyle = .default
    toolBar.isTranslucent = true
    toolBar.tintColor = UIColor(red: 92/255, green: 216/255, blue: 255/255, alpha: 1)
    toolBar.sizeToFit()

    // Adding Button ToolBar
    let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(ClientDetailViewController.doneClick))
    let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(ClientDetailViewController.cancelClick))
    toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
    toolBar.isUserInteractionEnabled = true
    provinceTextField.inputAccessoryView = toolBar

}

@objc func doneClick() {
    provinceTextField.resignFirstResponder()
}
@objc func cancelClick() {
    provinceTextField.resignFirstResponder()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
    return 2
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    var rows: Int = 0
    if section < numberOfRowsAtSection.count {
        rows = numberOfRowsAtSection[section]
    }
    return rows
}



override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let backItem = UIBarButtonItem()
    backItem.title = "Client"
    navigationItem.backBarButtonItem = backItem

    if  segue.identifier == "unwindToClients",
        let destination = segue.destination as? ClientsViewController
    {
        destination.client = client
    }

    if  segue.identifier == "showContacts",
        let destination = segue.destination as? ContactsViewController
    {
        destination.client = client
    }

}


// MARK: - Picker view
func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 1
}

func pickerView( _ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return provinces.count
}

func pickerView( _ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    return provinces[row].name
}

func pickerView( _ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    provinceTextField.text = provinces[row].name
}




}




struct Client: Codable {
var client_id: Int!
let name: String!
let postal_code: String!
let province: String!
let city: String!
let address: String!

init(name: String, client_id: Int! = nil, postal_code: String, province: String, city: String, address: String) {
    self.client_id = client_id
    self.name = name
    self.postal_code = postal_code
    self.province = province
    self.city = city
    self.address = address

}

var nameFirstLetter: String {
    return String(self.name[self.name.startIndex]).uppercased()
}

}
  1. 將數據源中的項目附加到要在表中插入行的索引處。 比按照步驟2,3,4。

  2. 調用方法tbl.beginupdate

  3. 調用表視圖的insertRowsAtIndexPaths方法
  4. 致電tbl.endupdate

您可以使用此方法更新tableView行。

 self.tableView.reloadRows(at: [indexPath!], with: .top)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM