How do I properly load the array (image, image2, image3) images from my class Model in collectionCell?
The class itself Model looks like this:
class Model {
var image: String
var image2: String
var image3: String
var images: [String] = []
var images2: [String] = []
var images3: [String] = []
var ref: FIRDatabaseReference!
init(snapshot: FIRDataSnapshot) {
ref = snapshot.ref
let value = snapshot.value as! NSDictionary
let snap = value["hall1"] as? NSDictionary
let snap2 = value["hall2"] as? NSDictionary
let snap3 = value["hall3"] as? NSDictionary
image = snap?["qwerty"] as? String ?? ""
image2 = snap2?["qwerty"] as? String ?? ""
image3 = snap3?["qwerty"] as? String ?? ""
if let post1 = snap as? [String: AnyObject] {
for (_, value) in post1["images"] as! [String: AnyObject] {
self.images.append(value as! String)
}
}
if let post2 = snap2 as? [String: AnyObject] {
for (_, value) in post2["images"] as! [String: AnyObject] {
self.images2.append(value as! String)
}
}
if let post3 = snap3 as? [String: AnyObject] {
for (_, value) in post3["images"] as! [String: AnyObject] {
self.images3.append(value as! String)
}
}
}
}
In my collectionCell loaded only the first image, I basically understand why, as I understand it is due to the fact that sd_setImage does not display arrays (correct me if I'm wrong), but how to fix it can not figure out.
Code of the collectionCell:
class CollectionViewCell11: UICollectionViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
var imagess: [Model] = []
#IBOutlet weak var collectionView: UICollectionView!
var vc1: ViewController?
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numb erOfItemsInSection section: Int) -> Int {
return imagess.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell12
cell.imageView.sd_setImage(with: URL(string: imagess[indexPath.item].image))
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if vc1 != nil {
let vc2 = vc1!.storyboard?.instantiateViewController(withIdentifier: "ViewController2") as! ViewController2
vc2.photo = [imagess[indexPath.item]]
let backItem = UIBarButtonItem()
backItem.title = ""
vc1!.navigationItem.backBarButtonItem = backItem
vc1!.navigationController?.pushViewController(vc2, animated: true)
}
}
}
To use I need only the class Model, because it contains another array of images which I plan to display the next controller for scroll image.
If your next ViewController is a child of this cell then you can pass the array on to it this way.
You write this in the viewDidLoad() of the next ViewController:
var newImagesArray: [Model] = []
if let parentVC = self.parent as? CollectionViewCell11 {
newImagesArray = parentVC.imagess
}
Related
I am trying to display images onto a collection view. The image url's are saved in the realtime database in Firebase and was wondering, what I am doing wrong? Essentially, users have a node called 'Images' where images they have uploaded are saved inside that node. The names depend on the image Title and the url is the image they have uploaded. Below is my code and a photo of how my database looks like it. If someone could help me out, I would appreciate that a lot. Thanks!
The Issue is that the images are not being displayed. It is successfully able to display the correct amount of images the user has in their account but does not read the image url. My guess is that because each url is under a different value (imageTitle). Any ideas?
My ViewController:
class PicturesViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var imageCollection: UICollectionView!
var customImageFlowLayout: CustomImageFlowLayout!
var images = [UserImages]()
var dbRef: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
dbRef = Database.database().reference().child("users/\(Auth.auth().currentUser!.uid)/Images")
loadDB()
}
func loadDB() {
dbRef.observe(DataEventType.value, with: { (snapshot) in
var newImages = [UserImages]()
for UserImagesSnapshot in snapshot.children {
let UserImagesObject = UserImages(snapshot: UserImagesSnapshot as! DataSnapshot)
newImages.append(UserImagesObject)
}
self.images = newImages
self.imageCollection.reloadData()
})
}
func collectionView(_ imageCollection: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ imageCollection: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = imageCollection.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! ImageCollectionViewCell
let image = images[indexPath.row]
cell.imageView.sd_setImage(with: URL(string: image.url), placeholderImage: UIImage(named: "image1"))
return cell
}
My Array - UserImages
struct UserImages {
let key: String!
let url: String!
let itemsRef: DatabaseReference!
init (url:String, key:String) {
self.key = key
self.url = url
self.itemsRef = nil
}
init(snapshot: DataSnapshot) {
key = snapshot.key
itemsRef = snapshot.ref
let snapshotValue = snapshot.value as? NSDictionary
if let imageUrl = snapshotValue?[""] as? String {
url = imageUrl
} else {
url = ""
}
}
}
Replace
let snapshotValue = snapshot.value as? NSDictionary
if let imageUrl = snapshotValue?[""] as? String {
url = imageUrl
} else {
url = ""
}
with
url = snapshot.value as! String
I have an array of dictionaries like
var array = [["name":abc,"number":123,"address":xyz],["name":def,"number":456,"address":yzx],["name":ghi,"number":789,"address":ooo],["name":jkl,"number":012,"address":ppp]]
Data coming from server
I want to display this data in a collectionview, based on cell for item index. Can anyone help me to do this?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! HistoryCollectionViewCell
var dict = pro[indexpath]
cell.nameobj.text = dict["name"] as! String
cell.addressobj.text = dict["address"] as! String
return cell
}
1) Create a person class
class Person : NSObject {
var name = ""
var number = ""
var address = ""
static let sharedInstance: PersonModel = PersonModel()
func getPersonModel(_ dictionary:NSDictionary) -> Person{
let person = Person()
person.name = dictionary.object(forKey: "name") as! String
person.number = dictionary.object(forKey: "number") as! String
person.address = dictionary.object(forKey: "address") as! String
return person
}
}
2) Map your json to person object model in the respective viewController the next point return the parsed json which can be used in here .
let personDict = convertStringToDictionary(text: "Json obtained from server")
let personArray = personDict.object(forKey: "Person") as! NSArray
for person in PesronArray {
let personModel = PersonModel.sharedInstance.getPersonModel(personDict as! NSDictionary)
persons.add(personModel)
}
3) You can use this method or create your own extension to parse the json into dictionary.
func convertStringToDictionary(text: String) -> [String: Any]? {
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
4) You can populate the collection view Using the persons array form the step 2.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let personObject = persons.object(at: indexPath.row) as? Person
}
Note : This code has not been tested, the json parser and conversion may vary with respect to JSON received from server.
I Have boiled down the problem to this
this closure :
override func viewDidLoad() {
super.viewDidLoad()
let data = homeDataSource()
getPrivatePosts { (posts) in
print("postsCOUNT" , posts!.count)
data.posts = posts!
}
self.datasource = data
collectionView?.reloadData()
}
prints out "postCOUNT 1 postCOUNT 3"
then when I print the count of data.posts I get 0... whats going on with that?? here is the full code
this is a custom UICollectionView:
import LBTAComponents
import Firebase
class homeView: DatasourceController {
override func viewDidLoad() {
super.viewDidLoad()
let data = homeDataSource()
getPrivatePosts { (posts) in
print("postsCOUNT" , posts!.count)
data.posts = posts!
}
self.datasource = data
collectionView?.reloadData()
}
override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width , height: 150)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: view.frame.width, height: 0 )
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: view.frame.width, height: 0)
}
// just to test
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "goToNewPost", sender: self)
}
func getPrivatePosts(completion : #escaping (_ privatePosts : [Post]?) ->()){
// fill posts array with posts from all buddys "privataPosts only"
var ret = [Post]()
staticValuesForData.instance.dataBaseUserref.child((Auth.auth().currentUser?.uid)!).child("contacts").observe( .value , with: { (snapshot) in
let dict = snapshot.children.allObjects as! [DataSnapshot]
for d in dict{
if let contactUid = d.childSnapshot(forPath: "uid").value as? String{
staticValuesForData.instance.dataBaseUserref.child(contactUid).child("privatePosts").observe( .value, with: { (snapshot) in
let posts = snapshot.children.allObjects as! [DataSnapshot]
print("postval" , posts)
for post in posts{
if let dict = post.value as? [String : AnyObject]{
let fullname = dict["fullname"] as! String
let picUrl = dict["picUrl"] as! String
let postContent = dict["postContent"] as! String
let time = dict["time"] as! Int
let uid = dict["uid"] as! String
let username = dict["username"] as! String
print("first name of person who did the post" , fullname)
let reposts = dict["reposts"] as! [String]
let downs = dict["downs"] as! [String]
// possible issue
var comments = [Comment]()
let commentArr = snapshot.childSnapshot(forPath: "comments").children.allObjects as! [DataSnapshot]
for c in commentArr{
if let dict = c.value as? [String : AnyObject]{
let cuid = dict["uid"] as! String
let ccommentText = dict["commentText"] as! String
let cpicUrl = dict["picUrl"] as! String
let cusername = dict["username"] as! String
let ctime = dict["time"] as! Int
let com = Comment(uid: cuid, commentText: ccommentText, time: ctime, picUrl: cpicUrl, username: cusername)
comments.append(com)
}
}
print("HERE : post content\(postContent) username : \(username) commentArr \(comments)")
let postToAdd = Post(postContent: postContent, picUrl: picUrl, userName: username, fullName: fullname, postID: uid, postTime: time, downs: downs, reposts: reposts, comments: comments)
print("LOOK AT MEE \(postToAdd.userName) is the username of the post object \(postToAdd.postContent) is the contetn")
ret.append(postToAdd)
print("RET" , ret)
}
}
completion(ret) // this is where the completion block should be called
})
}
}
})
}
}
This is a datasource object :
import LBTAComponents
class homeDataSource: Datasource {
var posts = [Post]()
override func numberOfItems(_ section: Int) -> Int {
print("COUNT " , posts.count)
return posts.count
}
override func headerClasses() -> [DatasourceCell.Type]? {
return [userHeader.self]
}
override func footerClasses() -> [DatasourceCell.Type]? {
return [userFooter.self]
}
override func cellClasses() -> [DatasourceCell.Type] {
return [userCell.self]
}
override func item(_ indexPath: IndexPath) -> Any? {
return posts[indexPath.item]
}
}
The frame work can be used here :
pod 'LBTAComponents'
You have the same basic misunderstanding twice.
In the second code section you create your ret variable initially empty and then fire of some async tasks. However you call the completion(ret) outside of the async task so it will fire immediately before the async tasks have finished and therefore return your initial empty value.
The fist code also will suffer the same problem in that you create your postArray initially empty then call your getPrivatePosts function supplying a completion handler but that completion handler will be called in an async task so there could be a delay but you use the value immediately and therefore will return the empty initial value.
You shouldn't create your posts array in that way. You should create the a mutable array:
var posts = [Post]()
Then in the viewDidLoad of your view controller you should then populate the array from your service (Firebase).
override func viewDidLoad() {
super.viewDidLoad()
getPrivatePosts() { posts in
self.posts = posts ?? []
}
}
You posts function also is never going to return the data you want from the service since your calling your completion block outside of the scope of the service request. Move the completion block to the bottom of the for loop in the staticValuesForData.instance.dataBaseUserref.child part of the getPrivatePosts function like so:
class func getPrivatePosts(completion : (_ privatePosts : [Post]?) ->. ()){
// fill posts array with posts from all buddys "privataPosts only"
var ret = [Post]()
staticValuesForData.instance.dataBaseUserref.child((Auth.auth().currentUser?.uid)!).child("contacts").observe( .value , with: { (snapshot) in
let dict = snapshot.children.allObjects as! [DataSnapshot]
for d in dict{
if let contactUid = d.childSnapshot(forPath: "uid").value as? String{
staticValuesForData.instance.dataBaseUserref.child(contactUid).child("privatePosts").observe( .value, with: { (snapshot) in
let posts = snapshot.children.allObjects as! [DataSnapshot]
print("postval" , posts)
for post in posts{
if let dict = post.value as? [String : AnyObject]{
let fullname = dict["fullname"] as! String
let picUrl = dict["picUrl"] as! String
let postContent = dict["postContent"] as! String
let time = dict["time"] as! Int
let uid = dict["uid"] as! String
let username = dict["username"] as! String
print("first name of person who did the post" , fullname)
let reposts = dict["reposts"] as! [String]
let downs = dict["downs"] as! [String]
// possible issue
var comments = [Comment]()
let commentArr = snapshot.childSnapshot(forPath: "comments").children.allObjects as! [DataSnapshot]
for c in commentArr{
if let dict = c.value as? [String : AnyObject]{
let cuid = dict["uid"] as! String
let ccommentText = dict["commentText"] as! String
let cpicUrl = dict["picUrl"] as! String
let cusername = dict["username"] as! String
let ctime = dict["time"] as! Int
let com = Comment(uid: cuid, commentText: ccommentText, time: ctime, picUrl: cpicUrl, username: cusername)
comments.append(com)
}
}
print("HERE : post content\(postContent) username : \(username) commentArr \(comments)")
let postToAdd = Post(postContent: postContent, picUrl: picUrl, userName: username, fullName: fullname, postID: uid, postTime: time, downs: downs, reposts: reposts, comments: comments)
print("LOOK AT MEE \(postToAdd.userName) is the username of the post object \(postToAdd.postContent) is the contetn")
ret.append(postToAdd)
print("RET" , ret)
}
}
completion(ret) // this is where the completion block should be called
})
}
}
})
}
I hope this helps.
I am having issues displaying the make and model of the selected cell in another view controller connected by a segue. The Cells display the Vin Number and I want the make and model info for that specific vin number passed to another ViewController. I have attempted to set up passing the values but can not get it to work. Can someone take a look and see if they can spot where I went wrong?
Here is what my firebase database looks like:
Vehicles: {
5UXKR0C34H0X82785: {
VehicleInfo: {
make:"toyota",
model:"corolla",
VinNumber: "5UXKR0C34H0X82785"
}
}
}
Here is my Vehicle model class
import Foundation
import FirebaseDatabase
struct VehicleModel {
var Make: String?
var Model: String?
var VinNumber: String?
init(Make: String?, Model: String?, VinNumber: String?){
self.Make = Make
self.Model = Model
self.VinNumber = VinNumber
}
init(snapshot: DataSnapshot) {
let snapshotValue = snapshot.value as! [String: AnyObject]
VinNumber = snapshotValue["VinNumber"] as? String
Make = snapshotValue["Make"] as? String
Model = snapshotValue["Model"] as? String
}
}
Here is my view controller code
import UIKit
import Firebase
import FirebaseDatabase
class InventoryTableViewController: UITableViewController{
var ref: DatabaseReference!
var refHandle: UInt!
var userList = [VehicleModel]()
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
tableView.delegate = self
tableView.dataSource = self
tableView?.register(UITableViewCell.self, forCellReuseIdentifier:
"cellId")
fetchUsers()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return userList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
// Set cell contents
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for:
indexPath) as UITableViewCell
let eachvehicle = userList[indexPath.row]
cell.textLabel!.text = "\(String(describing: eachvehicle.VinNumber))"
return cell
}
func fetchUsers(){
refHandle = ref.child("Vehicles").observe(.childAdded, with: {
(snapshot) in
if let dictionary = snapshot.childSnapshot(forPath:
"VehicleInfo").value as? [String: AnyObject] {
print(dictionary)
let VinNumber = dictionary["VinNumber"]
let Make = dictionary["Make"]
let Model = dictionary["Model"]
self.userList.insert(VehicleModel(Make: Make as? String, Model:
Model as? String, VinNumber: VinNumber as? String), at: 0)
self.tableView.reloadData()
}
})
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let destination = storyboard.instantiateViewController(withIdentifier:
"AdditionalInfoViewController") as! AdditionalInfoViewController
navigationController?.pushViewController(destination, animated: true)
performSegue(withIdentifier: "toAdditionalInfo", sender: self)
let row = indexPath.row
print("working so far ")
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
makeToPass = currentCell.Model
modelToPass = currentCell.Make
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toMapView" {
var viewController = segue.destination as!
AdditionalInfoViewController
viewController.makeToPass = makeToPass
viewController.modelToPass = modelToPass
}
}
structure for variables in my AdditionalInfoView Controller
var passedValue: String?
var latitudePassedValue: String?
var longitudePassedValue: String?
The problem seems to be in the parsing of the data from the database. The structure has make and model all lower case:
// ...
make:"toyota",
model:"corolla",
// ...
But in the parsing method you're addressing it with the first letter in uppercase:
// ...
Make = snapshotValue["Make"] as? String // "Make"
Model = snapshotValue["Model"] as? String // "Model"
// ...
I am having issues getting my data from firebase to show on my TableView. I only want the vin number to display on the table view. Right now i am either getting cells that display "nil", or nothing in the cells.
My goal is to have each cell display the Vin Number.
Can someone take a look and let me know where i have an issue?
Thanks!!!
Alex
here is what my firebase database looks like
child --Vehicles
child--5UXKR0C34H0X82785
child-- VehicleInfo
then under the "vehicle Info" child it displays these three fields
make:"toyota"
model:"corolla"
VinNumber: "5UXKR0C34H0X82785"
Here is my Vehicle model class
import Foundation
import FirebaseDatabase
struct VehicleModel {
var Make: String?
var Model: String?
var VinNumber: String?
init(Make: String?, Model: String?, VinNumber: String?){
self.Make = Make
self.Model = Model
self.VinNumber = VinNumber
}
init(snapshot: DataSnapshot) {
let snapshotValue = snapshot.value as! [String: AnyObject]
VinNumber = snapshotValue["VinNumber"] as? String
Make = snapshotValue["Make"] as? String
Model = snapshotValue["Model"] as? String
}
}
Here is my view controller code
import UIKit
import Firebase
import FirebaseDatabase
class InventoryTableViewController: UITableViewController{
var ref: DatabaseReference!
var refHandle: UInt!
var userList = [VehicleModel]()
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
tableView.delegate = self
tableView.dataSource = self
tableView?.register(UITableViewCell.self, forCellReuseIdentifier:
"cellId")
fetchUsers()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return userList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
// Set cell contents
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for:
indexPath) as UITableViewCell
let eachvehicle = userList[indexPath.row]
cell.textLabel!.text = "\(String(describing: eachvehicle.VinNumber))"
return cell
}
func fetchUsers(){
refHandle = ref.child("Vehicles").observe(.childAdded, with: {
(snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
print(dictionary)
let VinNumber = dictionary["VinNumber"]
let Make = dictionary["Make"]
let Model = dictionary["Model"]
self.userList.insert(VehicleModel(Make: Make as? String, Model:
Model as? String, VinNumber: VinNumber as? String), at: 0)
self.tableView.reloadData()
}
})
}
}
Also, I am having issues displaying the make and model of the selected cell in another view controller connected by a segue. I have attempted to set up passing the values but can not get it to work.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let destination = storyboard.instantiateViewController(withIdentifier:
"AdditionalInfoViewController") as! AdditionalInfoViewController
navigationController?.pushViewController(destination, animated: true)
performSegue(withIdentifier: "toAdditionalInfo", sender: self)
let row = indexPath.row
print("working so far ")
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
makeToPass = currentCell.Model
modelToPass = currentCell.Make
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toMapView" {
var viewController = segue.destination as! AdditionalInfoViewController
viewController.makeToPass = makeValueToPass
viewController.modelToPass = modelValueToPass
}
}
Correct me if I'm wrong, but this is your data structure, right?
Vehicles: {
5UXKR0C34H0X82785: {
VehicleInfo: {
make:"toyota",
model:"corolla",
VinNumber: "5UXKR0C34H0X82785"
}
}
}
Which means in order to access the data under VehicleInfo, you need to specify that location. There are a few ways you can do this, but one of them would be using childSnapshot(forPath:)
func fetchUsers(){
refHandle = ref.child("Vehicles").observe(.childAdded, with: {
(snapshot) in
if let dictionary = snapshot.childSnapshot(forPath: "VehicleInfo").value as? [String: AnyObject] {
print(dictionary)
let VinNumber = dictionary["VinNumber"]
let Make = dictionary["Make"]
let Model = dictionary["Model"]
self.userList.insert(VehicleModel(Make: Make as? String, Model:
Model as? String, VinNumber: VinNumber as? String), at: 0)
self.tableView.reloadData()
}
})
}
}