So my table view is not loading anything and I think it's because of this warning that I get. It saids the save function is not being used so how can it load something that is not saved. What I am saving is the indexPath and Section of the row that the user selected via a button action in the row.
Warning:
Result of call to 'save(defaults:)' is unused
Code:
func saveSorting(_ dataIdBlock: (Any) -> String) {
guard let items = self.items else { return }
for (section, rows) in items.enumerated() {
for (row, item) in rows.enumerated() {
let indexPath = IndexPath(row: row, section: section)
let dataId = dataIdBlock(item)
let ordering = DataHandling(dataId: dataId, indexPath: indexPath)
// Warning is here
ordering.save(defaults: indexPath.defaultsKey)
}
}
}
}
NSCoder Class for DataHandling / ordering.save
DataHandling.swift
class DataHandling: NSObject, NSCoding {
var indexPath: IndexPath?
var dataId: String?
init(dataId: String, indexPath: IndexPath) {
super.init()
self.dataId = dataId
self.indexPath = indexPath
}
required init(coder aDecoder: NSCoder) {
if let dataId = aDecoder.decodeObject(forKey: "dataId") as? String {
self.dataId = dataId
}
if let indexPath = aDecoder.decodeObject(forKey: "indexPath") as? IndexPath {
self.indexPath = indexPath
}
}
func encode(with aCoder: NSCoder) {
aCoder.encode(dataId, forKey: "dataId")
aCoder.encode(indexPath, forKey: "indexPath")
}
func save(defaults box: String) -> Bool {
let defaults = UserDefaults.standard
let savedData = NSKeyedArchiver.archivedData(withRootObject: self)
defaults.set(savedData, forKey: box)
return defaults.synchronize()
}
convenience init?(defaults box: String) {
let defaults = UserDefaults.standard
if let data = defaults.object(forKey: box) as? Data,
let obj = NSKeyedUnarchiver.unarchiveObject(with: data) as? DataHandling,
let dataId = obj.dataId,
let indexPath = obj.indexPath {
self.init(dataId: dataId, indexPath: indexPath)
} else {
return nil
}
}
class func allSavedOrdering(_ maxRows: Int) -> [Int: [DataHandling]] {
var result: [Int: [DataHandling]] = [:]
for section in 0...1 {
var rows: [DataHandling] = []
for row in 0..<maxRows {
let indexPath = IndexPath(row: row, section: section)
if let ordering = DataHandling(defaults: indexPath.defaultsKey) {
rows.append(ordering)
}
rows.sort(by: { $0.indexPath! < $1.indexPath! })
}
result[section] = rows
}
return result
}
}
Other code I'm using:
// Number of Rows in Section
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items?[section].count ?? 0
}
// Number of Sections
func numberOfSections(in tableView: UITableView) -> Int {
return self.items?.count ?? 0
}
Saving it with:
saveSorting() { "\($0)" }
Loading it in ViewDidLoad:
func fetchData() {
// Load Data from Server to testArray
retrieveData()
// request from remote or local
data = [testArray]
// Update the items to first section has 0 elements,
// and place all data in section 1
items = [[], data ?? []]
// apply ordering
applySorting() { "\($0)" }
// save ordering
saveSorting() { "\($0)" }
// refresh the table view
myTableView.reloadData()
}
Loading Code:
// Loading
func applySorting(_ dataIdBlock: (Any) -> String) {
// get all saved ordering
guard let data = self.data else { return }
let ordering = DataHandling.allSavedOrdering(data.count)
var result: [[Any]] = [[], []]
for (section, ordering) in ordering {
guard section <= 1 else { continue } // make sure the section is 0 or 1
let rows = data.filter({ obj -> Bool in
return ordering.index(where: { $0.dataId == .some(dataIdBlock(obj)) }) != nil
})
result[section] = rows
}
self.items = result
}
The DataHandling instance's save(defaults:) function technically returns a value, even if you don't use it. To silence this warning, assign it to _ to signify that you don't intend to use the result value, e.g.:
_ = ordering.save(defaults: indexPath.defaultsKey)
or
let _ = ordering.save(defaults: indexPath.defaultsKey)
Just to be clear, this is almost definitely not why your tableview is not loading data. It should be pretty insignificant. The indexPath.defaultsKey is being saved (assuming the API works).
Related
Greetings I have the disadvantage that the cells of the tableview are duplicated in a random way, at certain times the event occurs, not always
i think i need to clean or reload data but im not sure, could you help please.
final class MovementsDatasource: NSObject, PaginatedDatasource {
typealias Values = MovementsSectionModel
private let repository: TransactionsRepositoryProtocol
private let disposeBag = DisposeBag()
private let fetching = ActivityIndicator()
private let id: String, cvl: String
private let rowsObserver = PublishSubject<[Values]>()
var values = [Values]() { didSet { self.rowsObserver.onNext(self.values) } }
var page = 0
var hasNextPage = false
init(repository: TransactionsRepositoryProtocol, id: String, cvl: String) {
self.id = id
self.cvl = cvl
self.repository = repository
}
func getActivityIndicator() -> ActivityIndicator { self.fetching }
func getValuesObserver() -> Observable<[Values]> { self.rowsObserver }
func getNextPage() {
guard self.hasNextPage else { return }
getMovements(for: self.id, cvl: self.cvl, page: self.page)
}
func getFirstPage() {
self.page = 0
self.hasNextPage = false
self.values = []
getMovements(for: self.id, cvl: self.cvl, page: self.page)
}
}
extension MovementsDatasource {
private func getMovements(for productID: String, cvl: String, page: Int) {
self.repository
.movements(userCVL: cvl, ibanAccountId: productID, page: page)
.trackActivity(self.fetching)
.subscribe(onNext: { [weak self] response in
guard let _self = self else { return }
_self.hasNextPage = response.hasNextPage
_self.page = _self.hasNextPage ? (_self.page + 1) : _self.page
_self.publish(response)
})
.disposed(by: self.disposeBag)
}
private func publish(_ response: MovementsResponse) {
guard let rawMovement = response.values else { return }
let newRows = self.transform(rawMovement)
var rows = self.values
if !rows.isEmpty { rows += newRows }
else { rows = newRows }
self.values = self.merge(rows)
}
internal func transform(_ movements: [Movement]) -> [MovementsSectionModel] {
movements
.map { $0.movementDate.displayTimestamp() }
.unique()
.map { title -> MovementsSectionModel in
let sorted = movements.filter { $0.movementDate.displayTimestamp() == title }
return MovementsSectionModel(header: title, items: sorted)
}
}
internal func merge(_ sections: [MovementsSectionModel]) -> [MovementsSectionModel] {
sections
.map { $0.header }
.unique()
.map { title -> MovementsSectionModel in
let merged = sections.reduce(into: [MovementCellViewModel]()) {
accumulator, section in
if section.header == title { accumulator += section.items }
}
return MovementsSectionModel(header: title, items: merged)
}
}
}
extension MovementsDatasource {
func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
self.values[section].items.count
}
func numberOfSections(in _: UITableView) -> Int { self.values.count }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard !self.values.isEmpty, let cell = tableView.dequeueReusableCell(withIdentifier: MovementCell.reuseID, for: indexPath) as? MovementCell
else {
return UITableViewCell(frame: .zero)
}
let section = indexPath.section
let index = indexPath.item
if !self.values.isEmpty {
cell.viewModel = self.values[section].items[index]
}
return cell
}
}
I am following this tutorial for expanding and collapsing my table view section. As this demo is done in swift 2.2 I have made all the changes according to swift 3.0 . I am stuck at the below function at if condition(currentSectionCells[row]["isVisible"]) which gives me error as "Type 'NSFastEnumerationIterator.Element' (aka 'Any' has no subscript members)'".
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0...((currentSectionCells as! [[String: AnyObject]]).count - 1) {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
I have tried type casting it as below
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0...((((currentSectionCells) as? NSMutableArray)?.count)! - 1) {
let temp = [currentSectionCells][row] as? NSMutableDictionary
let temp2 = temp?["isVisible"] as! Bool
if temp2 == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
But this gives me a crash at runtime on this line "let temp2 = temp?["isVisible"] as! Bool"
Crash says "EXC_BAD_INSTRUCTION" and the temp shows as nil.
Please help guys. TIA
Table View Delegate and Data Source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if cellDescriptors != nil {
return cellDescriptors.count
}
else {
return 0
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return visibleRowsPerSection[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath: indexPath as NSIndexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! CustomCell
if currentCellDescriptor["cellIdentifier"] as! String == "sectionCellIdentifier" {
if let primaryTitle = currentCellDescriptor["secondaryTitle"]
{
cell.sectionTitleLabel.text = primaryTitle as? String
}
}
else if currentCellDescriptor["cellIdentifier"] as! String == "shortAnswerCell" {
cell.questionTitle.text = currentCellDescriptor["primaryTitle"] as? String
cell.questionTextView.text = currentCellDescriptor["secondaryTitle"] as? String
cell.answerTextView.text = currentCellDescriptor["answerTitle"] as? String
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let temp = cellDescriptors[indexPath.section] as? NSArray
let temp2 = temp?[indexOfTappedRow ] as? NSDictionary
let temp3 = temp2?["isExpandable"] as! Bool
if temp3 == true {
var shouldExpandAndShowSubRows = false
if temp3 == false {
// In this case the cell should expand.
shouldExpandAndShowSubRows = true
}
temp2?.setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (temp2?["additionalRows"] as! Int)) {
(temp![i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
getIndicesOfVisibleRows()
tblExpandable.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: UITableViewRowAnimation.fade)
}
I worked on that tutorial as well and completed it successfully in swift3.Your solution is given below modify accordingly.
class yourClass: UIViewController
{
#IBOutlet weak var profileTableView: UITableView!
internal var visibleRowsPerSection = [[Int]]()
internal var cellDescriptors: NSMutableArray!
// VIEW DID LOAD
override func viewDidLoad() {
super.viewDidLoad()
profileTableView.showsVerticalScrollIndicator = false
loadProfileControllerData()
profileTableSetUp()
// Do any additional setup after loading the view.
}
func loadProfileControllerData(){
if let path = Bundle.main.path(forResource: "CellDescriptor", ofType: "plist") {
cellDescriptors = NSMutableArray(contentsOfFile: path)
}
getIndicesOfVisibleRows()
profileTableView.reloadData()
}
// SHOW PARENT VISIBLE ROWS AND SAVE THERE ROW INDEX IN ARRAY
func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors.objectEnumerator().allObjects as! [[[String:Any]]]{
var visibleRows = [Int]()
for row in 0..<currentSectionCells.count {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
print(visibleRowsPerSection)
}
}
// GET REQUIRED OBJECT OF TYPE [String: Any]
func getCellDescriptorForIndexPath(indexPath: NSIndexPath) -> [String: Any] {
let indexOfVisibleRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let cellDescriptorss = cellDescriptors[indexPath.section] as! NSArray
let data = cellDescriptorss.object(at: indexOfVisibleRow) as! [String:Any]
return data
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//----------------------
// EXTENSION TO OUR PROFILE CLASS THAT DETERMINE OUR CLASS CONFIRM 2 IMPORTANT DELEGATES
extension profileViewController : UITableViewDelegate,UITableViewDataSource{
//MARK-: TABLE VIEW DELEGATE FUNCTIONS
// RETURN NUMBER OF SECTION IN TABLE VIEW
public func numberOfSections(in tableView: UITableView) -> Int{
if cellDescriptors.count != 0{
return cellDescriptors.count
}
else{
return 0
}
}
// RETURN NUMBER OF ROWS IN EACH SECTION OF TABLE VIEWS
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return visibleRowsPerSection[section].count
}
/* Return object of UITableViewCell that contains table SECTON data and USER profile data */
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath: indexPath as NSIndexPath)
let menuCell = tableView.dequeueReusableCell(withIdentifier: currentCellDescriptor["cellIdentifier"] as! String, for: indexPath) as! yourCellClass
if currentCellDescriptor["cellIdentifier"] as! String == "parent"{
}
else if currentCellDescriptor["cellIdentifier"] as! String == "child"{
menuCell.backgroundColor = UIColor.clear
}
return menuCell
}
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
let cellDescriptorss = cellDescriptors[indexPath.section] as! NSArray
var data = cellDescriptorss.object(at: indexOfTappedRow) as! [String:Any]
if data["isExpandable"] as! Bool == true{
var shouldExpandAndShowSubRows = false
if data["isExpanded"] as! Bool == true{
shouldExpandAndShowSubRows = false
(cellDescriptorss[indexOfTappedRow] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
}
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (data["additionalRows"] as! Int)) {
(cellDescriptorss[i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
getIndicesOfVisibleRows()
self.profileTableView.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: UITableViewRowAnimation.fade)
}
Thank You for helping me out, I was stuck at a point where the sections weren't expanding even after your help, so just made some changes in the syntax as Swift 3.0 is very specific about type casting hence the didSelectRowAt wasn't functioning properly. Here is the complete didSelectRowAt method. Happy coding.
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
if (cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow] ["isExpandable"] as! Bool == true {
var shouldExpandAndShowSubRows = false
if (cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow]["isExpanded"] as! Bool == false {
// In this case the cell should expand.
shouldExpandAndShowSubRows = true
}
((cellDescriptors[indexPath.section] as! NSMutableArray)[indexOfTappedRow] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isExpanded")
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + ((cellDescriptors[indexPath.section] as! [[String: AnyObject]])[indexOfTappedRow]["additionalRows"] as! Int)) {
((cellDescriptors[indexPath.section] as! NSMutableArray)[i] as AnyObject).setValue(shouldExpandAndShowSubRows, forKey: "isVisible")
}
}
Swift 3/4 without use of NSMutable arrays based on the tutorial and all the code wrapped in a model.
class CellsDescriptorModel {
private var cellDescriptors: [[[String:Any]]]!
private var visibleRowsPerSection : [[Int]]
var CellDescriptors : [[[String:Any]]] { get { return cellDescriptors }}
var VisibleRowsPerSection : [[Int]] { get { return visibleRowsPerSection }}
init(plist:String) {
visibleRowsPerSection = [[Int]]()
if let url = Bundle.main.url(forResource:plist, withExtension: "plist") {
do {
let data = try Data(contentsOf:url)
cellDescriptors = try PropertyListSerialization.propertyList(from: data, options: [], format: nil) as! [[[String:Any]]]
} catch {
print(error)
}
}
getIndicesOfVisibleRows()
}
func getCellDescriptorForIndexPath(indexPath: IndexPath) -> [String: Any] {
let indexOfVisibleRow = visibleRowsPerSection[indexPath.section][indexPath.row]
return cellDescriptors[indexPath.section][indexOfVisibleRow]
}
func expandCell(indexPath:IndexPath) {
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
if cellDescriptors[indexPath.section][indexOfTappedRow] ["isExpandable"] as! Bool == true {
var shouldExpandAndShowSubRows = false
if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] as! Bool == false {
shouldExpandAndShowSubRows = true
}
cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] = shouldExpandAndShowSubRows
for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (cellDescriptors[indexPath.section][indexOfTappedRow]["additionalRows"] as! Int)) {
cellDescriptors[indexPath.section][i]["isVisible"] = shouldExpandAndShowSubRows
}
}
else {
if cellDescriptors[indexPath.section][indexOfTappedRow]["cellIdentifier"] as! String == "DataPickerTableViewCell" {
var indexOfParentCell: Int!
for index in (0..<indexOfTappedRow).reversed() {
if cellDescriptors[indexPath.section][index]["isExpandable"] as! Bool == true {
indexOfParentCell = index
break
}
}
cellDescriptors[indexPath.section][indexOfParentCell]["secondaryTitle"] = ""
cellDescriptors[indexPath.section][indexOfParentCell]["isExpanded"] = false
for i in (indexOfParentCell + 1)...(indexOfParentCell + (cellDescriptors[indexPath.section][indexOfParentCell]["additionalRows"] as! Int)) {
cellDescriptors[indexPath.section][i]["isVisible"] = false
}
}
}
getIndicesOfVisibleRows()
}
private func getIndicesOfVisibleRows() {
visibleRowsPerSection.removeAll()
for currentSectionCells in cellDescriptors {
var visibleRows = [Int]()
for row in 0..<currentSectionCells.count {
if currentSectionCells[row]["isVisible"] as! Bool == true {
visibleRows.append(row)
}
}
visibleRowsPerSection.append(visibleRows)
}
}
}
Not looking for code (wouldn't hurt though) just looking to know how I should go about this. I have an app which is like a blog reader. I have information in a MYSQL database which is taken with JSON, placed in a jsonArray and then placed in an array to be shown in a table view. In that table view I generally have all my objects in 1 section. Each row/object has a button when clicked it moves that row into another section. I also have a search controller to search through the main section (Section 1).
How do I save the order or position of the rows to Core Data?
For example: I have 0 rows in Section 0 and 5 rows in Section 1, I click the button on one of the rows in Section 1 and it moves that row to Section 0, now Section 0 has 1 row while Section 1 has 4 rows. How do I save this new tableview order to Core Data? I just want to save the rows positions, so the app remembers which section that selected row was in.
Do I save the indexPath of the row in the section?
When adding an entity and attributes, what do I use to save?
Also, its a mysql reader so when the tableview is reloaded and a new content is added, will it still show since the tableview will be reading from core data?
I'm learning Core Data (Swift 3 code) but just having trouble using it for this app.
Thank you for helping out!
Use UserDefaults to save the ordering data if you don't need to save the whole data from MySQL database. Define a class only contains dataId and indexPath:
class DataOrdering: NSObject, NSCoding {
var indexPath: IndexPath?
var dataId: String?
init(dataId: String, indexPath: IndexPath) {
super.init()
self.dataId = dataId
self.indexPath = indexPath
}
required init(coder aDecoder: NSCoder) {
if let dataId = aDecoder.decodeObject(forKey: "dataId") as? String {
self.dataId = dataId
}
if let indexPath = aDecoder.decodeObject(forKey: "indexPath") as? IndexPath {
self.indexPath = indexPath
}
}
func encode(with aCoder: NSCoder) {
aCoder.encode(dataId, forKey: "dataId")
aCoder.encode(indexPath, forKey: "indexPath")
}
func save(defaults key: String) -> Bool {
let defaults = UserDefaults.standard
let savedData = NSKeyedArchiver.archivedData(withRootObject: self)
defaults.set(savedData, forKey: key)
return defaults.synchronize()
}
convenience init?(defaults key: String) {
let defaults = UserDefaults.standard
if let data = defaults.object(forKey: key) as? Data,
let obj = NSKeyedUnarchiver.unarchiveObject(with: data) as? DataOrdering,
let dataId = obj.dataId,
let indexPath = obj.indexPath {
self.init(dataId: dataId, indexPath: indexPath)
} else {
return nil
}
}
class func allSavedOrdering(_ maxRows: Int) -> [Int: [DataOrdering]] {
var result: [Int: [DataOrdering]] = [:]
for section in 0...1 {
var rows: [DataOrdering] = []
for row in 0..<maxRows {
let indexPath = IndexPath(row: row, section: section)
if let ordering = DataOrdering(defaults: indexPath.defaultsKey) {
rows.append(ordering)
}
rows.sort(by: { $0.indexPath! < $1.indexPath! })
}
result[section] = rows
}
return result
}
}
Playground sample:
let data = DataOrdering(dataId: "1", indexPath: IndexPath(row: 0, section: 0))
let savedData = NSKeyedArchiver.archivedData(withRootObject: data)
let obj = NSKeyedUnarchiver.unarchiveObject(with: savedData) as? DataOrdering
obj?.dataId // print: "1"
obj?.indexPath // print: [0,0]
Use save function to save with a "key" and read it back by DataOrdering(defaults: "key")
UPDATED
Added codes for a view controller to use this class:
extension IndexPath {
var defaultsKey: String { return "data_ordering_\(section)_\(row)" }
}
class ViewController: UITableViewController {
var data: [Any]?
var items: [[Any]]?
func fetchData() {
// request from remote or local
data = [1, 2, 3, "a", "b", "c"] // sample data
// Update the items to first section has 0 elements,
// and place all data in section 1
items = [[], data ?? []]
// apply ordering
applySorting() { "\($0)" }
// save ordering
saveSorting() { "\($0)" }
// refresh the table view
tableView.reloadData()
}
func applySorting(_ dataIdBlock: (Any) -> String) {
// get all saved ordering
guard let data = self.data else { return }
let ordering = DataOrdering.allSavedOrdering(data.count)
var result: [[Any]] = [[], []]
for (section, ordering) in ordering {
guard section <= 1 else { continue } // make sure the section is 0 or 1
let rows = data.filter({ obj -> Bool in
return ordering.index(where: { $0.dataId == .some(dataIdBlock(obj)) }) != nil
})
result[section] = rows
}
self.items = result
}
func saveSorting(_ dataIdBlock: (Any) -> String) {
guard let items = self.items else { return }
for (section, rows) in items.enumerated() {
for (row, item) in rows.enumerated() {
let indexPath = IndexPath(row: row, section: section)
let dataId = dataIdBlock(item)
let ordering = DataOrdering(dataId: dataId, indexPath: indexPath)
ordering.save(defaults: indexPath.defaultsKey)
}
}
}
#IBAction func buttonMoveToSectionTapped(_ sender: UIButton) {
// if the sender's tag is row index
// or you can get indexPath by tableView.indexPath(for: cell) function too
let row = sender.tag
// move this item from section 1 to section 0 (last position)
if let item = items?[1].remove(at: row) {
items?[0].append(item)
}
// Save all sorting
saveSorting() { "\($0)" }
tableView.reloadData() // refresh table view
}
override func numberOfSections(in tableView: UITableView) -> Int {
return self.items?.count ?? 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items?[section].count ?? 0
}
}
Getting a JSON object from a rest web service I get the data from the object and I want to show it in a tableview.
class TableViewController1: UITableViewController {
var nomProduit = ["ok"]
var prixProduit = [""]
var vt1 : String?
var vt2 : String?
var i : Int!
var compteur1:Int!
var resultat1:NSArray?
var x : AnyObject?
override func viewDidLoad() {
super.viewDidLoad()
// \(detectionString)
let str:String = "http://vps43623.ovh.net/yamoinscher/api/products/6194005492077"
let url = NSURL(string: str)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)
self.resultat1 = jsonResult["meme_categorie"] as? NSArray
self.compteur1 = self.resultat1!.count
print(self.compteur1!)
//self.value = (compteur1 as? Int)!
for self.i=0 ; self.i < self.compteur1! ; self.i = self.i+1 {
if let aStatus = self.resultat1![self.i] as? NSDictionary{
self.vt1 = aStatus["libelle_prod"]! as? String
self.nomProduit.append(self.vt1!)
self.vt2 = aStatus["prix"]! as? String
self.prixProduit.append(self.vt2!)
//print(self.nomProduit[self.i])
}
}
} catch {
print("JSON serialization failed")
}
}
}
task.resume()
}
Then My problem is that this array stays nil:
self.prixProduit.append(self.vt2!)
here is the rest of my code
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 17
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! customCell1
// cell.PrixSim.text = nomProduit[indexPath.row]
print(self.nomProduit[0])
return cell
}
First of all use a custom struct for the category objects, it makes things so much easier.
At the beginning of TableViewController1
class TableViewController1: UITableViewController {
declare this struct
struct Produit {
var id : String
var prix : String
var title : String
}
and a data source array (forget all your other properties / variables)
var produits = [Produit]()
In viewDidLoad get the data, populate the data source array and reload the table view on the main thread.
This code uses Swift native collection types
override func viewDidLoad() {
super.viewDidLoad()
// \(detectionString)
let str = "http://vps43623.ovh.net/yamoinscher/api/products/6194005492077"
let url = NSURL(string: str)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(urlContent, options: [])
if let jsonResult = jsonObject as? [String:AnyObject] {
if let memeCategorie = jsonResult["meme_categorie"] as? [[String:String]] {
for categorie in memeCategorie {
if let prix = categorie["prix"], title = categorie["libelle_prod"], id = categorie["id"] {
self.produits.append(Produit(id: id, prix: prix, title: title))
}
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
}
} catch {
print("JSON serialization failed", error)
}
} else if let connectionError = error {
print("connection error", connectionError)
}
}
task.resume()
}
In numberOfRowsInSection return the actual number of items rather than a hard-coded number.
You can omit numberOfSectionsInTableView since the default value is 1.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return produits.count
}
In cellForRowAtIndexPath get the item by index path and assign the values to your labels (or whatever). For now the values are just printed out.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! customCell1
let produit = produits[indexPath.row]
print(produit.id, produit.title, produit.prix)
return cell
}
}
I'm currently trying to load different entities from my CoreData model into one UITableView but under different sections. I've tried the following method:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
// #warning Incomplete method implementation.
// Return the number of rows in the section.
var rows = 0
if section == 0 {
rows = crew.count
} else if section == 1 {
rows = aircraft.count
} else if section == 2 {
rows = batteries.count
}
return rows
}
But that doesn't seem to work. It returns the first crew.count for every section. I should note that crew, aircraft and batteries are arrays of NSManagedObject.
Does anyone have any advice on how to implement what I'm looking for?
Thanks.
EDIT: Here are other methods I'm implementing for clarity..
var crew = [NSManagedObject]()
var aircraft = [NSManagedObject]()
var batteries = [NSManagedObject]()
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let crewFetchRequest = NSFetchRequest(entityName:"Crew")
var crewError: NSError?
let crewFetchedResults = managedContext.executeFetchRequest(crewFetchRequest, error: &crewError) as? [NSManagedObject]
if let crewResults = crewFetchedResults {
crew = crewResults
} else {
println("Could not fetch \(crewError), \(crewError!.userInfo)")
}
let aircraftFetchRequest = NSFetchRequest(entityName:"Aircraft")
var aircraftError: NSError?
let aircraftFetchedResults = managedContext.executeFetchRequest(aircraftFetchRequest, error: &aircraftError) as? [NSManagedObject]
if let aircraftResults = crewFetchedResults {
aircraft = aircraftResults
} else {
println("Could not fetch \(aircraftError), \(aircraftError!.userInfo)")
}
let batteryFetchRequest = NSFetchRequest(entityName:"Battery")
var batteryError: NSError?
let batteryFetchedResults = managedContext.executeFetchRequest(batteryFetchRequest, error: &batteryError) as? [NSManagedObject]
if let batteryResults = crewFetchedResults {
batteries = batteryResults
} else {
println("Could not fetch \(batteryError), \(batteryError!.userInfo)")
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 3
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
// #warning Incomplete method implementation.
// Return the number of rows in the section.
var rows = 0
if section == 0 {
rows = crew.count
} else if section == 1 {
rows = aircraft.count
} else if section == 2 {
rows = batteries.count
}
return rows
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String?
{
return sectionTitles[section]
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("CrewCell", forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
switch indexPath.section.description
{
case "0":
cell.textLabel!.text = crew[indexPath.item].valueForKey("name") as? String
if crew[indexPath.item].valueForKey("pilot") as? Bool == true {
cell.detailTextLabel!.text = "Pilot"
} else {
cell.detailTextLabel!.text = " "
}
break;
case "1":
cell.textLabel!.text = "Aircraft"
break;
case "2":
cell.textLabel!.text = "Battery"
break;
default:
break;
}
return cell
}
Your code is good, but you will need to set the number of section in tableview as well by implementing
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 3
}
EDITED:
From the edited question I can now clearly see that your problem is with your data source
let aircraftFetchRequest = NSFetchRequest(entityName:"Aircraft")
var aircraftError: NSError?
let aircraftFetchedResults = managedContext.executeFetchRequest(aircraftFetchRequest, error: &aircraftError) as? [NSManagedObject]
if let aircraftResults = crewFetchedResults {
aircraft = aircraftResults
} else {
println("Could not fetch \(aircraftError), \(aircraftError!.userInfo)")
}
let batteryFetchRequest = NSFetchRequest(entityName:"Battery")
var batteryError: NSError?
let batteryFetchedResults = managedContext.executeFetchRequest(batteryFetchRequest, error: &batteryError) as? [NSManagedObject]
if let batteryResults = crewFetchedResults {
batteries = batteryResults
}
if let aircraftResults = crewFetchedResults and if let batteryResults = crewFetchedResults will return crewFetchResults so all crew,aircrafts and batteries arrays are holding exactly the same elements. You must replace these lines with if let aircraftResults = aircraftFetchedResults and if let batteryResults = batteryFetchedResults. Hope I was clear enough