ios - Loading 3 Different Information to 3 Different types of Cells -


i have 3 different types of cells in tableviewcontroller. type of cell have , objectid of item different class. go each cell in cellforrowat method , load whatever data is. method has led me 2 problems: 1) dynamic height of 1 of cell not work because it's label's text not found until after cell made. 2) cells have "jumpy" (i can see rows being populated scroll down, guess because loading content every scroll) scroll down tableview.

so i want preload of data before , put in cellforrowat instead of searching data in cellforrowat. fix both problems, have no idea how this. based on coding knowledge place information go in each cell in arrays populate cells accordingly, not know how when using 3 different cells because put information in cells array need use indexpath.row; can not because loading 3 different types of data , adding them different arrays indexpaths not aligned properly. way can think of doing , it's wrong. how can fix problem?

i have copied code @ bottom can see how loading cells , maybe can understanding of how fix issue:

func loadnews() {                 //start finding followers         let followquery = pfquery(classname: "follow")         followquery.wherekey("follower", equalto: pfuser.current()?.objectid! ?? string())         followquery.findobjectsinbackground { (objects, error) in             if error == nil {                 //clean followarray                 self.followarray.removeall(keepingcapacity: false)                  //find users following                 object in objects!{                     self.followarray.append(object.object(forkey: "following") as! string)                 }                 self.followarray.append(pfuser.current()?.objectid! ?? string()) //so can see our own post                   //getting related news post                 let newsquery = pfquery(classname: "news")                 newsquery.wherekey("user", containedin: self.followarray) //find info we're following                 newsquery.limit = 30                 newsquery.adddescendingorder("createdat") //get recent                 newsquery.findobjectsinbackground(block: { (objects, error) in                     if error == nil {                              //clean                         self.newstypearray.removeall(keepingcapacity: false)                         self.objectidarray.removeall(keepingcapacity: false)                         self.newsdatearray.removeall(keepingcapacity: false)                          object in objects! {                                                         self.newstypearray.append(object.value(forkey: "type") as! string) //get type (animal / human / elements)                              self.objectidarray.append(object.value(forkey: "id") as! string) //get object id corresponds different class info                             self.newsdatearray.append(object.createdat) //get when posted                         }                         self.tableview.reloaddata()                     } else {                         print(error?.localizeddescription ?? string())                     }                 })             } else {                 print(error?.localizeddescription ?? string())             }         }        }        override func tableview(_ tableview: uitableview, cellforrowat indexpath: indexpath) -> uitableviewcell { let type = newstypearray[indexpath.row]  if type == "element" { let cell = tableview.dequeuereusablecell(withidentifier: "elementcell") as! elementcell                         let query = query(classname: "element")                         query.wherekey("objectid", equalto: self.objectidarray[indexpath.row])                         query.limit = 1                         query.findobjectsinbackground(block: { (objects, error) in                             if error == nil {                                                             object in objects! {                                      let name =  (object.object(forkey: "type") as! string)                                     let caption = (object.object(forkey: "caption") as! string) //small description (usually 2 lines)                                     cell.captionlabel.text = caption                                  }                                                            } else {                                 print(error?.localizeddescription ?? string())                             }                         }) return cell } else if type == "human" { let cell = tableview.dequeuereusablecell(withidentifier: "humancell") as! humancell                         let query = query(classname: "human")                         query.wherekey("objectid", equalto: self.objectidarray[indexpath.row])                         query.limit = 1                         query.findobjectsinbackground(block: { (objects, error) in                             if error == nil {                                                             object in objects! {                                      let name =  (object.object(forkey: "name") as! string)                                     let caption = (object.object(forkey: "caption") as! string) //small description (1 line)                                     cell.captionlabel.text = caption                                  }                                                            } else {                                 print(error?.localizeddescription ?? string())                             }                         }) return cell  } else { //its animal cell                          let cell = tableview.dequeuereusablecell(withidentifier: "cell") as! animalcell                         let query = query(classname: "animals")                         query.wherekey("objectid", equalto: self.objectidarray[indexpath.row])                         query.limit = 1                         query.findobjectsinbackground(block: { (objects, error) in                             if error == nil {                                                             object in objects! {                                      let caption = (object.object(forkey: "caption") as! string) //large description of animal (can 2 - 8 lines)                                     cell.captionlabel.text = caption                                  }                                                            } else {                                 print(error?.localizeddescription ?? string())                             }                         }) return cell     } } 

----- edit ------

implementation of @woof's logic:

in separate swift file:

class queryobject {     var id: string?     var date: date?     var userid : string?     var name: string?     }  class element: queryobject {     var objectid : string?     var owner : string?     var type : string?     var ability : string?     var strength : string? }  class human: queryobject {     var objectid : string?     var follower : string?     var leader : string? }  class animal: queryobject {     var objectid : string?     var type: string?     var owner : string?     var strength : string?     var speed : string?     var durability : string? } 

in tableviewcontroller:

var tableobjects: [queryobject] = []  func loadnews() {      //start finding followers     let followquery = pfquery(classname: "follow")     followquery.wherekey("follower", equalto: pfuser.current()?.objectid! ?? string())      followquery.findobjectsinbackground { [weak self](objects, error) in         if error == nil {             //clean followarray             self?.followarray.removeall(keepingcapacity: false)              //find users following             object in objects!{                 self?.followarray.append(object.object(forkey: "following") as! string)             }             self?.followarray.append(pfuser.current()?.objectid! ?? string()) //so can see our own post             //this custom additional method make query             self?.querynews(name: "news", followarray: self?.followarray ?? [], completionhandler: { (results) in                 //if block called in background queue, need return main 1 before making update                 dispatchqueue.main.async {                      //check array not nil                     if let objects = results {                         self?.tableobjects = objects                         self?.tableview.reloaddata()                     }else{                         //objects nil                         //do nothing or additional stuff                     }                  }             })          } else {             print(error?.localizeddescription ?? string())         }     } }  //i've made code separated, make easy read private func querynews(name: string, followarray: [string], completionhandler: @escaping (_ results: [queryobject]?) -> void) {      //making temp array     var temporaryarray: [queryobject] = []      //getting related news post     let newsquery = pfquery(classname: "news")     newsquery.wherekey("user", containedin: followarray) //find info we're following     newsquery.limit = 30     newsquery.adddescendingorder("createdat") //get recent     newsquery.findobjectsinbackground(block: { [weak self] (objects, error) in         if error == nil {              //now important thing             //we need create dispatch group make possible load additional data before updating table             //note! if data large, maybe need show kind of activity indicator, otherwise user won't understand going on table              let dispathgroup = dispatchgroup()             object in objects! {                  //detecting type of object                 guard let type = object.value(forkey: "type") as? string else{                     //wrong value or type, don't check other fields of object , start check next 1                     continue                 }                  let userid = object.value(forkey: "user") as? string                 let id = object.value(forkey: "id") as? string                 let date = object.createdat                  //so can check type , create objects                  //and entering our group                 dispathgroup.enter()                  switch type {                 case "element":                     //now make query type                     self?.queryelementclass(name: "element", id: id!, completionhandler: { (name, objectid, owner, type, ability, strength) in                          //i've added check parameters, , if nil, won't add objects table                         //but can change wish                         if let objectname = name, let objectsid = objectid {                             //now can create object                             let newelement = element()                             newelement.userid = userid                             newelement.id = id                             newelement.date = date                              newelement.objectid = objectid                             newelement.owner = owner                             newelement.type = type                             newelement.ability = ability                              newelement.strength = strength                              temporaryarray.append(newelement)                         }                          //don't forget leave dispatchgroup                          dispathgroup.leave()                     })                  case "human":                     //same human                     self?.queryhumanclass(name: "human", id: id!, completionhandler: { (name, objectid, follower, leader) in                          if let objectname = name, let objectsid = objectid {                             let newhuman = human()                             newhuman.userid = userid                             newhuman.id = id                             newhuman.date = date                             temporaryarray.append(newhuman)                         }                          //don't forget leave dispatchgroup                          dispathgroup.leave()                     })                  case "animal":                      //same animal                     self?.queryanimalclass(name: "animal", id: id!, completionhandler: { (name, objectid, type, owner, strength, speed, durability) in                         if let objectname = name, let objectcaption = caption {                             let newanimal = animal()                             newanimal.userid = userid                             newanimal.id = id                             newanimal.date = date                             temporaryarray.append(newanimal)                         }                          //don't forget leave dispatchgroup                          dispathgroup.leave()                     })                  default:                     //unrecognized type                     //don't forget leave dispatchgroup                      dispathgroup.leave()                 }             }             //we need wait tasks entered group             //you can add timeout here, like: user should wait 5 seconds maximum, if queries in group not finished somehow             dispathgroup.wait()              //so finished queries, , can return finished array             completionhandler(temporaryarray)          } else {             print(error?.localizeddescription ?? string())              //we got error, return nil             completionhandler(nil)         }     }) }  //the method making query of additional class private func queryelementclass(name: string, id: string, completionhandler: @escaping (_ name: string?, _ objectid: string?, _ owner: string?,  _ type: string?,  _ ability: string?, _ strength: string?) -> void) {      let query = pfquery(classname: "elements")     query.wherekey("objectid", equalto: id)     query.limit = 1     query.findobjectsinbackground { (objects, error) in         if error == nil {             if let object = objects?.first {                 let name =  object.object(forkey: "type") as? string                 let objectid = object.object(forkey: "objectid") as? string                 let owner = object.object(forkey: "owner") as? string                 let type = object.object(forkey: "type") as? string                 let ability = object.object(forkey: "ability") as? string                 let strength = object.object(forkey: "strength") as? string                  completionhandler(name, objectid, owner, type, ability, strength)             } else {                 print(error?.localizeddescription ?? string())                 completionhandler(nil, nil, nil, nil, nil, nil)             }         } else {             print(error?.localizeddescription ?? string())          }     } }  //the method making query of additional class private func queryhumanclass(name: string, id: string, completionhandler: @escaping (_ name: string?, _ objectid: string?, _ follower: string?,  _ leader: string?) -> void) {     let query = pfquery(classname: "human")     query.wherekey("objectid", equalto: id)     query.limit = 1     query.findobjectsinbackground(block: { (objects, error) in          if let object = objects?.first {              let name =  object.object(forkey: "type") as? string             let objectid = object.object(forkey: "objectid") as? string             let follower = object.object(forkey: "follower") as? string             let leader = object.object(forkey: "leader") as? string              completionhandler(name, objectid, follower, leader)          } else {             print(error?.localizeddescription ?? string())              completionhandler(nil, nil, nil, nil)         }     }) }  //the method making query of additional class private func queryanimalclass(name: string, id: string, completionhandler: @escaping (_ name: string?, _ objectid: string?, _ owner: string?, _ type: string?,  _ strength: string?,  _ speed: string?, _ durability: string?) -> void) {      let query = pfquery(classname: "animals")     query.wherekey("objectid", equalto: id)     query.limit = 1     query.findobjectsinbackground(block: { (objects, error) in          if let object = objects?.first {              let name =  object.object(forkey: "type") as? string             let objectid = object.object(forkey: "objectid") as? string             let owner = object.object(forkey: "owner") as? string             let strength = object.object(forkey: "strength") as? string             let type = object.object(forkey: "type") as? string             let speed = object.object(forkey: "speed") as? string             let durability = object.object(forkey: "durability") as? string              completionhandler(name, objectid, owner, type, strength, speed, durability)          } else {             print(error?.localizeddescription ?? string())              completionhandler(nil, nil, nil, nil, nil, nil, nil)         }     }) } 

looking @ projects see multiple arrays different data. hard edit code kind of structure.

i make in way:

1) create objects store values, structs/classes animal, human, element. if have same values ids or whatever, can create super class object , make other objects subclasses

2) create 1 array data source table objects not values

//if there no super class var objects:[anyobject] = [] 

or

//for superclass var objects:[yoursuperclass] = [] 

in code below use superclass, can change anyobject

3) make method fill array of objects before updating table:

//i think better use clousures , make data fetching in different queue  func loadnews(completionhandler: @escaping (_ objects: [yoursuperclass]) -> void){       yourbackgroundqueue.async{                var objects = // fill here array objects                // important return data in main thread make update               dispatchqueue.main.async{                      completion(objects)               }       } } 

and fill our datasourse array, call method when need:

func updatetable(){         loadnews(){ [weak self] objects in        self?.objects = objects        self?.tablewview.reloaddata() } 

so have array of objects

4)we can use downcast specific class set cells:

override func tableview(_ tableview: uitableview, cellforrowat indexpath: indexpath) -> uitableviewcell {           let object = objects[indexpath.row]         //making downcast          if let animal = object as? animal,  let cell = tableview.dequeuereusablecell(withidentifier: "animalcell") as? animalcell         //now can fill cell properties animal object has        //return cell        return cell  }  if let human = object as? human,  let cell = tableview.dequeuereusablecell(withidentifier: "humancell") as? humancell         //do stuff humancell        //return cell        return cell  }   //same way can detect , fill other cells  //this return empty cell if there object in array wasn't recognized. in case app won't crash, see wrong   return uitableviewcell() } 

so main thoughts:

  • make full loading before updating in separated queue (there may exceptions, if have load images , don't wait images downloaded before showing table, better fill cells using simple values , make image loading inside each cell , show activity indicator each one)

  • create array of objects parameters, instead of making several arrays simple values

  • use array of objects determine cell type in table.

============edit================ note! i've made code in playground without importing pfquery if there errors, let me know. if stuck, let me know, maybe check project directly

so, new code

//declaring objects in separated file class queryobject {     var id: string?     var date: date? //change of date object.createdat has different type     var caption: string?     var name: string? //    var type: string? //use var don't need have subclasses }  //if subclasses not have unique parameters, can left 1 class queryobject, without subclasses //in case uncomment "type" variable in queryobject, can check var in cellforrowat class animal: queryobject {     //add additional properties }  class human: queryobject {     //add additional properties }  class element: queryobject {     //add additional properties }  class yourcontroller: uitableviewcontroller {     //allocate var inside viewcontroller     var tableobjects: [queryobject] = []      func loadnews() {          //start finding followers         let followquery = pfquery(classname: "follow")         followquery.wherekey("follower", equalto: pfuser.current()?.objectid! ?? string())          followquery.findobjectsinbackground { [weak self](objects, error) in             if error == nil {                 //clean followarray                 self?.followarray.removeall(keepingcapacity: false)                  //find users following                 object in objects!{                     self?.followarray.append(object.object(forkey: "following") as! string)                 }                 self?.followarray.append(pfuser.current()?.objectid! ?? string()) //so can see our own post                 //this custom additional method make query                  self?.querynews(name: "news", followarray: self?.followarray ?? [], completionhandler: { (results) in                     //if block called in background queue, need return main 1 before making update                     dispatchqueue.main.async {                          //check array not nil                         if let objects = results {                             self?.tableobjects = objects                             self?.tableview.reloaddata()                         }else{                             //objects nil                             //do nothing or additional stuff                         }                      }                 })              } else {                 print(error?.localizeddescription ?? string())             }         }     }      //i've made code separated, make easy read     private func querynews(name: string, followarray: [string], completionhandler: @escaping (_ results: [queryobject]?) -> void) {          //making temp array         var temporaryarray: [queryobject] = []          //getting related news post         let newsquery = pfquery(classname: "news")         newsquery.wherekey("user", containedin: followarray) //find info we're following         newsquery.limit = 30         newsquery.adddescendingorder("createdat") //get recent         newsquery.findobjectsinbackground(block: { [weak self] (objects, error) in             if error == nil {                  //now important thing                 //we need create dispatch group make possible load additional data before updating table                 //note! if data large, maybe need show kind of activity indicator, otherwise user won't understand going on table                  let dispathgroup = dispatchgroup()                  object in objects! {                      //detecting type of object                     guard let type = object.value(forkey: "type") as? string else{                         //wrong value or type, don't check other fields of object , start check next 1                         continue                     }                      let id = object.value(forkey: "id") as? string                     let date = object.createdat                      //so can check type , create objects                      //and entering our group                     dispathgroup.enter()                      switch type {                     case "animal":                          //now make query type                         self?.queryadditionalclass(name: "animals", id: id, completionhandler: { (name, caption) in                              //i've added check parameters, , if nil, won't add objects table                             //but can change wish                             if let objectname = name, let objectcaption = caption {                                 //now can create object                                  let newanimal = animal()                                 newanimal.id = id                                 newanimal.date = date                                  temporaryarray.append(newanimal)                             }                              //don't forget leave dispatchgroup                              dispathgroup.leave()                          })                     case "human":                          //same human                         self?.queryadditionalclass(name: "human", id: id, completionhandler: { (name, caption) in                              if let objectname = name, let objectcaption = caption {                                 let newhuman = human()                                 newhuman.id = id                                 newhuman.date = date                                 temporaryarray.append(newhuman)                             }                              //don't forget leave dispatchgroup                              dispathgroup.leave()                          })                     case "elements":                          //same element                         self?.queryadditionalclass(name: "element", id: id, completionhandler: { (name, caption) in                              if let objectname = name, let objectcaption = caption {                                 let newelement = element()                                 newelement.id = id                                 newelement.date = date                                 temporaryarray.append(newelement)                             }                              //don't forget leave dispatchgroup                              dispathgroup.leave()                          })                     default:                         //unrecognized type                         //don't forget leave dispatchgroup                          dispathgroup.leave()                     }                  }                  //we need wait tasks entered group                 //you can add timeout here, like: user should wait 5 seconds maximum, if queries in group not finished somehow                 dispathgroup.wait()                  //so finished queries, , can return finished array                 completionhandler(temporaryarray)              } else {                 print(error?.localizeddescription ?? string())                  //we got error, return nil                 completionhandler(nil)             }         })     }      //the method making query of additional class     private func queryadditionalclass(name: string, id: string, completionhandler: @escaping (_ name: string?, _ caption: string?) -> void) {          let query = pfquery(classname: name)         query.wherekey("objectid", equalto: id)         query.limit = 1         query.findobjectsinbackground(block: { (objects, error) in              if let object = objects?.first {                  let name =  object.object(forkey: "type") as? string                 let caption = object.object(forkey: "caption") as? string                  completionhandler(name, caption)              }else{                 print(error?.localizeddescription ?? string())                  completionhandler(nil, nil)             }     }      //now can detect object have , show correct cell depending on object's type     override func tableview(_ tableview: uitableview, cellforrowat indexpath: indexpath) -> uitableviewcell {         let object = tableobjects[indexpath.row]          //making downcast or if won't use subclasses, check type variable using switch case made in loadnews()         if let animal = object as? animal,             let cell = tableview.dequeuereusablecell(withidentifier: "animalcell") as? animalcell {              cell.captionlabel.text = animal.caption              //do additional stuff animal cell              //return cell             return cell         }          if let human = object as? human,             let cell = tableview.dequeuereusablecell(withidentifier: "humancell") as? humancell {              cell.captionlabel.text = human.caption              //do stuff humancell              //return cell             return cell         }          if let element = object as? element,             let cell = tableview.dequeuereusablecell(withidentifier: "elementcell") as? elementcell {              cell.captionlabel.text = element.caption              //do stuff elementcell              //return cell             return cell         }          return uitableviewcell()     } } 

Comments

Popular posts from this blog

ubuntu - PHP script to find files of certain extensions in a directory, returns populated array when run in browser, but empty array when run from terminal -

php - How can i create a user dashboard -

javascript - How to detect toggling of the fullscreen-toolbar in jQuery Mobile? -