Search code examples
swiftuitableviewcore-datasavesql-delete

Text Overlapping Text That Should've deleted


enter image description here HI, so this is my vc for a view where I save info about a plane (the type and rego). It saves the plane and deletes and you can close the app and come back and it saves it there in the core data model but I have a weird problem. After you make a plane and it shows in the table view and add a few more planes (table view cell which has info) then the labels which display the info start to overlap with what should be deleted information. The pictures bellow show what I mean. Any help would be greatly appreciated. enter image description here

var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

var typeField: UITextField?
var regoField: UITextField?

@IBAction func addPlaneButton(_ sender: Any) {

   let alertController = UIAlertController(title: "New Plane", message: "Please Input The Type And Registration", preferredStyle: .alert)
    alertController.addTextField(configurationHandler: typeField)
    alertController.addTextField(configurationHandler: regoField)

    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
    let saveAction = UIAlertAction(title: "Save", style: .default, handler: self.savePlane)

    alertController.addAction(cancelAction)
    alertController.addAction(saveAction)

    self.present(alertController, animated: true)


    print("Add Plane Pressed")
}

func typeField(textField: UITextField!) {
    typeField = textField
    typeField?.placeholder = "Aircraft Type"
}

func regoField(textField: UITextField!) {
    regoField = textField
    regoField?.placeholder = "Aircraft Registration"
}



@IBOutlet weak var tableView: UITableView!

var timer = Timer()

let utcItem = UIBarButtonItem()
let utcLbl = UILabel()


override func viewDidLoad() {
    super.viewDidLoad()

    //Table View
    tableView.delegate = self
    tableView.dataSource = self
    self.tableView.rowHeight = 88

    setupView()

}


            //////Functions////


func setupView() {
    //UTC Time Formatter
    let dateFormatter = DateFormatter()
    dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
    dateFormatter.dateFormat = "HH:mm"
    _ = Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)

    //UTC Time
    utcLbl.frame = CGRect(x: 0, y: 0, width: 100, height: 20)
    utcLbl.text = "\(dateFormatter.string(from: Date())) UTC"
    utcItem.customView = utcLbl
    utcLbl.backgroundColor = UIColor.init(fromHexCode: "4FB7F1")
    utcLbl.layer.cornerRadius = 10
    utcLbl.textAlignment = .center
    utcLbl.layer.masksToBounds = true // Or utcLbl.clipsToBounds = true
    self.navigationItem.setLeftBarButtonItems([utcItem], animated: true)

    // Large Title
    self.title = "Planes"
    self.navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.font: UIFont(name: "Avenir-Black", size: 35)!]
    self.navigationController?.navigationBar.prefersLargeTitles = true
    let customBlue = UIColor(red:0.08, green:0.38, blue:0.75, alpha:1.0)
    navigationController?.navigationBar.barTintColor = customBlue
}

//Constant UTC Time Lbl
@objc func updateTime() {
    let formatter = DateFormatter()
    formatter.timeZone = TimeZone(abbreviation: "UTC")
    formatter.dateFormat = "HH:mm"
    utcLbl.text = formatter.string(from: Date()) + " UTC"
}

//Save the Plane Info

func savePlane(alert: UIAlertAction) {

    if typeField?.text != "" || regoField?.text != "" {

        let newLog = NSEntityDescription.insertNewObject(forEntityName: "Planes", into: context)
        newLog.setValue(self.typeField?.text, forKey: "type")
        newLog.setValue(self.regoField?.text, forKey: "rego")

        do{
            try context.save()
        }
        catch {
            print(error)
        }

        //Making the table update itself when user logs the plane
        self.fetchData()
        self.tableView.reloadData()

    }
    print("Plane Saved")
}

func fetchData() {
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext


    do{
        planeArray = try context.fetch(Planes.fetchRequest())
    }
    catch{
        print(error)
    }
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

    if editingStyle == .delete {
        let save = planeArray[indexPath.row]
        context.delete(save)
        (UIApplication.shared.delegate as! AppDelegate).saveContext()

        do {
            planeArray = try context.fetch(Planes.fetchRequest())
        }
        catch {
            print(error)
        }
        tableView.reloadData()
    }
}


//Table View Functions

public func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return (planeArray.count)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

    //Plane Pic
    let planeView = UIImageView()
    planeView.frame = CGRect(x: 0, y: 0, width: 69, height: 67)
    //planeView.center = CGPoint(x: cell.center.x - 150, y: cell.center.y)
    planeView.center = CGPoint(x: cell.center.x - 145, y: cell.center.y)
    let planeImage: UIImage = UIImage(named: "plane")!
    planeView.image = planeImage
    cell.addSubview(planeView)

    //Type Label
    let type = UILabel()
    type.frame = CGRect(x: 0, y: 0, width: 45, height: 21)
    type.center = CGPoint(x: cell.center.x - 80, y: cell.center.y - 22.5)
    type.text = "Type:"
    type.font = UIFont(name: "Montserrat-Medium", size: 17)
    cell.addSubview(type)

    //Type Answer
    let typeAnswer = UILabel()
    typeAnswer.frame = CGRect(x: 0, y: 0, width: 220, height: 21)
    typeAnswer.center = CGPoint(x: cell.center.x + 62.5, y: cell.center.y - 22.5)
    typeAnswer.text = ""
    typeAnswer.font = UIFont(name: "Montserrat-Light", size: 17)
    typeAnswer.textAlignment = .right
    cell.addSubview(typeAnswer)

    //Rego Label
    let rego = UILabel()
    rego.frame = CGRect(x: 0, y: 0, width: 110, height: 21)
    rego.center = CGPoint(x: cell.center.x - 47.5, y: cell.center.y + 18.5)
    rego.text = "Registration:"
    rego.font = UIFont(name: "Montserrat-Medium", size: 17)
    cell.addSubview(rego)

    //rego answer
    let regoAnswer = UILabel()
    regoAnswer.frame = CGRect(x: 0, y: 0, width: 160, height: 21)
    regoAnswer.center = CGPoint(x: cell.center.x + 92.5, y: cell.center.y + 18.5)
    regoAnswer.text = ""
    regoAnswer.font = UIFont(name: "Montserrat-Light", size: 17)
    regoAnswer.textAlignment = .right
    cell.addSubview(regoAnswer)

    let save = planeArray[indexPath.row]
    typeAnswer.text = save.type
    regoAnswer.text = save.rego
    return cell
}


override func viewWillAppear(_ animated: Bool) {
    //Making the table update itself when user logs the plane
    fetchData()
    tableView.reloadData()
}

}


Solution

  • UITableView reuses the cells. In your case, when the cells are created the first time, you add a UILabel to it. The next time this cell is loaded, UITableView reuses the existing cell and cellForRowAt adds another UILabel to this cell. The proper implementation is to create a custom UITableViewCell and reset the value of all the attributes in cellForRowAt method.

    You can try something like below (please note that this is just a rough implementation and it is assumed that you know the basics of ios programming. If that is not the case, I'd recommend researching it a bit):

    Add a custom tableview cell

    class CustomTableViewCell: UITableViewCell {
        @IBOutlet weak var imgViewPlane: UIImageView!
        @IBOutlet weak var lblType: UILabel!
        @IBOutlet weak var lblTypeAnswer: UILabel!
        @IBOutlet weak var lblRego: UILabel!
        @IBOutlet weak var lblRegoAnswer: UILabel!
    }
    

    Create a dynamic prototype cell in storyboard tableview and link the IBOutlets. Then in your cellForRowAt, do something like this

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomTableViewCell
    
        cell.imgViewPlane.image = planeImage
        cell.lblType.text = "Type:"
        cell.lblTypeAnswer.text = planeArray[indexPath.row].type
        cell.lblRego.text = "Registration:"
        cell.lblRegoAnswer.text = planeArray[indexPath.row].rego
    
        return cell
    }