UITableViewCell custom button image displaying incorrectly after UITableView scrolling - ios

I have UITableViewCells that contains a custom button, which is an image. When the button is pressed, the image changes. However, when I scroll up and down the tableView, the cell that is tapped no longer displays the right image, or the "tapped" image is displaying elsewhere. I understand this problem is related to the cells being dequeued and reused. What I've tried to do is create a delegate in my custom cell class. The delegate calls a method in the tableViewController class. I am doing this with the code
self.delegate?.pressedButtonInCell?(self)
However this is not working..why?
=======custom cell class====
import UIKit
#objc protocol BoostCellDelegate{
optional func pressedButtonInCell(cell:BoostCell)
optional func displayAlert (title:String , error: String)
}
class BoostCell: UITableViewCell {
var delegate:BoostCellDelegate?
var boosted = false
var boostedButton = UIImage(named: "boostButtons.png")
#IBOutlet var userImage: UIImageView!
#IBOutlet var name: UILabel!
#IBOutlet var createdAt: UILabel!
#IBOutlet var boostButton: UIButton!
#IBAction func boostPressed(sender: UIButton) {
let objectId = sender.titleLabel!.text!
var query = PFQuery(className:"boostPost")
query.getObjectInBackgroundWithId(objectId) {
(boostPost: PFObject!, error: NSError!) -> Void in
if error != nil {
NSLog("%#", error)
self.delegate?.displayAlert?("Something's gone wrong", error: "Might be the bad reception")
} else {
if boostPost != nil{
if boostPost["boostedBy"] != nil {
var boostedByArray : NSArray = boostPost["boostedBy"] as NSArray
if boostedByArray.containsObject(PFUser.currentUser().username){
println("username already boosted")
}
else{
var boostNumber = boostPost["boostNumber"] as Int
boostNumber++
boostPost["boostNumber"] = boostNumber
boostPost.addObject(PFUser.currentUser().username, forKey:"boostedBy")
boostPost.saveInBackground()
println("new username added to boosted")
self.delegate?.pressedButtonInCell?(self)
}
}
else if boostPost["boostedBy"] == nil{
var boostNumber = boostPost["boostNumber"] as Int
boostNumber++
boostPost["boostNumber"] = boostNumber
boostPost.addObject(PFUser.currentUser().username, forKey:"boostedBy")
boostPost.saveInBackground()
println("new username added to boosted")
self.delegate?.pressedButtonInCell?(self)
=====================================code in tableViewController class =========
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as BoostCell
cell.name.text = userNewNames[indexPath.row]
cell.content.text = userNewContent[indexPath.row]
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
cell.createdAt.text = userCreatedAt[indexPath.row]
cell.delegate = self
if userBoostedBy[indexPath.row].count == 2 {
cell.boostedBy.text = "1 boost"
}
else if userBoostedBy[indexPath.row].count > 2 {
var count = userBoostedBy[indexPath.row].count - 1
cell.boostedBy.text = "\(count) boosts"
}
else {
cell.boostedBy.text = "0 boost"
}
if (userButtonState[indexPath.row] == true) {
cell.boostButton.setImage(self.boostedButton, forState: UIControlState.Normal)
}
else {
cell.boostButton.setImage(self.unBoostedButton, forState: UIControlState.Normal)
}
userImagesFiles[indexPath.row].getDataInBackgroundWithBlock({ (imageData:NSData!, error:NSError!) in
if error == nil {
let image = UIImage(data: imageData)
cell.userImage.image = image
}
})
cell.boostButton.setTitle(objectIds[indexPath.row], forState: UIControlState.Normal)
func pressedButtonInCell(cell:BoostCell)
{
cell.boostButton.setImage(self.boostedButton, forState: UIControlState.Normal)
}
// function to display the alert message
func displayAlert (title:String , error: String){
var alert = UIAlertController(title: title, message: error, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: {
action in
}))
self.presentViewController(alert, animated: true, completion: nil)
}
return cell
}

It looks like your cellForRowAtIndexPath method uses userButtonState[indexPath.row] to determine whether to change the image for the boostButton. So, you just need to amend your pressedButtonInCell method to set userButtonState for the relevant row:
func pressedButtonInCell(cell:BoostCell)
{
cell.boostButton.setImage(self.boostedButton, forState: UIControlState.Normal)
if let indexPath = self.tableView.indexPathForCell(cell) {
userButtonState[indexPath.row] = true
}
}
(This assumes self.tableView points to your table view; if not amend to provide the correct reference). Now, when you press the button on a row, the relevant element of the array will be set to true. If the row then scrolls off screen, when it scrolls back on again, the tableView:cellForRowAtIndexPath: method will check userButtonState, find it to be true and so update the image for the button. Conversely, if userButtonState is false, the image is reset (just in case the re-used cell happened to have the boostedButton image).

Related

Cannot dynamically add custom cell from 2nd data source

I've spent 20+ hours searching/trying different solutions just for this specific problem and cannot make it work. I'm just starting to learn Swift, so be gentile... I made sure to exhaust all options before asking for help.
I have 3 view controllers (VC1: form with 5 text fields, VC2: tableview for displaying gathered data, VC3: 2nd form with 3 text fields and an image picker)
screen shot of storyboard
VC1 gathers the text field data, pass to VC2, and use custom cell #1 to add to the tableview. I used the exact same methods to do the same for VC3 (changing the cell identifier, and using if/else to determine which section to add to) but cannot get any results. At first I thought my data wasn't being passed, but that checks out ("Finihed & Send" button set to alert and prints variable's text) next I thought it was my logic, but custom cell #1 works... I've been staring it this code for so long, I have horrible dreams about it. I feel like this should be obtainable with the techniques I'm applying but wonder if I am I wandering into Core Data territory but just don't know it.
tableview with only 1 custom cell
My intent is to have VC1 add a cell (custom cell 1) at indexPath.row 0 and VC3 to add (custom cell 2) indexPath.row >= 1. Everything works like I want it to with the exception of adding a second custom cell to the tableview.
addWorkViewController
import UIKit
import MapKit
import CoreLocation
class mainVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, CLLocationManagerDelegate, UITextFieldDelegate {
#IBOutlet weak var scrollviewWORK: UIScrollView!
#IBOutlet weak var typeWORK: UISegmentedControl!
#IBOutlet weak var locationWORK: UITextField!
#IBOutlet weak var positionWORK: UISegmentedControl!
#IBOutlet weak var priceWORK: UITextField!
#IBOutlet weak var photo1WORK: UIImageView!
#IBOutlet weak var descriptionWORK: UITextView!
/// Prepare Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case is WorkOverview:
let workDest: WorkOverview = segue.destination as! WorkOverview
var cost = ""
var snap = UIImage()
if (priceWORK.text == nil) {
cost = ""
} else {
cost = priceWORK.text!
}
if (photo1WORK.image == nil) {
snap = UIImage()
} else {
snap = photo1WORK.image!
}
workDest.workLocation = locationWORK.text!
workDest.workDescription = descriptionWORK.text!
workDest.workPrice = cost
workDest.workPhoto = snap
case is PlantList:
let plantDest: PlantList = segue.destination as! PlantList
plantDest.placeholder = ""
default:
break
}
}
/// END Segue Preparation
/// Save to List Button
#IBAction func saveToListBTN(_ sender: UIButton) {
performSegue(withIdentifier: "unwindToList", sender: self)
}
/// END Save to List Button
/// Insert Plant
#IBAction func insertPlant(_ sender: UIButton) {
performSegue(withIdentifier: "toPlantListSegue", sender: self)
}
var addedPlant: String? = ""
/// END Insert Plant
/// Clear All Button
#IBAction func clearAllBTN(_ sender: UIButton) {
}
/// END Clear All Button
/// Segmented Controller - Work Type
#IBAction func positionChanged(_ sender: UISegmentedControl) {
switch positionWORK.selectedSegmentIndex {
case 0:
locationWORK.text? += " - Front"
case 1:
locationWORK.text? += " - Back"
case 2:
locationWORK.text? += " - Side"
default:
break
}
}
#IBAction func indexChanged(_ sender: UISegmentedControl) {
switch typeWORK.selectedSegmentIndex {
case 0:
descriptionWORK.text = "Provide and install "
case 1:
descriptionWORK.text = "Replace "
case 2:
descriptionWORK.text = "Remove and dispose of "
default:
break
}
}
/// END Segmented Controller - Work Type
/// ScrollView Keyboard Adjust
func textFieldDidBeginEditing(_ textField: UITextField) {
if (textField == priceWORK){
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 205), animated: true)
} else {}
}
func textFieldDidEndEditing(_ textField: UITextField) {
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
/// END Scrollview Keyboard Adjust
/// VIEWDIDLOAD
override func viewDidLoad() {
super.viewDidLoad()
// Toolbar
let toolBar = UIToolbar()
toolBar.sizeToFit()
let backArrow = UIBarButtonItem.init(image: #imageLiteral(resourceName: "backArrow"), style: .plain, target: nil, action: #selector(backArrowClicked))
let spacerA = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let workDoneBtn = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneBtnClicked))
let spacerB = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let nextArrow = UIBarButtonItem.init(image: #imageLiteral(resourceName: "nextArrow"), style: .plain, target: nil, action: #selector(nextArrowClicked))
toolBar.setItems([backArrow, spacerA, workDoneBtn, spacerB, nextArrow], animated: false)
toolBar.setItems([backArrow, spacerA, workDoneBtn, spacerB, nextArrow], animated: false)
locationWORK.inputAccessoryView = toolBar
priceWORK.inputAccessoryView = toolBar
descriptionWORK.inputAccessoryView = toolBar
}
/// END VIEWDIDLOAD
/// Toolbar - Done Button
#objc func doneBtnClicked() {
view.endEditing(true)
}
/// END Toolbar - Done Button
/// Arrow to Next TextField
#objc func nextArrowClicked() {
if (locationWORK.isFirstResponder) {
descriptionWORK.becomeFirstResponder()
} else if (descriptionWORK.isFirstResponder) {
priceWORK.becomeFirstResponder()
} else if (priceWORK.isFirstResponder) {
view.endEditing(true)
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
}
/// END Arrow to Next TextField
/// Arrow to Previous TextField
#objc func backArrowClicked() {
if (locationWORK.isFirstResponder) {
view.endEditing(true)
scrollviewWORK.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
} else if (descriptionWORK.isFirstResponder) {
locationWORK.becomeFirstResponder()
} else if (priceWORK.isFirstResponder) {
descriptionWORK.becomeFirstResponder()
}
}
/// END Arrow to Previous TextField
/// Image Select from Library & Camera
#IBAction func takePhotoONE(_ sender: UIButton) {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
let actionSheet = UIAlertController(title: "Want to add a photo?", message: "Please choose a source.", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler: { (action:UIAlertAction) in
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePickerController.sourceType = .camera
self.present(imagePickerController, animated: true, completion: nil)
}else{
print("Camera is not available")
}
}))
actionSheet.addAction(UIAlertAction(title: "Photo Library", style: .default, handler: { (action:UIAlertAction) in imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)
}))
actionSheet.addAction(UIAlertAction(title: "Remove Photo", style: .destructive, handler: { (action:UIAlertAction) in self.photo1WORK.image = nil}))
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
photo1WORK.image = image
picker.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
/// END Image Select from Library & Camera
/// GPS Location
let addressManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let streetAddress = locations[0]
CLGeocoder().reverseGeocodeLocation(streetAddress) { (placemark, error) in
if error != nil
{
print ("Sorry, there has been an error.")
}
else
{
if let place = placemark?[0]
{
if place.subThoroughfare != nil
{
self.locationWORK.text = "\(place.subThoroughfare!) \(place.thoroughfare!)"
}
}
}
}
}
#IBAction func getGPS(_ sender: UIButton) {
// Address
addressManager.delegate = self
addressManager.desiredAccuracy = kCLLocationAccuracyBest
addressManager.requestWhenInUseAuthorization()
addressManager.startUpdatingLocation()
}
/// END GPS Location
}
TableViewController
import UIKit
class WorkOverview: UIViewController {
#IBOutlet weak var listTableView: UITableView!
#IBAction func addWorkBTN(_ sender: UIButton) {
performSegue(withIdentifier: "overviewToWorkSegue", sender: self)
}
#IBAction func unwindToList(segue:UIStoryboardSegue) { }
#IBAction func finishedBTN(_ sender: UIButton) {
let alertController = UIAlertController(title: "Data Pass Test", message:
workLocation, preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default,handler: nil))
self.present(alertController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
property = createArray()
workPJs = workArray()
listTableView.delegate = self
listTableView.dataSource = self
}
var propForm = String()
var propName = String()
var propCity = String()
var propDate = String()
var propDue = String()
var propRep = String()
var workLocation = String()
var workDescription = String()
var workPrice = String()
var workPhoto = UIImage()
var workPJs: [WorkManager] = []
var property: [TaskManager] = []
func workArray() -> [WorkManager] {
var tempWork: [WorkManager] = []
let work1 = WorkManager(location: workLocation, description: workDescription, price: workPrice, photo: workPhoto)
tempWork.append(work1)
return tempWork
}
func createArray() -> [TaskManager] {
var tempProperty: [TaskManager] = []
let prop1 = TaskManager(title: propForm, property: propName, city: propCity, date: propDate, due: propDue, rep: propRep)
tempProperty.append(prop1)
return tempProperty
}
}
extension WorkOverview: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return property.count
} else {
return workPJs.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let prop = property[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "PropertyCell", for: indexPath) as! PropertyCell
cell.setProperty(prop: prop)
return cell
} else if indexPath.section == 1 {
let wrk = workPJs[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "WorkCell", for: indexPath) as! WorkCell
cell.setWork(wrk: wrk)
return cell
}
return UITableViewCell()
}
}
WorkCell class
import UIKit
class WorkCell: UITableViewCell {
#IBOutlet weak var theLocation: UILabel!
#IBOutlet weak var theDescription: UILabel!
#IBOutlet weak var thePrice: UILabel!
#IBOutlet weak var thePhoto: UIImageView!
func setWork(wrk: WorkManager) {
theLocation.text = wrk.location
theDescription.text = wrk.description
thePrice.text = wrk.price
thePhoto.image = wrk.photo
}
}
WorkManager class
import UIKit
class WorkManager {
var location: String
var description: String
var price: String
var photo: UIImage
init(location: String, description: String, price: String, photo: UIImage){
self.location = location
self.description = description
self.price = price
self.photo = photo
}
}
in ViewDidLoad add this code according to names of your cell
tableView.registerNib(UINib(nibName: "cell xib", bundle: nil), forCellReuseIdentifier: "cell name")
in your case you have to register both nib of your custom cell.
i have feelings your section if else condition is wrong. better to debug.
Here, you need to define the second TableViewCell class but in your code only WorkCell class define. so, i hope below link is help you.
UITableview with more than One Custom Cells with Swift
I changed the if statement in cellForRowAt func to a switch case (don't know if it helped or not) but I know adding .reloadData() to my tableview in the viewDidAppear func got it working. Still needs some debugging but that's life.

wrong sequence tableview.reloaddata() in swift

I'm writing a code for a 'checklist' creator'. There are several parts in the application. One is for creating a new checklist from scratch. Based on the choices the user makes a row of a checklist will have a checkbox, input field or yes/no segmentswitch. I managed to set this all up using Parse, but there is one problem.
When I click on a button, a new row is added and I can enter some date here.
Then I can click that same button again and the same happens etc.
However, when I'm adding my fifth row, the fifth row will become the current first row and the first row is now an empty one. I've added the code below. It's quitte long due to all different variable's, but I think the most important ones are the
'add' IBaction and the part where the table view is configured. Can someone help me solving my problem?
Thanks,
Sven
Picture: This is what the mistake looks like. The last row is actually the one I configured as first row. The first row is now empty (off screen)
import UIKit
import Parse
import Foundation
var emptyone = true
class Homescreen: UITableViewController, UITextFieldDelegate {
var topField = [""]
var first = Bool()
var label1 = [""]
var switchEnab = [false]
var fieldEnab = [false]
var yesnoEnab = [false]
var topSegmentEnab = [false]
var topFieldEnab = [false]
var segmentName0 = [""]
var segmentName1 = [""]
var segmentName2 = [""]
var messageEnab = [false]
var message1 = [""]
var labelEnab = [false]
var field1 = [""]
var switch1 = [false]
var topSegment = [1]
var yesno1 = [1]
var nummer = Int()
// Add a new row
#IBAction func add(sender: AnyObject) {
first = true
self.label1.append("" as String!)
self.topField.append("" as String!)
self.segmentName0.append("" as String!)
self.segmentName1.append("" as String!)
self.segmentName2.append("" as String!)
self.message1.append("" as String!)
self.field1.append("" as String!)
self.switchEnab.append(false)
self.fieldEnab.append(false)
self.topFieldEnab.append(false)
self.yesnoEnab.append(false)
self.topSegmentEnab.append(false)
self.messageEnab.append(false)
self.labelEnab.append(false)
self.switch1.append(false)
self.yesno1.append(1)
self.topSegment.append(1)
self.tableView.reloadData()
}
#IBOutlet var celOpmaak: UITableView!
// Function initators
var activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
func busy(){
activityIndicator = UIActivityIndicatorView(frame: CGRectMake(0, 0, 50, 50))
activityIndicator.center = self.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
view.addSubview(activityIndicator)
activityIndicator.startAnimating()
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
}
func displayAlert(title: String, message: String){
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { (action) in
}))
self.presentViewController(alert, animated: true, completion: nil)
}
// Final save of the checklist
#IBAction func saveButton(sender: AnyObject) {
if last == true{
dispatch_async(dispatch_get_main_queue()) {
self.first = false
self.tableView.reloadData()
}
busy()
let newValues = PFObject(className: "clTypes")
newValues["taskName"] = ""
newValues["dateStamp"] = " - - "
newValues["topSegmentEnab"] = self.topSegmentEnab
newValues["segmentName0"] = self.segmentName0
newValues["segmentName1"] = self.segmentName1
newValues["segmentName2"] = self.segmentName2
newValues["topFieldEnab"] = self.topFieldEnab
newValues["topField"] = self.topField
newValues["label1"] = self.label1
newValues["label1Enab"] = self.labelEnab
newValues["fieldEnab"] = self.fieldEnab
newValues["field1"] = self.field1
newValues["switchEnab"] = self.switchEnab
newValues["switch1"] = self.switch1
newValues["yesnoEnab"] = self.yesnoEnab
newValues["yesno1"] = self.yesno1
newValues["topSegment"] = self.topSegment
newValues["messageEnab"] = self.messageEnab
newValues["message1"] = self.message1
newValues["clType"] = clType1
newValues["checklistTitle"] = checklistTitle1
newValues.saveInBackground()
self.activityIndicator.stopAnimating()
UIApplication.sharedApplication().endIgnoringInteractionEvents()
self.displayAlert("Checklist saved", message: "The checklist has been saved to the server succesfully")
self.dismissViewControllerAnimated(true, completion: nil)
self.tableView.reloadData()
self.performSegueWithIdentifier("overview", sender: self)
}else{
displayAlert("No final button", message: "Please add a 'last' cell to the checklist")
}
}
// VIEWDIDLOAD
override func viewDidLoad() {
super.viewDidLoad()
self.label1.removeAll(keepCapacity: true)
self.field1.removeAll(keepCapacity: true)
self.message1.removeAll(keepCapacity: true)
self.topField.removeAll(keepCapacity: true)
self.segmentName0.removeAll(keepCapacity: true)
self.segmentName1.removeAll(keepCapacity: true)
self.segmentName2.removeAll(keepCapacity: true)
self.switchEnab.removeAll(keepCapacity: true)
self.yesnoEnab.removeAll(keepCapacity: true)
self.topFieldEnab.removeAll(keepCapacity: true)
self.fieldEnab.removeAll(keepCapacity: true)
self.messageEnab.removeAll(keepCapacity: true)
self.labelEnab.removeAll(keepCapacity: true)
self.switch1.removeAll(keepCapacity: true)
self.topSegmentEnab.removeAll(keepCapacity: true)
self.yesno1.removeAll(keepCapacity: true)
self.topSegment.removeAll(keepCapacity: true)
last = false
//var helloWorldTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector("sayHello"), userInfo: nil, repeats: true)
toeVoegen.wraps = false
toeVoegen.autorepeat = false
toeVoegen.maximumValue = 80
self.tableView.reloadData()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return label1.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell! = tableView.dequeueReusableCellWithIdentifier("cel", forIndexPath: indexPath) as! creatorCellTableViewCell
if cell == nil{
print("hoi")
let cell = tableView.dequeueReusableCellWithIdentifier("cel", forIndexPath: indexPath) as! creatorCellTableViewCell
if first == true{
cell.option1.delegate = self
func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
}
print(self.label1)
print(Int(toeVoegen.value))
label1[indexPath.row] = cell.labelField.text!
}else{
// This part is only called when the 'final save button' has been activated
label1[indexPath.row] = cell.labelField.text!
field1[indexPath.row] = ""
yesno1[indexPath.row] = 1
topField[indexPath.row] = ""
// switchEnab, fieldEnab, yesnoEnab
if cell.degreeField.selectedSegmentIndex == 1{
labelEnab[indexPath.row]=false
topFieldEnab[indexPath.row]=true
topSegmentEnab[indexPath.row]=true
segmentName0[indexPath.row] = ""
segmentName1[indexPath.row] = ""
segmentName2[indexPath.row] = ""
if cell.typeField.selectedSegmentIndex == 0{
switchEnab[indexPath.row] = false
fieldEnab[indexPath.row] = true
yesnoEnab[indexPath.row] = true
messageEnab[indexPath.row]=false
message1[indexPath.row]=cell.messageField.text!
}else if cell.typeField.selectedSegmentIndex == 1{
switchEnab[indexPath.row] = true
fieldEnab[indexPath.row] = true
yesnoEnab[indexPath.row] = false
if cell.messageSwitch.on == true{
messageEnab[indexPath.row]=cell.messageSwitch.on
message1[indexPath.row]=cell.messageField.text!
}else{
messageEnab[indexPath.row]=false
message1[indexPath.row]="hallo"
}
}else if cell.typeField.selectedSegmentIndex == 2{
labelEnab[indexPath.row]=false
switchEnab[indexPath.row] = true
fieldEnab[indexPath.row] = false
yesnoEnab[indexPath.row] = true
messageEnab[indexPath.row]=false
message1[indexPath.row]=""
}
}else if cell.degreeField.selectedSegmentIndex == 0{
switchEnab[indexPath.row]=true
fieldEnab[indexPath.row]=true
yesnoEnab[indexPath.row]=true
labelEnab[indexPath.row]=true
messageEnab[indexPath.row]=false
message1[indexPath.row]=""
if cell.topVeld.selectedSegmentIndex == 0{
topSegmentEnab[indexPath.row] = false
topFieldEnab[indexPath.row] = true
segmentName0[indexPath.row] = cell.option1.text!
segmentName1[indexPath.row] = cell.option2.text!
segmentName2[indexPath.row] = cell.option3.text!
} else{
topSegmentEnab[indexPath.row]=true
topFieldEnab[indexPath.row]=false
}
}
}
}
return cell
}
Most likely this is related to the fact that you are reusing cells. Every time you do this:
let cell = tableView.dequeueReusableCellWithIdentifier("cel",
forIndexPath: indexPath) as! creatorCellTableViewCell
there is a chance (a very likely chance as soon as you have more rows than fit on your screen) that a different cell is returned than was originally at that index. For example, as soon as you scroll up to row #5 and row #1 is no longer on the screen you could be getting the same exact cell that was at row #1 being displayed at row #5. When you are reusing cells you have to set the state on the cell every single time. For example:
let cell = tableView.dequeueReusableCellWithIdentifier("cel",
forIndexPath: indexPath) as! creatorCellTableViewCell
cell.labelField.text! = label1[indexPath.row]
// do this for every field that needs to be set

Backend not updating after first tap and grabbing previous value after sequential taps

I am working on a "to do" app in Swift and using Firebase as my backend.
On the first tap on my checkbox to signal that a task has been done, the UI updates and the variable should become true (a checkmark appears) but firebase and the local instance of the bool value for that item do not update to false. After a second tap, the UI continues to have normal functionality (the checkmark disappears); but firebase and the local instance both update to true. Following taps from then on are reversed (True for no checkmark and false for checkmark). When I stop the simulator in Xcode and re-run, the values and UI that load in are correct. It is not until I try and tap on the checkmark that I get the incorrect functionality again. Firebase only updates after the second tap and change in UI. I have include just the code that pertains to the checkbox. I think the problem happens in the doneHit function but I am not quite sure why its happening.
Please help. If there is an easier way to go about this, that would be helpful too.
protocol TaskCellDelegate {
func doneHit(cell : TaskCell)
}
class TaskCell : UITableViewCell {
var delegate : TaskCellDelegate?
#IBOutlet weak var label: UILabel!
#IBOutlet weak var checkBox: CheckBox!
#IBOutlet weak var detailLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
checkBox.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
}
func buttonClicked(sender:UIButton) {
delegate?.doneHit(self)
}
}
class CheckBox: UIButton {
//images
let checkedImage = UIImage(named: "checkedbox")
let unCheckedImage = UIImage(named: "uncheckedbox")
//bool propety
var isChecked:Bool = false{
didSet {
if isChecked == true{
self.setImage(checkedImage, forState: .Normal)
}
else {
self.setImage(unCheckedImage, forState: .Normal)
}
}
}
override func awakeFromNib() {
self.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
}
func buttonClicked(sender:UIButton) {
if(sender == self){
if isChecked == false {
isChecked = true
} else {
isChecked = false
}
}
}
}
func doneHit(cell:TaskCell) {
if let ip = tableView.indexPathForCell(cell) {
var task = tasks[ip.row]
task.done = cell.checkBox.isChecked
if task.done == true {
task.completedBy = "Completed by: \(self.user)"
cell.label.textColor = UIColor.grayColor()
}
else {
task.completedBy = ""
cell.label.textColor = UIColor.blackColor()
}
let taskNameRef = self.ref.childByAppendingPath("tasks/\(task.title)")
let completedByData = ["completedBy": "\(self.user)"]
let doneData = ["done": task.done]
taskNameRef.updateChildValues(completedByData)
taskNameRef.updateChildValues(doneData)
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! TaskCell
// Configure the cell...
cell.selectionStyle = .None
let idx = tasks[indexPath.row]
if let label = cell.viewWithTag(1) as? UILabel {
label.text = idx.title
if idx.done == true {
label.textColor = UIColor.grayColor()
} else {
label.textColor = UIColor.blackColor()
}
if let checkBox = cell.viewWithTag(2) as? CheckBox {
checkBox.isChecked = idx.done
}
if let userCompleted = cell.viewWithTag(3) as? UILabel {
if idx.done == true {
userCompleted.text = "Completed By: \(idx.completedBy)"
}
else {
userCompleted.text = ""
}
}
}
print("Task.done is: \(idx.done)")
print("isChecked is:\(cell.checkBox.isChecked)")
cell.delegate = self
return cell
}
Figured it out myself after 3 days. I scrapped the Checkbox class and turned the checkbox into a UIImageView and added a gesture recognizer. The rest was just moving the Checkbox logic into the TaskTVC class under the doneHit method.

Issue with updating clicked table view cell

I have an issue that has been vexing me for a while and I feel like I am so close. Here is the situation. I have a tableview that holds different calculus videos and I would like to give my students the ability to download the videos for playing offline. That part works fine. The problem I am having is that when the user clicks on the download button, the download button should hide and show an activity indicator as well as a progress bar (all of which are in a custom cell). I have two problems:
1) The download button does not automatically hide and show the activity view unless I scroll away from it and come back to it.
2) If I do scroll down all of a sudden random cells will not have the download button anymore.
This must be due to the fact that cells are being reused but I thought I was doing things properly although obviously there is a mistake. I will post the relevant code snippets below:
cellForRowAtIndexPath Code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("VideoCell", forIndexPath: indexPath) as! TableViewCell
let currentVideo = videos[indexPath.section][indexPath.row]
cell.downloadButton.addTarget(self, action: "downloadButtonPressed:", forControlEvents: .TouchUpInside)
cell.video = currentVideo
return cell
}
Custom Table View Cell Code:
class TableViewCell: UITableViewCell {
#IBOutlet weak var videoTitleLabel: UILabel!
#IBOutlet weak var progressView: UIProgressView!
#IBOutlet weak var customAccessoryView: UIView!
#IBOutlet weak var downloadButton: UIButton!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
let userDefaults = NSUserDefaults.standardUserDefaults()
var video: Video? {
didSet {
updateUI()
}
}
private func updateUI() {
guard let video = video else {
print("video should not be nil")
return
}
videoTitleLabel.text = video.title
if video.downloadStatus.isDownloading {
progressView.hidden = false
progressView.progress = video.downloadStatus.downloadProgress
downloadButton.hidden = true
activityIndicator.hidden = false
activityIndicator.startAnimating()
} else {
progressView.hidden = true
activityIndicator.stopAnimating()
}
if video.downloadStatus.isSaved {
activityIndicator.stopAnimating()
progressView.hidden = true
downloadButton.hidden = true
}
}
Download request
func downloadButtonPressed(sender: UIButton) {
let buttonPosition = sender.convertPoint(CGPointZero, toView: self.tableView)
guard let indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition) else {
print("Error getting index path from button press")
return
}
let section = indexPath.section
let row = indexPath.row
print("Button pressed at section \(section) and row \(row)")//correct row and section
//Code to save Video to Documents directory goes here
let currentVideo = videos[section][row]
guard !currentVideo.downloadStatus.isSaved else {
print("Video is already saved")
return
}
guard let url = currentVideo.url else {
print("Video not found...url is invalid")
return
}
let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)
Alamofire.download(.GET, url, destination: destination)
.progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
let progress = Float(totalBytesRead) / Float(totalBytesExpectedToRead)
currentVideo.downloadStatus.isDownloading = true
currentVideo.downloadStatus.downloadProgress = progress
}.response { request,response,data, error in
if let error = error {
print("Failed with error: \(error)")
} else {
print("Downloaded file successfully")
currentVideo.downloadStatus.isDownloading = false
currentVideo.downloadStatus.isSaved = true
self.saveDownloadStatusInDefaultsForVideo(currentVideo, isSaved: true)
}
print("Files currently in the documents directory:")
self.printDocumentsDirectoryContents() //file is there
}
}
In your class TableViewCell you're setting the property hidden of downloadButton to true under certain circumstances.
If you want the button to be visible by default set the property explicitly to false when reusing the cell

Button with constraints inside PersonCell hides half of the button Swift

Created a button inside a personCell which either shows Follow / Unfollow.
I've added the following constraints to the button:
Align Center Y to: Superview
Height = 43
Trailing space to: Superview
With or without the constraints it still cuts of a side of the button.
If the button was supposed to display "follow" it would only show "ow"
Code for PersonCell
class PersonCell: UITableViewCell {
#IBOutlet weak var followButton: UIButton!
var isFollowing: Bool?
var user: PFUser?
{
didSet
{
self.configure()
}
}
override func awakeFromNib()
{
super.awakeFromNib()
self.isFollowing = false
self.followButton?.hidden = true
}
override func prepareForReuse()
{
super.prepareForReuse()
self.isFollowing = false
self.followButton?.hidden = true
self.textLabel?.text = ""
self.user = nil
}
func configure()
{
if let constUser = user
{
self.textLabel?.text = constUser.username
// are we following this person?
NetworkManager.sharedInstance.isFollowing(constUser, completionHandler: {
(isFollowing, error) -> () in
if let _ = error
{
// Alert the user, or otherwise modify the UI
}
else
{
self.isFollowing = isFollowing
if isFollowing == true
{
let image = UIImage(named: "UnfollowButton")
self.followButton?.setImage(image, forState: .Normal)
}
else
{
let image = UIImage(named: "FollowButton")
self.followButton?.setImage(image, forState: .Normal)
}
self.followButton?.hidden = false
}
})
}
}
#IBAction func didTapFollow(sender: UIButton)
{
self.followButton?.enabled = false
let newValue = !(self.isFollowing == true)
NetworkManager.sharedInstance.updateFollowValue(newValue, user: self.user!) { (error) -> () in
self.followButton?.enabled = true
let image = (newValue == true) ? UIImage(named: "UnfollowButton") : UIImage(named: "FollowButton")
self.followButton?.setImage(image, forState: .Normal)
self.isFollowing = newValue
}
}
}
Im not yet allowed to post images but see this if you need to view how it looks:
http://postimg.org/image/jnf47mnr1/
Thankx in advance!

Resources