Screen Hangs and terminated - ios

Screen gets hanged and terminated. I have trace down that because of my while loop ,screens hanged and app terminated.
While loop runs only of 10 times. when I reopen my application my Data is got saved.
How to null or deallocate the UITableViewcell
let itemcell:TableViewCell = self.tableView.cellForRow(at: item) as! TableViewCell
Since self.tableView.dequeueReusableCell(withIdentifier: "listdetails", for: item) returning empty value .I have to use cellforRow.
Please provide input on this.
#IBAction func saveButton(_ sender: UIBarButtonItem) {
let ndx = IndexPath(row:0, section: 0)
var counter:Int = 0
let cell = self.tableView.cellForRow(at: ndx) as! TableViewCell
let locationNameCell = self.tableView.cellForRow(at: IndexPath(row:1, section: 0)) as! TableViewCell
let shoppingDetails = ShoppingDetails(context:managedContext)
let storeName = cell.storeName.text!
let storeFlag = validateShoppingList(storeName: storeName)
if storeFlag == true {
let shoppingDetails = ShoppingDetails(context:managedContext)
var listDetails :ListDetails!
while counter < sectionRowCount {
let item = IndexPath(row:counter, section: 1)
var itemcell = self.tableView.cellForRow(at: item) as! TableViewCell
let list = shoppingDetails.shoppingToList?.mutableCopy() as! NSMutableSet
listDetails = ListDetails(context:managedContext)
let listItem = itemcell.listItem.text!.trimmingCharacters(in: .whitespaces)
listDetails.listItem = listItem
var qty = itemcell.qtytextfield.text!
if qty.isEmpty {
qty = "0"
}
var units = itemcell.unitstextfield.text!
if units.isEmpty {
units = ""
}
listDetails.qty = Int64(qty)!
listDetails.units = itemcell.unitstextfield.text!
listDetails.isChecked = false
list.add(listDetails)
shoppingDetails.addToShoppingToList(list)
counter = counter + 1
list.removeAllObjects()
}
shoppingDetails.storeName = storeName
if locationNameCell.locationName.text != nil {
shoppingDetails.location = locationNameCell.locationName.text!
}
shoppingDetails.initialLetter = (cell.storeName.text!).first?.description
let seqNo:Int = ShoppingDetails.getSeqNo(managedObjectContext: managedContext) + 1
shoppingDetails.seqNo = Int32(seqNo)
coreData.saveContext()
let vc = storyboard!.instantiateViewController(withIdentifier: "StoreDisplayController") as! StoreDisplayController
vc.managedContext = managedContext
vc.coreData = coreData
vc.storeName = cell.storeName.text!
// managedContext.delete(shoppingDetails)
// managedContext.delete(listDetails)
// self.present(vc, animated:true, completion:nil)
// self.navigationController?.popToViewController(vc, animated: true)
self.navigationController?.pushViewController(vc, animated: true)
}
enter image description here

Save your data in this method as the user will scroll to fill all content
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
// ...
//fill model array parameters from cell properties before data lost
var itemcell = self.tableView.cellForRow(at: indexPath) as! TableViewCell
let list = shoppingDetails.shoppingToList?.mutableCopy() as! NSMutableSet
listDetails = ListDetails(context:managedContext)
let listItem = itemcell.listItem.text!.trimmingCharacters(in: .whitespaces)
listDetails.listItem = listItem
var qty = itemcell.qtytextfield.text!
if qty.isEmpty {
qty = "0"
}
var units = itemcell.unitstextfield.text!
if units.isEmpty {
units = ""
}
listDetails.qty = Int64(qty)!
listDetails.units = itemcell.unitstextfield.text!
listDetails.isChecked = false
list.add(listDetails)
shoppingDetails.addToShoppingToList(list)
}
then when save button is clicked check that model array for empty item and scroll to that indexpath to fill

Related

Custom UITableViewCells not keeping order or correct checkmarks

I am having trouble with my custom uitableviewcells not reloading in the proper order and the checkmarks not matching with the correct item.
Here is my starting point:
As an example I scramble it like this:
After closing and restarting, this is what I get:
This is the relevant portion of functions:
func loadData() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = ListItem.createFetchRequest()
request.returnsObjectsAsFaults = false
var priority = 0
if let results = try? context.fetch(request) {
for result in results {
priority = Int(result.priorityLevel)!
items[priority - 1].insert(result, at: 0)
}
}
// This is to show me what gets loaded
var z = 0
var x = 0
for i in items {
x = 0
for _ in items[z] {
print(i[x].isChecked, i[x].priorityLevel, i[x].itemText)
x += 1
}
z += 1
}
// This is to show me what gets loaded
}
func setChecked(cell: ListItemCell) {
guard let indexPath = self.listTableView.indexPath(for: cell) else {
// Note, this shouldn't happen - how did the user tap on a button that wasn't on screen?
return
}
let cell = listTableView.cellForRow(at: indexPath) as! ListItemCell
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = ListItem.createFetchRequest()
request.returnsObjectsAsFaults = false
var tempItems: [[ListItem]] = [[], [], [], [], []]
var priorityInt = 0
if let results = try? context.fetch(request) {
for result in results {
priorityInt = Int(result.priorityLevel)!
tempItems[priorityInt - 1].insert(result, at: 0)
}
}
if cell.checkButton.isSelected {
cell.checkButton.isSelected = false
tempItems[indexPath.section][indexPath.row].isChecked = false
}
else {
cell.checkButton.isSelected = true
tempItems[indexPath.section][indexPath.row].isChecked = true
}
do {
try context.save()
}
catch let err {
print(err)
}
}
func popoverData(priority: String, itemText: String) {
let cell = listTableView.cellForRow(at: popoverCellIndex) as! ListItemCell
cell.priorityLabel.text = priority
cell.itemLabel.text = itemText
let newIndexPath = IndexPath(row: 0, section: Int(priority)! - 1)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = ListItem.createFetchRequest()
request.returnsObjectsAsFaults = false
var tempItems: [[ListItem]] = [[], [], [], [], []]
var priorityInt = 0
if let results = try? context.fetch(request) {
for result in results {
priorityInt = Int(result.priorityLevel)!
tempItems[priorityInt - 1].insert(result, at: 0)
}
}
tempItems[popoverCellIndex.section][popoverCellIndex.row].priorityLevel = priority
tempItems[popoverCellIndex.section][popoverCellIndex.row].itemText = itemText
tempItems[popoverCellIndex.section][popoverCellIndex.row].isChecked = cell.checkButton.isSelected
do {
try context.save()
}
catch let err {
print(err)
}
cell.isSelected = false
items = [[], [], [], [], []]
loadData()
listTableView.moveRow(at: popoverCellIndex, to: newIndexPath)
}
The obvious desired result is for the checkmarks to remain with their relevant cells and the cells to remain in their set order. I don't see where this is going wrong.
As requested, here is the cellForRowAt. I included didSelectRowAt as well.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath) as! ListItemCell
cell.checkButton.isSelected = items[indexPath.section][indexPath.row].isChecked
cell.priorityLabel.text = items[indexPath.section][indexPath.row].priorityLevel
cell.itemLabel.text = items[indexPath.section][indexPath.row].itemText
cell.delegate = self
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = listTableView.cellForRow(at: indexPath) as! ListItemCell
popoverCellIndex = indexPath
let controller = PopoverViewController()
controller.delegate = self
controller.modalPresentationStyle = .popover
controller.preferredContentSize = CGSize(width: 300, height: 400)
controller.priorityPassed = cell.priorityLabel.text!
controller.textBox.text = cell.itemLabel.text!
controller.priorityPassed = cell.priorityLabel.text!
let presentationController = AlwaysPresentAsPopover.configurePresentation(forController: controller)
presentationController.sourceView = cell
presentationController.sourceRect = cell.bounds
presentationController.permittedArrowDirections = [.up, .down]
presentationController.backgroundColor = UIColor(r: 211, g: 221, b: 230)
self.present(controller, animated: true)
}

How to update a specific cell label using MZDownloadManager even if user move to any ViewController and came back

I'm using MZDownloadManger library to download the files using url, every thing is working fine except the label update, when i start the downloading it changes to "Starting Downloading" then starts its progress like 10% 20% etc. its working fine but when i move to any other view controller its progress stops and not update the label value to "Downloaded". i have set a flag in my local data base '0' and '1', 0 means not downloaded and 1 means downloaded.
here is the code when a user select the cell and hit for download:
func keepOfflineFiles(sender: UIButton) {
if files[sender.tag].onLocal == "1"{
self.displayAlert(title: AlertTitle.alert, message: AlertMsg.alreadyDownloaded)
} else{
if self.files[sender.tag].status == "on amazon"{
let indexPath = IndexPath.init(row: sender.tag, section: 0)
let cell = self.tblFilesPro.cellForRow(at: indexPath)
if let cell = cell {
let downloadCell = cell as! filesTableViewCell
downloadCell.lblDetailsPro.text = "Starting Download. . ."
}
let pathString:String = ""
let fileName:String = self.files[sender.tag].file_id! + self.files[sender.tag].Extension!
if(fileName != ""){
let local_url = NSURL(fileURLWithPath: pathString.getDocumentsPath())
let filePath = local_url.appendingPathComponent(fileName)?.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath!) {
// FILE AVAILABLE
let indexPath = IndexPath.init(row: sender.tag, section: 0)
let cell = self.tblFilesPro.cellForRow(at: indexPath)
if let cell = cell {
let downloadCell = cell as! filesTableViewCell
downloadCell.lblDetailsPro.text = "Downloaded"
}
self.fileid = self.files[sender.tag].file_id!
self.updateFileStatusToRealm()
//self.displayAlert(title: AlertTitle.alert, message: AlertMsg.alreadyDownloaded)
} else {
// FILE NOT AVAILABLE
let completeUrl:String = Tray.downloadURLBasePath + self.files[sender.tag].fileLink!
if(self.verifyUrl(urlString: completeUrl)) {
self.fileid = self.files[sender.tag].file_id!
let index:String = String(sender.tag)
self.AppDelegateObj.downloadManager.addDownloadTask(fileName as String, fileURL: completeUrl as String, destinationPath: index as String)
}
}
}
}else{
self.displayAlert(title: AlertTitle.alert, message: AlertMsg.inArchiveProcess)
}
}
}
here are the delegates of MZDownloadManager that i called in AppDelegate
To update the progress
func downloadRequestDidUpdateProgress(_ downloadModel: MZDownloadModel, index: Int) {
let root : UINavigationController = self.window?.rootViewController as! UINavigationController
if let master = root.topViewController as? TabBarController {
if let nav = master.viewControllers?[0] as? FilesVC {
nav.refreshCellForIndex(downloadModel, index: Int(downloadModel.destinationPath)!)
}
} else {
print("Somthing went wrong while downloading this file.")
}
}
When downloading finished
func downloadRequestFinished(_ downloadModel: MZDownloadModel, index: Int) {
let root : UINavigationController = self.window!.rootViewController! as! UINavigationController
if let master = root.topViewController as? TabBarController {
if let nav = master.viewControllers![0] as? FilesVC{
nav.getDownloadingStatusOfCellForIndex(downloadModel, index: Int(downloadModel.destinationPath)!)
}
} else {
print("Somthing went wrong while finishing downloading of this file.")
}
}
Method to refresh the cell label
func refreshCellForIndex(_ downloadModel: MZDownloadModel, index: Int) {
let indexPath = IndexPath.init(row: index, section: 0)
let cell = self.tblFilesPro.cellForRow(at: indexPath)
if let cell = cell {
let downloadCell = cell as? filesTableViewCell
downloadCell?.updateCellForRowAtIndexPath(indexPath, downloadModel: downloadModel)
}
}
Method to get the cell and change value
func getDownloadingStatusOfCellForIndex(_ downloadModel: MZDownloadModel, index: Int) {
let indexPath = IndexPath.init(row: index, section: 0)
let cell = self.tblFilesPro.cellForRow(at: indexPath)
if let cell = cell {
let downloadCell = cell as? filesTableViewCell
downloadCell?.lblDetailsPro.text = "Downloaded"
self.fileid = self.files[index].file_id!
self.updateFileStatusToRealm()
}
}
here is the method which change the flag value 0 to 1 in database:
func updateFileStatusToRealm(){
let fileToUpdate = uiRealm.objects(filesDataTable.self).filter("file_id = %#", self.fileid)
let realm = try! Realm()
if let file = fileToUpdate.first {
try! realm.write {
file.onLocal = "1"
tblFilesPro.reloadData()
}
}
}

Multiple collectionviews in one Viewcontroller causes Index out of range error

I try to use three collectionviews in one Viewcontroller. I parse the data like the following method shows:
At the bottom i add the data depending on the position to the right list (this part works)
func getEventData(eventIDs: [String], plz: String, positiona: Int){
for eventId in eventIDs {
let ref = Database.database().reference().child("Events").child(plz).child(eventId)
ref.observe(.value, with: { snapshot in
let item = snapshot.value as? [String: AnyObject]
let eventName = item?["name"] as! String
let date = item?["date"] as! String
let lat = item?["lat"] as! String
let lng = item?["lng"] as! String
let infos = item?["additionalInfos"] as! String
let position = item?["position"] as! String
let ts = item?["ts"] as! Int
let createdBy = item?["createdBy"] as! String
let timestamp = NSDate().timeIntervalSince1970
if (ts > Int(timestamp)) {
let eo = EventObject(eventID: eventId, eventName:
eventName, info: infos, createdBy: createdBy, date: date, lat: lat, lng: lng, position: position, ts: ts)
if positiona == 0{
self.acceptedEvents.append(eo)
self.acceptedEventscv.reloadData()
}else if positiona == 1{
self.myEvents.append(eo)
self.myEventscv.reloadData()
}else if positiona == 2{
self.storedEvents.append(eo)
self.storedEventscv.reloadData()
}
}
})
}
}
In my NumbersofItemsInSection method i did the following which works as well:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.acceptedEventscv{
return acceptedEvents.count
}else if collectionView == self.storedEventscv{
return storedEvents.count
}else if collectionView == self.myEventscv{
return myEvents.count
}else{
return 0
}
}
and in my CellForRowAtItem method i tried the following
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.acceptedEventscv{
let cell =
collectionView.dequeueReusableCell(withReuseIdentifier:
"acceptedEventsCell", for: indexPath) as!
acceptedEventsCollectionViewCell
let eo = acceptedEvents[indexPath.row]
cell.eventName.text = eo.eventName
let items = eo.date!.components(separatedBy: " ")//Here replase
space with your value and result is Array.
//Direct line of code
//let items = "This is my String".components(separatedBy: " ")
let date = items[0]
let time = items[1]
cell.date.text = date
cell.time.text = time
getUserNameAge(label: cell.usernameAge)
return cell
}else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "savedEventsCell", for: indexPath) as! savedEventsCollectionViewCell
let eo = acceptedEvents[indexPath.row]
cell.eventName.text = eo.eventName
let items = eo.date!.components(separatedBy: " ")//Here replase space with your value and result is Array.
//Direct line of code
//let items = "This is my String".components(separatedBy: " ")
let date = items[0]
let time = items[1]
cell.date.text = date
cell.time.text = time
getUserNameAge(label: cell.usernameAge)
return cell
}
}
The problem is if i have more items in the seccond CollectionView, i always get this error:
Thread 1: Fatal error: Index out of range
at this line of code:
let eo = acceptedEvents[indexPath.row]
cellForItemAt also must be like
if collectionView == self.acceptedEventscv{
let item = acceptedEvents[indexPath.row]
----
return cell
}else if collectionView == self.storedEventscv{
let item = storedEvents[indexPath.row]
----
return cell
}else {
let item = myEvents[indexPath.row]
----
return cell
}
what happens now in your case is that you access the same array acceptedEvents in both cases where the returned count in numberOfItemsInSection may be different

Swift: Table View is only returning one cell

I'm attempting to load a table view with two different prototype cells. profileCell should only load once and at the top of the table view. dogCell should count an array of dog objects named dogs downloaded from firebase. Currently, only the first cell is displaying correctly.
I think the numberOfRowsInSection method isn't accurately counting the dog objects in the dogs array. When I put a breakpoint on return dogs.count + 1 and po dogs.count the debugger keeps outputting 0.
When I use return dogs.count the table view loads but with only the profile cell. If I use return dogs.count + 1(to account for the profile cell at the top) an exception is thrown when constructing dogCell: "fatal error: Index out of range"
Perhaps I need to change the way my tableview is reloading data?
Here's my code:
class DogTableViewController: UITableViewController {
var user = User()
let profileCell = ProfileTableViewCell()
var dogs = [Dog]()
override func viewDidLoad() {
super.viewDidLoad()
let userDogRef = Database.database().reference().child("users").child(user.uid!).child("dogs")
let userProfileImageView = UIImageView()
userProfileImageView.translatesAutoresizingMaskIntoConstraints = false
userProfileImageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
userProfileImageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
userProfileImageView.layer.cornerRadius = 20
userProfileImageView.clipsToBounds = true
userProfileImageView.contentMode = .scaleAspectFill
userProfileImageView.image = UIImage(named: "AppIcon")
navigationItem.titleView = userProfileImageView
//MARK: Download dogs from firebase
userDogRef.observe(.childAdded, with: { (snapshot) in
if snapshot.value == nil {
print("no new dog found")
} else {
print("new dog found")
let snapshotValue = snapshot.value as! Dictionary<String, String>
let dogID = snapshotValue["dogID"]!
let dogRef = Database.database().reference().child("dogs").child(dogID)
dogRef.observeSingleEvent(of: .value, with: { (snap) in
print("Found dog data!")
let value = snap.value as? NSDictionary
let newDog = Dog()
newDog.name = value?["name"] as? String ?? ""
newDog.breed = value?["breed"] as? String ?? ""
newDog.creator = value?["creator"] as? String ?? ""
newDog.score = Int(value?["score"] as? String ?? "")
newDog.imageURL = value?["imageURL"] as? String ?? ""
newDog.dogID = snapshot.key
URLSession.shared.dataTask(with: URL(string: newDog.imageURL!)!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
newDog.picture = UIImage(data: data!)!
self.dogs.append(newDog)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}).resume()
})
}
})
tableView.estimatedRowHeight = 454
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dogs.count + 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let profileCell = tableView.dequeueReusableCell(withIdentifier: "profileCell", for: indexPath) as! ProfileTableViewCell
profileCell.nameLabel.text = user.name
profileCell.totalReputationLabel.text = String(describing: user.reputation!)
profileCell.usernameLabel.text = user.username
return profileCell
} else {
let dogCell = tableView.dequeueReusableCell(withIdentifier: "dogCell", for: indexPath) as! DogTableViewCell
dogCell.dogBreedLabel.text = dogs[indexPath.row].breed
dogCell.dogNameLabel.text = dogs[indexPath.row].name
dogCell.dogScoreLabel.text = String(describing: dogs[indexPath.row].score)
dogCell.dogImageView.image = dogs[indexPath.row].picture
dogCell.dogCreatorButton.titleLabel?.text = dogs[indexPath.row].creator
dogCell.dogVotesLabel.text = "0"
return dogCell
}
}
}
I actually found a solution shortly after writing this question, but I think it might be helpful for others to read.
Because the first indexPath.row is dedicated to a profile cell, I should not have been using the indexPath.row to navigate my dogs array. Instead I should have been using indexPath.row - 1 to get the correct dogs index.
Here's the section I updated:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let profileCell = tableView.dequeueReusableCell(withIdentifier: "profileCell", for: indexPath) as! ProfileTableViewCell
profileCell.nameLabel.text = user.name
profileCell.totalReputationLabel.text = String(describing: user.reputation!)
profileCell.usernameLabel.text = user.username
return profileCell
} else {
let dogCell = tableView.dequeueReusableCell(withIdentifier: "dogCell", for: indexPath) as! DogTableViewCell
dogCell.dogBreedLabel.text = dogs[indexPath.row - 1].breed
dogCell.dogNameLabel.text = dogs[indexPath.row - 1].name
dogCell.dogScoreLabel.text = String(describing: dogs[indexPath.row - 1].score)
dogCell.dogImageView.image = dogs[indexPath.row - 1].picture
dogCell.dogCreatorButton.titleLabel?.text = dogs[indexPath.row - 1].creator
dogCell.dogVotesLabel.text = "0"
return dogCell
}
}

how to make active only one radio button for all the sections?

here i had divided into sections in it each section there are rows depending on count so for every row the radio button will be there and i need to make active only one radio button for all of the sections but i am unable to implement it and the image for sections will be shown here and my code is
#IBAction func paymentRadioAction(_ sender: KGRadioButton) {
let chekIndex = self.checkIsPaymentRadioSelect.index(of: sender.tag)
if sender.isSelected {
} else{
if(chekIndex == nil){
self.checkIsPaymentRadioSelect.removeAll(keepingCapacity: false)
self.checkIsPaymentRadioSelect.append(sender.tag)
self.shippingTableView.reloadData()
}
}
}
here is the code for cell for row at indexpath
let cell = tableView.dequeueReusableCell(withIdentifier: "shippingCell", for: indexPath) as! shippingMethodTableViewCell
var key = self.keys[indexPath.section]
print(key)
var a :[Any] = arrayss[key] as! [Any]
var dictionary = a[indexPath.row] as! [String:Any]
let name = dictionary["name"]
let price = dictionary ["price"]
cell.methodNameLabel.text = name as! String
cell.priceLabel.text = price as! String
let checkIndex = self.checkIsPaymentRadioSelect.index(of: indexPath.row)
if(checkIndex != nil){
cell.radioButton.isSelected = true
}else{
cell.radioButton.isSelected = false
}
return cell
}
Here you don't have to use an array to keep track of the selected radio button
Make chekIndex as global variable of type IndexPath as var chekIndex:IndexPath?
and modify the code as below
#IBAction func paymentRadioAction(_ sender: KGRadioButton) {
let center = sender.center;
let centralPOint = sender.superview?.convert(sender.center, to:self.shippingTableView )
let indexPath = self.shippingTableView.indexPathForRow(at: centralPoint)
if sender.isSelected {
} else{
chekIndex = indexPath
self.shippingTableView.reloadData()
}
}
let cell = tableView.dequeueReusableCell(withIdentifier: "shippingCell", for: indexPath) as! shippingMethodTableViewCell
var key = self.keys[indexPath.section]
print(key)
var a :[Any] = arrayss[key] as! [Any]
var dictionary = a[indexPath.row] as! [String:Any]
let name = dictionary["name"]
let price = dictionary ["price"]
cell.methodNameLabel.text = name as! String
cell.priceLabel.text = price as! String
if checkIndex == indexPath {
cell.radioButton.isSelected = true
} else {
cell.radioButton.isSelected = false
}
return cell
}

Resources