I have an array of 115 objects containing name and photo url string from Firebase. Printing the data shows results so i know its pulling data correctly.
The problem is the Cells are never populated by the data.
If i add a print(name) inside the class DJProfileCell: UICollectionViewCell it never gets called so i believe thats where the issue is.
class VLCDJProfileViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var similarArtistsCollection: UICollectionView!
var ref: DatabaseReference!
let profileCellID = "cellId"
var djObject = SimilarDJ(image: "image1", name: "name1")
var djsSimilarArray = [SimilarDJ]()
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
loadDJs()
collectionView?.register(DJProfileCell.self, forCellWithReuseIdentifier: profileCellID)
}
func loadDJs(){
let allDJs = self.ref.child("DJ")
allDJs.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let djsDict = snap.value as! [String: Any]
let PhotoUrl = djsDict["PhotoUrl"] as! String
let name = djsDict["name"] as! String + " "
self.djObject = SimilarDJ (image: PhotoUrl, name: name + " ")
self.djsSimilarArray.append(self.djObject)
self.similarArtistsCollection.reloadData();
}
})
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return djsSimilarArray.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: profileCellID, for: indexPath) as! DJProfileCell
cell.djprofile = djsSimilarArray[indexPath.item]
return cell
}
class DJProfileCell: UICollectionViewCell {
var djprofile: SimilarDJ? {
didSet {
guard let djImageProfile = djprofile?.image else {return}
guard let djNameProfile = djprofile?.name else {return}
let url = URL(string: djImageProfile)
djImageView.kf.indicatorType = .activity
djImageView.kf.setImage(with: url)
djImageLabel.text = djNameProfile
djImageLabel.adjustsFontSizeToFitWidth = true
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
func setup(){
self.backgroundColor = .white
self.addSubview(djImageView)
self.addSubview(djImageLabel)
}
let djImageView: UIImageView = {
let iv = UIImageView()
// iv.contentMode = .scaleAspectFit
// iv.backgroundColor = .green
return iv
}()
let djImageLabel: MarqueeLabel = {
let label = MarqueeLabel()
label.text = "Name"
label.textColor = UIColor.black
label.font = label.font.withSize(14)
label.textAlignment = .center
return label
}()
required init?(coder aDecoder: NSCoder) {
fatalError("init has not been implemented")
}
}
struct SimilarDJ {
let image: String?
let name: String?
}
in cell class - djImageView and djImageLabel are never added to the view's hierarchy. I see no IBOutlet and no addSubview().
Related
How to Retrieve the images from firebase storage in which I have manually created folders according the category of my "wallpaper app" and I have created a list of "items" according to the category of my wallpaper. I am not understanding how to retrieve the images according to the "items"-(it is there in the code below) from the storage of firebase and display it accordingly.
I just want to store all the images according to the category of the "items" which is specified in the code below and display it in the app.
import UIKit
import GlidingCollection
import FirebaseStorage
import Firebase
class ViewController: UIViewController {
#IBOutlet var glidingView: GlidingCollection!
fileprivate var collectionView: UICollectionView!
fileprivate var items = ["riches", "animals", "nature", "architecture","toys"]
fileprivate var images: [[UIImage?]] = []
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
func setup() {
setupGlidingCollectionView()
loadImages()
}
func setupGlidingCollectionView() {
glidingView.dataSource = self
let nib = UINib(nibName: "CollectionCell", bundle: nil)
collectionView = glidingView.collectionView
collectionView.register(nib, forCellWithReuseIdentifier: "Cell")
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = glidingView.backgroundColor
}
func loadImages() {
let storage = Storage.storage()
let storageRef = storage.reference()
let starsRef = storageRef.child("Animals")
starsRef.downloadURL { url, error in
if let error = error {
print("not there")
} else {
for item in self.items {
let imageURLs = FileManager.default.fileUrls(for: "jpeg", fileName: item)
var images: [UIImage?] = []
for url in imageURLs {
guard let data = try? Data(contentsOf: url) else { continue }
let image = UIImage(data: data)
images.append(image)
}
self.images.append(images)
}
}
}
}
}
// MARK: - Setup
// MARK: - CollectionView 🎛
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as? CollectionCell else { return UICollectionViewCell() }
let section = glidingView.expandedItemIndex
let image = images[section][indexPath.row]
cell.imageView.image = image
cell.contentView.clipsToBounds = true
let layer = cell.layer
let config = GlidingConfig.shared
layer.shadowOffset = config.cardShadowOffset
layer.shadowColor = config.cardShadowColor.cgColor
layer.shadowOpacity = config.cardShadowOpacity
layer.shadowRadius = config.cardShadowRadius
layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let section = glidingView.expandedItemIndex
let item = indexPath.item
print("Selected item #\(item) in section #\(section)")
}
}
// MARK: - Gliding Collection 🎢
extension ViewController: GlidingCollectionDatasource {
func numberOfItems(in collection: GlidingCollection) -> Int {
return items.count
}
func glidingCollection(_ collection: GlidingCollection, itemAtIndex index: Int) -> String {
return "– " + items[index]
}
}
in another swift file linked to the cell of the collection view."CollectionCell.xib"
import UIKit
class CollectionCell: UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
}
I want all the images categorised and according to the items displayed in my app.
When I select an image from a CollectionView and this cell, I want to get the name of that image and save it to CloudKit. My main question is how to get the name of the selected image?
This is my CollectionViewCell:
class AddImageViewCell: UICollectionViewCell {
#IBOutlet weak var addListImageView: UIImageView!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.cornerRadius = self.frame.size.width * 0.2
self.layer.borderWidth = 2
self.layer.borderColor = UIColor.clear.cgColor
}
}
Some of my AddListViewController:
class AddListViewController: UIViewController {
var imageArray : [UIImage] = [UIImage(named: "Images/do001.png")!,
UIImage(named: "Images/do002.png")!,
UIImage(named: "Images/do003.png")!,
UIImage(named: "Images/do004.png")!,
UIImage(named: "Images/do005.png")!,
UIImage(named: "Images/do006.png")!,
UIImage(named: "Images/do007.png")!,
UIImage(named: "Images/do008.png")!,
UIImage(named: "Images/do009.png")!,
UIImage(named: "Images/do010.png")!,
UIImage(named: "Images/do011.png")!,
UIImage(named: "Images/do012.png")!,
UIImage(named: "Images/do013.png")!,
UIImage(named: "Images/do014.png")!,
UIImage(named: "Images/do015.png")!,
UIImage(named: "Images/do016.png")!]
let selectedImage = 0
And here I want to get the name of the image file:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = addImageCollectionView.cellForItem(at: indexPath) as! AddImageViewCell
cell.layer.borderColor = UIColor.white.cgColor
var imageInCell = cell.addListImageView.image
print("\(String(describing: imageInCell))")
}
Here CellForItemAt:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : AddImageViewCell = addImageCollectionView.dequeueReusableCell(withReuseIdentifier: "AddImageCell", for: indexPath) as! AddImageViewCell
cell.addListImageView.image = imageArray[indexPath.item]
return cell
}
Don't read the value from the cells. Cells are reused. Instead, use your model.
I'd suggest to do:
var imageNameArray: [String] = ["Images/do001.png", "Images/do002.png", ...]
In collectionView(_:cellForItemAt:):
let imageName = imageNameArray[indexPath.item]
let image = UIImage(named: imageName)
cell.addListImageView.image = image
In collectionView(_:didSelectItemAt:):
let selectedImageName = imageNameArray[indexPath.item]
You can use this extension,
it creates a property name and set it value using custom init,
if you did't use the custom init it will return empty String
extension UIImage {
convenience init?(named: String, saveName: Bool) {
self.init(named: named)
if saveName {
self.name = named
}
}
struct Name {
static var name: String = ""
}
var name: String {
get {
return Name.name
}
set(newValue) {
Name.name = newValue
}
}
}
Usage:
let img = UIImage(named: "test.png", saveName: true)
img?.name // "test.png"
// With image view
let imageView = UIImageView(image: img)
imageView.image?.name
Here is my class
class myCell: UICollectionViewCell {
public var myProp:String = ""
let myControl:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.Text = myProp
return label
}()
}
I want to use the myProp within the creation of my UI elements but the compiler is saying I cannot use myProp.
or why is this incorrect
class myCell: UICollectionViewCell {
public var myLabel:UILabel = UILabel()
let myControl:UIView = {
let ui = UIView()
myLabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
return lbl
}()
ui.AddSubView(myLabel)
return ui
}()
}
This will work
class CollectionViewCell: UICollectionViewCell {
public var myProp:String = ""
override init(frame: CGRect) {
super.init(frame: frame)
}
func setText() {
let myControl:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = myProp
return label
}()
self.addSubview(myControl)
}
}
During rendering, in cellForRowAtIndex need to implement this for adding subview with text.
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CollectionViewCell
cell.myProp = "text"
cell.setText()
return cell
}
Attaching the code here.
This is the API: " https://bittrex.com/api/v1.1/public/getmarketsummaries "
I am wanting to add the "High" and "Low" in columns beside "MarketName"
Also, I want to refresh this every 10 seconds.
The refresh part, I am getting error, for sendUpdateRequest().
The full code is:
import UIKit
var listData = [[String : AnyObject]]()
class DemoJsonTableViewController: UITableViewController {
var listData = [[String : AnyObject]]()
override func viewDidLoad() {
super.viewDidLoad()
var timer : Timer? = nil
timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: (#selector(self.sendUpdateRequest)), userInfo: nil, repeats: true)
func sendUpdateRequest(){
let url:String = "https://bittrex.com/api/v1.1/public/getmarketsummaries"
let urlRequest = URL(string: url)
URLSession.shared.dataTask(with: urlRequest!) { (data, response, error) in
if(error != nil){
print(error.debugDescription)
}
else{
do{
var response = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:AnyObject]
self.listData = response["result"] as! [[String:AnyObject]]
DispatchQueue.main.async {
self.tableView.reloadData()
}
}catch let error as NSError{
print(error)
}
}
}.resume()
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.listData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let item = self.listData[indexPath.row]
cell.textLabel?.text = item["MarketName"] as? String
let lastValue = item["Last"] as? NSNumber
cell.detailTextLabel?.text = lastValue?.stringValue
print(self.listData.count)
return cell
}
This is what you need. Copy paste the entire code.
import UIKit
class DemoJsonTableViewController: UITableViewController {
var listData = [[String : AnyObject]]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(DrawerCell.self, forCellReuseIdentifier: "Cell")
self.sendUpdateRequest()
Timer.scheduledTimer(timeInterval: 10, target: self, selector: (#selector(self.sendUpdateRequest)), userInfo: nil, repeats: true)
}
func sendUpdateRequest(){
let url:String = "https://bittrex.com/api/v1.1/public/getmarketsummaries"
let urlRequest = URL(string: url)
URLSession.shared.dataTask(with: urlRequest!) { (data, response, error) in
if(error != nil){
print(error.debugDescription)
}
else{
do{
var response = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:AnyObject]
self.listData = response["result"] as! [[String:AnyObject]]
DispatchQueue.main.async {
self.tableView.reloadData()
}
}catch let error as NSError{
print(error)
}
}
}.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.listData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! DrawerCell
let item = self.listData[indexPath.row]
cell.TitleLabel.text = item["MarketName"] as? String
cell.Description.text = String(describing: item["Last"] as? NSNumber)
cell.HighLabel.text = String(describing: item["High"] as! NSNumber)
cell.LowLabel.text = String(describing: item["Low"] as! NSNumber)
return cell
}
}
class DrawerCell: UITableViewCell {
var TitleLabel: UILabel = UILabel()
var Description: UILabel = UILabel()
var HighLabel: UILabel = UILabel()
var LowLabel: UILabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
TitleLabel.textColor = UIColor.black
TitleLabel.font = UIFont.init(name: "AppleSDGothicNeo-Bold", size: 10)
TitleLabel.textAlignment = .left
contentView.addSubview(TitleLabel)
Description.textColor = UIColor.black
Description.font = UIFont.init(name: "AppleSDGothicNeo-Bold", size: 8)
Description.textAlignment = .left
contentView.addSubview(Description)
HighLabel.textColor = UIColor.black
HighLabel.font = UIFont.init(name: "AppleSDGothicNeo-Bold", size: 10)
HighLabel.textAlignment = .center
contentView.addSubview(HighLabel)
LowLabel.textColor = UIColor.black
LowLabel.font = UIFont.init(name: "AppleSDGothicNeo-Bold", size: 10)
LowLabel.textAlignment = .center
contentView.addSubview(LowLabel)
}
override func layoutSubviews() {
super.layoutSubviews()
TitleLabel.frame = CGRect(x: 10, y: 0, width: self.frame.size.width - 110, height: 20)
Description.frame = CGRect(x: 10, y: 20, width: self.frame.size.width - 110, height: 20)
HighLabel.frame = CGRect(x: self.frame.size.width - 110, y: 0, width: 100, height: 20)
LowLabel.frame = CGRect(x: self.frame.size.width - 110, y: 20, width: 100, height: 20)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func prepareForReuse() {
TitleLabel.text = nil
Description.text = nil
HighLabel.text = nil
LowLabel.text = nil
}
}
Couple of issues with the code here:
Firstly, you are defining sendUpdateRequest() inside viewDidLoad(). Move the whole function outside these enclosing braces to get rid of that error. Second, you don't need to define listData outside the class.
You can then use your existing code for the refresh functionality.
For the columns for High And Low, you will need to subclass a UITableViewCell and add extra labels.
Follow the steps outlined here: How to add more than two labels to prototype cell?.
I'm loading some data from a web server and I'm getting ambiguous reference to member tableview numberOfRowsInSection? Below is my swift file. What am i missing here?
TableViewController.swift
import UIKit
class TableViewController: UITableViewController {
var viewModel:ViewModel!
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel = ViewModel()
self.tableView.delegate = viewModel
self.tableView.dataSource = viewModel
self.tableView.registerNib(UINib(nibName: "TableViewCell", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: tableViewCellIdentifier)
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 50;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
ViewModel.swift
import UIKit
class ViewModel: NSObject,UITableViewDelegate, UITableViewDataSource, UICollectionViewDelegate, UICollectionViewDataSource {
var dataArrayPosts: NSArray!
var posts:[Post] = []
override init() {
super.init()
let myURL = NSURL(string: "mydomainURLhere");
let requestPosts = NSMutableURLRequest(URL: myURL!);
requestPosts.HTTPMethod = "POST";
let postStringVars = ""
requestPosts.HTTPBody = postStringVars.dataUsingEncoding(NSUTF8StringEncoding);
let taskPosts = NSURLSession.sharedSession().dataTaskWithRequest(requestPosts){ data, response, error in
if error != nil{
print("error\(error)");
return;
}
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
//print(json)
if let category = json["Posts"] as? NSMutableArray{
//print(category)
_ = category
self.dataArrayPosts = json["Posts"] as! NSMutableArray;
dispatch_async(dispatch_get_main_queue()) {
self.posts.removeAll()
for item in self.dataArrayPosts {
let post = Post(postDesc: (item["postDesc"] as? String)!,imageName: (item["imageName"] as? String)!,bName: (item["bName"] as? String)!,postDate: (item["postDate"] as? String)!,postShowsUntil:(item["postShowsUntil"] as? String)!)
self.posts.append(post)
// Ambiguous on reloadData???????
self.tableView.reloadData()
}
}
}else{
dispatch_async(dispatch_get_main_queue()) {
print("Nothing Was Found")
}
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
taskPosts.resume()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(tableViewCellIdentifier, forIndexPath: indexPath) as! TableViewCell
let post = posts[indexPath.row]
cell.quoteTextLabel.text = post.postDesc
cell.nameLabel.text = post.bName
if let imageName = post.imageName where !imageName.isEmpty{
cell.photoView?.image = UIImage(named: imageName)
cell.photoWidthConstraint.constant = kDefaultPhotoWidth
cell.photoRightMarginConstraint.constant = kDefaultPhotoRightMargin
}
else {
cell.photoView?.image = nil
cell.photoWidthConstraint.constant = 0
cell.photoRightMarginConstraint.constant = 0
}
cell.contentView.setNeedsLayout()
cell.contentView.layoutIfNeeded()
return cell
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return posts.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(collectionViewCellIdentifier, forIndexPath: indexPath) as! CollectionViewCell
return cell
}
}
None of the things that ViewModel inherits from expose a tableView property, your ViewModel needs a reference to your UITableViewController. As the ViewController owns the ViewModel it should be a weak reference, something like the following
class ViewModel: NSObject,UITableViewDelegate, UITableViewDataSource, UICollectionViewDelegate, UICollectionViewDataSource {
weak let tableViewController:UITableViewController
var dataArrayPosts: NSArray!
var posts:[Post] = []
override init(controller:UITableViewController) {
self.tableViewController = controller
super.init()
// ...
self.tableViewController.tableView.reloadData()
}
class TableViewController: UITableViewController {
var viewModel:ViewModel!
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel = ViewModel(controller:self)
// ...
}