building my first iOS app here and I am stuck. I searched online and did not find anything about this particular issue.
Basically I have a UICollectionView displaying some data. Everything works fine on the simulator, but on the actual iPhone, it's like the UICollectionView is just not there.
No error messages and the app doesn't crash, and all the other elements of the App are functioning properly so I am quite perplexed.
Code:
//
// SecondViewController.swift
//
// Created by pc on 3/5/19.
// Copyright © 2019 BF. All rights reserved.
//
import UIKit
import WebKit
import SwiftSoup
class SecondViewController: UIViewController, WKNavigationDelegate, UICollectionViewDataSource {
#IBOutlet var dataTable: UICollectionView!
#IBOutlet weak var viewHeader: UILabel!
#IBOutlet weak var viewFooter: UILabel!
#IBAction func backButtonClicked(_ sender: UIButton) {
dismissVC()
}
var webView: WKWebView!
var tableContent = [[String]]()
var vSpinner : UIView?
var testString = [[String]]()
var submittedValue2: String = ""
var currentSelection2: String = ""
override func viewDidAppear(_ animated: Bool) {
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
present(alert, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.dataTable.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
//dataTable.backgroundColor = UIColor.white
dataTable.delegate = self as? UICollectionViewDelegate
dataTable.dataSource = self
if let layout = dataTable.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
}
webView = WKWebView()
webView.navigationDelegate = self
//view = webView
let url = URL(string: "https://someWebsite.com")!
webView.load(URLRequest(url: url))
webView.allowsBackForwardNavigationGestures = true
runData()
}
///////ADDS DELAY
func runData() {
DispatchQueue.main.asyncAfter(deadline: .now() + 4) { // Change `2.0` to the desired number of seconds.
self.setWebViewValue(name: "txtValue", data: self.submittedValue2, vin: self.currentSelection2)
}
}
///////// PULLS DATA
func setWebViewValue(name: String, data: String, vin: String) {
if vin == "VIN:" {
webView.evaluateJavaScript("document.getElementById('rbRequest_1').click()", completionHandler: nil)
}
else {
}
webView.evaluateJavaScript("document.getElementById(\"\(name)\").value = \"\(data)\"", completionHandler: nil)
webView.evaluateJavaScript("document.getElementById('btnSubmit').click()", completionHandler: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { // Change `2.0` to the desired number of seconds.
self.webView.evaluateJavaScript("document.documentElement.outerHTML.toString()") { (result, error) -> Void in
if error != nil {
print(error!)
}
let document = try! SwiftSoup.parse(result as! String)
for row in try! document.select("table[id=\"gvTests\"] tr") {
var rowContent = [String]()
for col in try! row.select("td") {
let colContent = try! col.text()
rowContent.append(colContent)
}
self.tableContent.append(rowContent)
}
if self.tableContent.isEmpty == false {
//self.tableContent.remove(at: 0)
self.tableContent[0] = ["Make","Model","Year","Date","Pass/Fail","Certificate","Referee"]
}
print(self.tableContent)
self.tableContent = self.transpose(input: self.tableContent)
if self.tableContent.isEmpty == false {
self.dataTable.reloadData()
}
}
self.dismiss(animated: false, completion: nil)
}
}
////// Collection View functions
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if tableContent.isEmpty == false {
return tableContent[0].count
}
else {
return 0
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
if tableContent.isEmpty == false {
return tableContent.count
}
else {
return 0
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let title = UILabel(frame: CGRect(x: 0,y: 0,width: cell.bounds.size.width,height: cell.bounds.size.height))
for subview in cell.contentView.subviews {
subview.removeFromSuperview()
}
cell.contentView.addSubview(title)
title.text = tableContent[indexPath.section][indexPath.item]
//title.textColor = UIColor.black
title.layer.borderWidth = 1
title.layer.borderColor = UIColor.black.cgColor
title.textAlignment = NSTextAlignment.center
return cell
}
public func transpose<T>(input: [[T]]) -> [[T]] {
if input.isEmpty { return [[T]]() }
let count = input[0].count
var out = [[T]](repeating: [T](), count: count)
for outer in input {
for (index, inner) in outer.enumerated() {
out[index].append(inner)
}
}
return out
}
func dismissVC() {
dismiss(animated: true, completion: nil)
}
}
This might cause from constraint on your collectionView.
Turns out the issue is not with the UIcollectionview, but that the data is empty because instead of being extracted from the website like it is when ran in the simulator. I will post another question for that. Thanks all.
Related
I have made a tableView with cells that take the data from an API. I have imported ViewAnimator Package because I want to add some animation when the cells appear but the animation starts while the tableview had already be presented with data.
Maybe I have made a mistake at the logic but I can't find the solution.
The OpeningViewController is this :
import UIKit
import ViewAnimator
class OpeningViewController: UIViewController {
//MARK: - IBProperties
#IBOutlet var openingImg: UIImageView!
#IBOutlet var startButton: UIButton!
//MARK: - Properties
var nft : Nft?
//MARK: - Life Cyrcle
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let animation = AnimationType.from(direction: .top, offset: 50)
openingImg.animate(animations: [animation] , delay: 0.3, duration: 2)
openingImg.layer.shadowColor = UIColor.black.cgColor
openingImg.layer.shadowOffset = CGSize(width: 0, height: 0)
openingImg.layer.shadowOpacity = 0.65
openingImg.layer.shadowRadius = 10
}
//MARK: - Methods
#IBAction func startApp(_ sender: Any) {
HapticsManager.shared.selectionVibrate()
let storyBoard = UIStoryboard(name: "Lobby", bundle: nil)
let controller = storyBoard.instantiateViewController(withIdentifier: "LobbyViewController") as! LobbyViewController
controller.modalTransitionStyle = .flipHorizontal
self.navigationController?.pushViewController(controller, animated: true)
}
}
The presentation happens in LobbyViewController :
import UIKit
import ViewAnimator
class LobbyViewController: UIViewController {
// MARK: - IBProperties
#IBOutlet weak var tableView: UITableView!
// MARK: - Properties
var data: [DataEnum] = []
var likes:[Int] = []
var numlikes: Int = 0
var nfts: [Nft] = []
let creators : [Creator] = []
var icons: [Icon] = []
var loadData = APICaller()
// MARK: - Life Cyrcle
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "AssetTableViewCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "AssetTableViewCell")
let nib2 = UINib(nibName: "CreatorsTableViewCell", bundle: nil)
tableView.register(nib2, forCellReuseIdentifier: "CreatorsTableViewCell")
tableView.dataSource = self //method to generate cells,header and footer before they are displaying
tableView.delegate = self //method to provide information about these cells, header and footer ....
downloadJSON {
self.tableView.reloadData()
print("success")
}
loadData.downloadData { (result) in
print(result)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let animation = AnimationType.from(direction: .top, offset: 300)
UIView.animate(views: tableView.visibleCells,
animations: [animation], delay: 1, duration: 2)
}
//stelnei ta dedomena apo to kathe row ston PresentViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PresentViewController {
if tableView.cellForRow(at: tableView.indexPathForSelectedRow!) is AssetTableViewCell {
destination.nft = nfts[tableView.indexPathForSelectedRow!.row-1]
destination.delegate = self
} else {
//add alert action
let alert = UIAlertController(title: "Invalid Touch", message: "You press wrong row. Choose one of the following list.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: {
return
})
}
}
}
// MARK: - Methods
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://public.arx.net/~chris2/nfts.json")
URLSession.shared.dataTask(with: url!) { [self] data, response, error in
if error == nil {
do {
self.nfts = try JSONDecoder().decode([Nft].self, from: data!)
let creators = nfts.map { nft in
nft.creator
}
self.data.append(.type1(creators: creators))
self.nfts.forEach { nft in
self.data.append(.type2(nft: nft))
}
DispatchQueue.main.async {
completed()
}
}
catch {
print("error fetching data from api")
}
}
}.resume()
}
}
// MARK: - Extensions
extension LobbyViewController : UITableViewDelegate , UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
indexPath.row == 0 ? 100 : UITableView.automaticDimension
}
//gemizo ta rows tou table
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch self.data[indexPath.item] {
case .type1(let creators):
print("--->", creators)
let cell = tableView.dequeueReusableCell(withIdentifier: "CreatorsTableViewCell",
for: indexPath) as! CreatorsTableViewCell
cell.layer.cornerRadius = 15
cell.layer.shadowColor = UIColor.black.cgColor
cell.layer.shadowOffset = CGSize(width: 0, height: 0)
cell.layer.shadowOpacity = 0.8
cell.layer.shadowRadius = 15
cell.layer.cornerRadius = cell.frame.height/2
cell.updateCreators(creators)
return cell
case .type2(let nft):
let cell = tableView.dequeueReusableCell(withIdentifier: "AssetTableViewCell",
for: indexPath) as! AssetTableViewCell
cell.nameLabel?.text = nft.name
cell.nameLabel.layer.cornerRadius = cell.nameLabel.frame.height/2
cell.likesLabel?.text = "\((numlikes))"
let imgUrl = (nft.image_url)
print(imgUrl)
cell.iconView.downloaded(from: imgUrl)
cell.iconView.layer.cornerRadius = cell.iconView.frame.height/2
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetails", sender: self)
}
}
extension LobbyViewController : TestDelegate{
func sendBackTheLikess(int: Int) {
numlikes = int
tableView.reloadData()
}
}
// MARK: - Enums
enum DataEnum {
case type1(creators: [Creator])
case type2(nft: Nft)
}
// MARK: - Struct
struct Constants {
static let url = "https://public.arx.net/~chris2/nfts.json"
}
The APICaller :
import Foundation
final class APICaller {
static let shared = APICaller()
public struct Constants {
static let url = "https://public.arx.net/~chris2/nfts.json"
}
public func downloadData(completion:#escaping (Result<[Nft], Error>) -> Void )
{
guard let url = URL(string:Constants.url)else{
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
//print(response)
print("here")
guard let data = data , error == nil else{
print("something went wrong with data")
return
}
print("here4")
//mexri edo exoume parei ta data kai tora me to do-catch tha ta kanoume convert se object
do{
//Decode the response
let nfts = try JSONDecoder().decode([Nft].self, from: data)
completion(.success(nfts))
print(nfts)
}catch{
completion(.failure(error))
}
}
task.resume()
}
}
and here is a video as a gif of what happen fro better understanding
https://gifyu.com/image/SEGkZ
I have an api that I parse and I present the data on my tableview that generates cells . I have make an animation that normally must act before as a present animation for the cells but this did not happen . the result is that the cells appear and suddenly disappear and then appear with the animation .
in the link you can find the gif that I upload the shows what happen .
https://gifyu.com/image/SEGkZ
here is the code :
The OpeningViewController is this :
import UIKit
import ViewAnimator
class OpeningViewController: UIViewController {
//MARK: - IBProperties
#IBOutlet var openingImg: UIImageView!
#IBOutlet var startButton: UIButton!
//MARK: - Properties
var nft : Nft?
//MARK: - Life Cyrcle
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let animation = AnimationType.from(direction: .top, offset: 50)
openingImg.animate(animations: [animation] , delay: 0.3, duration: 2)
openingImg.layer.shadowColor = UIColor.black.cgColor
openingImg.layer.shadowOffset = CGSize(width: 0, height: 0)
openingImg.layer.shadowOpacity = 0.65
openingImg.layer.shadowRadius = 10
}
//MARK: - Methods
#IBAction func startApp(_ sender: Any) {
HapticsManager.shared.selectionVibrate()
let storyBoard = UIStoryboard(name: "Lobby", bundle: nil)
let controller = storyBoard.instantiateViewController(withIdentifier: "LobbyViewController") as! LobbyViewController
controller.modalTransitionStyle = .flipHorizontal
self.navigationController?.pushViewController(controller, animated: true)
}
}
The presentation happens in LobbyViewController :
import UIKit
import ViewAnimator
class LobbyViewController: UIViewController {
// MARK: - IBProperties
#IBOutlet weak var tableView: UITableView!
// MARK: - Properties
var data: [DataEnum] = []
var likes:[Int] = []
var numlikes: Int = 0
var nfts: [Nft] = []
let creators : [Creator] = []
var icons: [Icon] = []
var loadData = APICaller()
// MARK: - Life Cyrcle
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "AssetTableViewCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "AssetTableViewCell")
let nib2 = UINib(nibName: "CreatorsTableViewCell", bundle: nil)
tableView.register(nib2, forCellReuseIdentifier: "CreatorsTableViewCell")
tableView.dataSource = self //method to generate cells,header and footer before they are displaying
tableView.delegate = self //method to provide information about these cells, header and footer ....
downloadJSON {
self.tableView.reloadData()
print("success")
}
loadData.downloadData { (result) in
print(result)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let animation = AnimationType.from(direction: .top, offset: 300)
UIView.animate(views: tableView.visibleCells,
animations: [animation], delay: 1, duration: 2)
}
//stelnei ta dedomena apo to kathe row ston PresentViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PresentViewController {
if tableView.cellForRow(at: tableView.indexPathForSelectedRow!) is AssetTableViewCell {
destination.nft = nfts[tableView.indexPathForSelectedRow!.row-1]
destination.delegate = self
} else {
//add alert action
let alert = UIAlertController(title: "Invalid Touch", message: "You press wrong row. Choose one of the following list.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: {
return
})
}
}
}
// MARK: - Methods
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://public.arx.net/~chris2/nfts.json")
URLSession.shared.dataTask(with: url!) { [self] data, response, error in
if error == nil {
do {
self.nfts = try JSONDecoder().decode([Nft].self, from: data!)
let creators = nfts.map { nft in
nft.creator
}
self.data.append(.type1(creators: creators))
self.nfts.forEach { nft in
self.data.append(.type2(nft: nft))
}
DispatchQueue.main.async {
completed()
}
}
catch {
print("error fetching data from api")
}
}
}.resume()
}
}
// MARK: - Extensions
extension LobbyViewController : UITableViewDelegate , UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
indexPath.row == 0 ? 100 : UITableView.automaticDimension
}
//gemizo ta rows tou table
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch self.data[indexPath.item] {
case .type1(let creators):
print("--->", creators)
let cell = tableView.dequeueReusableCell(withIdentifier: "CreatorsTableViewCell",
for: indexPath) as! CreatorsTableViewCell
cell.layer.cornerRadius = 15
cell.layer.shadowColor = UIColor.black.cgColor
cell.layer.shadowOffset = CGSize(width: 0, height: 0)
cell.layer.shadowOpacity = 0.8
cell.layer.shadowRadius = 15
cell.layer.cornerRadius = cell.frame.height/2
cell.updateCreators(creators)
return cell
case .type2(let nft):
let cell = tableView.dequeueReusableCell(withIdentifier: "AssetTableViewCell",
for: indexPath) as! AssetTableViewCell
cell.nameLabel?.text = nft.name
cell.nameLabel.layer.cornerRadius = cell.nameLabel.frame.height/2
cell.likesLabel?.text = "\((numlikes))"
let imgUrl = (nft.image_url)
print(imgUrl)
cell.iconView.downloaded(from: imgUrl)
cell.iconView.layer.cornerRadius = cell.iconView.frame.height/2
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetails", sender: self)
}
}
extension LobbyViewController : TestDelegate{
func sendBackTheLikess(int: Int) {
numlikes = int
tableView.reloadData()
}
}
// MARK: - Enums
enum DataEnum {
case type1(creators: [Creator])
case type2(nft: Nft)
}
// MARK: - Struct
struct Constants {
static let url = "https://public.arx.net/~chris2/nfts.json"
}
The APICaller :
import Foundation
final class APICaller {
static let shared = APICaller()
public struct Constants {
static let url = "https://public.arx.net/~chris2/nfts.json"
}
public func downloadData(completion:#escaping (Result<[Nft], Error>) -> Void )
{
guard let url = URL(string:Constants.url)else{
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
//print(response)
print("here")
guard let data = data , error == nil else{
print("something went wrong with data")
return
}
print("here4")
//mexri edo exoume parei ta data kai tora me to do-catch tha ta kanoume convert se object
do{
//Decode the response
let nfts = try JSONDecoder().decode([Nft].self, from: data)
completion(.success(nfts))
print(nfts)
}catch{
completion(.failure(error))
}
}
task.resume()
}
}
Just move your animation from viewDidAppear(animated:) to tableView(_:willDisplay:forRowAt:) and call for each cell separately. Also don’t forget not to call this animation once it finished.
I have been practicing doing Swift programmatic UI and MVVM. I tried to follow the following tutorial: https://medium.com/flawless-app-stories/mvvm-in-ios-swift-aa1448a66fb4 and also I added a tab bar and settings view controller just for practice. My issue is in my HomeView controller I see the data is being obtained by my table is displaying empty cells.
HomeViewController:
import Foundation
import UIKit
class HomeViewController: UIViewController{
private var employeeViewModel: EmployeeViewModel!
var employeeTableView: UITableView = UITableView()
private var datasource: EmployeeTableViewDataSource<EmployeeTableViewCell, EmployeeData>!
override func viewDidLoad() {
// some code here
configure()
callToViewModelForUIUpdate()
}
func configure(){
self.title = "Home"
self.view.backgroundColor = .lightGray
let width = self.view.frame.width
let navigationBar: UINavigationBar = UINavigationBar(frame: CGRect(x: 0, y: 0, width: width, height: 44))
let navigationItem = UINavigationItem(title: "Home")
navigationBar.setItems([navigationItem], animated: false)
self.view.addSubview(navigationBar)
let screenSize: CGRect = UIScreen.main.bounds
// get the screens width and height
let screenWidth = screenSize.width
let screenHeight = screenSize.height
// set the table to the screens width and height
employeeTableView.frame = CGRect(x: 0,y: 0, width: screenWidth, height: screenHeight)
//register the cell to the table
employeeTableView.register(EmployeeTableViewCell.self, forCellReuseIdentifier: "EmployeeTableViewCell")
//add the table to the view
self.view.addSubview(employeeTableView)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
}
func callToViewModelForUIUpdate(){
self.employeeViewModel = EmployeeViewModel()
self.employeeViewModel.bindEmployeeViewModelToController = {
self.updateDataSource()
}
}
func updateDataSource(){
self.datasource = EmployeeTableViewDataSource(cellIdentifier: "EmployeeTableViewCell", items: self.employeeViewModel.empData.data, configureCell: { (cell, evm) in
cell.employeeIdLabel.text = "\(evm.id)"
cell.employeeNameLabel.text = evm.employeeName
})
DispatchQueue.main.async {
self.employeeTableView.dataSource = self.datasource
self.employeeTableView.reloadData()
}
}
}
EmployeeViewModel:
import Foundation
class EmployeeViewModel: NSObject{
private var apiService: APIService!
private(set) var empData: Employee!{
didSet{
self.bindEmployeeViewModelToController()
}
}
var bindEmployeeViewModelToController: (() -> ()) = {}
override init(){
super.init()
self.apiService = APIService()
callFuncToGetEmpData()
}
func callFuncToGetEmpData(){
self.apiService.apiToGetEmployeeData{(empData) in
self.empData = empData
}
}
}
EmployeeTableViewCell:
import Foundation
import UIKit
class EmployeeTableViewCell: UITableViewCell{
var employeeIdLabel = UILabel()
var employeeNameLabel = UILabel()
var employee: EmployeeData?{
didSet{
employeeIdLabel.text = String(describing: employee?.id)
employeeNameLabel.text = employee?.employeeName
}
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
APIService:
import Foundation
class APIService: NSObject{
private let source = URL(string: "http://dummy.restapiexample.com/api/v1/employees")!
func apiToGetEmployeeData(completion: #escaping (Employee)-> ()){
URLSession.shared.dataTask(with: source){(data, urlResponse, error) in
if let data = data{
let jsonDecoder = JSONDecoder()
let empData = try! jsonDecoder.decode(Employee.self, from: data)
completion(empData)
}
}.resume()
}
}
EmployeeTableViewDataSource:
import Foundation
import UIKit
class EmployeeTableViewDataSource<CELL : UITableViewCell,T> : NSObject, UITableViewDataSource {
private var cellIdentifier : String!
private var items : [T]!
var configureCell : (CELL, T) -> () = {_,_ in }
init(cellIdentifier : String, items : [T], configureCell : #escaping (CELL, T) -> ()) {
self.cellIdentifier = cellIdentifier
self.items = items
self.configureCell = configureCell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! CELL
let item = self.items[indexPath.row]
self.configureCell(cell, item)
return cell
}
}
EmployeeModel:
import Foundation
// MARK: - Welcome
struct Employee: Decodable {
let status: String
let data: [EmployeeData]
}
// MARK: - Datum
struct EmployeeData: Decodable {
let id, employeeSalary, employeeAge: Int
let employeeName: String
let profileImage: String
enum CodingKeys: String, CodingKey {
case id
case employeeName = "employee_name"
case employeeSalary = "employee_salary"
case employeeAge = "employee_age"
case profileImage = "profile_image"
}
}
ViewController:
import UIKit
class ViewController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
configureTabBar()
}
func configureTabBar(){
//createFirstTab view controller
let firstTab = HomeViewController()
// create nav controller to embed view controller in
let navVc = UINavigationController(rootViewController: firstTab)
// set the title and image at the bottom tab bar for the 1st VC
let firstItem = UITabBarItem(title: "Home", image: UIImage(systemName: "pencil"), selectedImage: UIImage(systemName: "pencil"))
//set the item to pertain to the first tab
firstTab.tabBarItem = firstItem
//createSecondTab
let secondTab = SettingsViewController()
let secondNavVC = UINavigationController(rootViewController: secondTab)
let secondItem = UITabBarItem(title: "Settings", image: UIImage(systemName: "More"), selectedImage: UIImage(systemName: "More"))
secondTab.tabBarItem = secondItem
//add the VCs as VCs of the tab bar
self.viewControllers = [navVc, secondNavVC]
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
print("Selected \(viewController.title)")
}
}
Everything populates and navigation is fine but the tableview is just empty.
In the code you posted, I don't see anywhere that you are adding the employeeIdLabel and employeeNameLabel labels to the cell's view hierarchy.
Try using this cell class:
class EmployeeTableViewCell: UITableViewCell{
var employeeIdLabel = UILabel()
var employeeNameLabel = UILabel()
var employee: EmployeeData?{
didSet{
employeeIdLabel.text = String(describing: employee?.id)
employeeNameLabel.text = employee?.employeeName
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
employeeIdLabel.backgroundColor = .green
employeeNameLabel.backgroundColor = .yellow
contentView.addSubview(employeeIdLabel)
contentView.addSubview(employeeNameLabel)
employeeIdLabel.translatesAutoresizingMaskIntoConstraints = false
employeeNameLabel.translatesAutoresizingMaskIntoConstraints = false
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
employeeIdLabel.topAnchor.constraint(equalTo: g.topAnchor),
employeeIdLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
employeeIdLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
employeeNameLabel.topAnchor.constraint(equalTo: employeeIdLabel.bottomAnchor, constant: 8.0),
employeeNameLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
employeeNameLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
employeeNameLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Result:
As a side note, make sure you gracefully handle a failed attempt to get the data:
class APIService: NSObject{
private let source = URL(string: "http://dummy.restapiexample.com/api/v1/employees")!
func apiToGetEmployeeData(completion: #escaping (Employee)-> ()){
URLSession.shared.dataTask(with: source){(data, urlResponse, error) in
if let data = data{
let jsonDecoder = JSONDecoder()
do {
let empData = try jsonDecoder.decode(Employee.self, from: data)
completion(empData)
} catch {
print("Failed to get data!")
// show an alert or something...
}
}
}.resume()
}
}
The Delegated function fires but crashes as its nil, the objects in the items array is populated by CoreData, this var model: CoreDataModel = CoreDataModel(CoreDataController.shared) has to be instantiated rather than as expected in the viewDidLoad to prevent a nil error for the table view row count (model.items.count)
On startup the items array is the complete Sqlite DB, on search its the subset of the table and printing to the console proves the array is changed and only has the subset of Albums.
BaseViewController
import UIKit
import CoreData
protocol UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
}
protocol MasterModel {
var client: LastFMClient { get }
func searchFeed(with userSearchTerm: String?, completion: #escaping (Bool) -> Void)
}
protocol DataReloadTableViewDelegate: class {
func reloadAlbumsTable()
}
class BaseViewController: UITableViewController, MasterModel {
let cellId = "sdlfjowieurewfn3489844224947824dslaksjfs;ad"
let logoContainer = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
let image = UIImage(named: "lastFMRedBlack")
let searchBar = UISearchBar()
let client = LastFMClient()
var model: CoreDataModel = CoreDataModel(CoreDataController.shared)
private var searchResults: Root?
override func viewDidLoad() {
super.viewDidLoad()
setupSearchController()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
tableView.register(SubtitleTableViewCell.self, forCellReuseIdentifier: cellId)
tableView.tableFooterView = UIView(frame: CGRect.zero)
tableView.separatorColor = UIColor(red: 72.5/255, green: 0/255, blue: 0/255, alpha: 1)
imageView.contentMode = .scaleAspectFit
imageView.image = image
logoContainer.addSubview(imageView)
navigationItem.titleView = logoContainer
print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask))
model.delegate = self
model.fetchAllAlbums()
}
// MARK - SearchBar
private func setupSearchController() {
searchBar.sizeToFit()
searchBar.placeholder = "Search for Album"
searchBar.delegate = self
showSearchBarButton(shouldShow: true)
}
func showSearchBarButton (shouldShow: Bool) {
if shouldShow {
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(handleShowSearchBar))
} else {
searchBar.showsCancelButton = true
navigationItem.rightBarButtonItem = nil
}
}
func search(shouldShow: Bool) {
showSearchBarButton(shouldShow: !shouldShow)
navigationItem.titleView = shouldShow ? searchBar : logoContainer
}
#objc func handleShowSearchBar(){
search(shouldShow: true)
searchBar.becomeFirstResponder()
}
// MARK - API Request
func searchFeed(with userSearchTerm: String?, completion: #escaping (Bool) -> Void) {
// Use the API to get data
client.getFeed(from: LastFMRequest.albumSearch(userSearchTerm: userSearchTerm) ) { result in
switch result {
case .success(let data):
do {
let data = try DataParser.parse(data, type: RootNode.self)
self.searchResults = data.results
completion(true)
} catch {
print(error.localizedDescription)
completion(false)
}
case .failure(let error):
print(error.localizedDescription)
completion(false)
}
}
}
}
extension BaseViewController: UISearchBarDelegate {
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searchBar.text = nil
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
search(shouldShow: false)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let searchTextString = searchBar.text else { return }
searchFeed(with: searchTextString.replacingOccurrences(of: " ", with: "+").lowercased(), completion: {_ in
if self.searchResults!.albumMatches.album.count == 0 {
DispatchQueue.main.async {
let alertController = UIAlertController(title: "No Albums Found", message: "Try Another Keyword(s)", preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default) { action in
print("Pressed OK")
}
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
}
} else {
let dataManager = DataManager(data: self.searchResults!)
do {
try dataManager.saveData()
} catch {
print(error)
}
}
})
search(shouldShow: false)
searchBar.resignFirstResponder()
}
}
class SubtitleTableViewCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension BaseViewController: UITableViewDataSource {
var numberOrSections: Int { return 1 }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard section >= 0 && section < numberOrSections else { return 0 }
return model.items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let albumItem = model.items[indexPath.row]
cell.textLabel?.text = albumItem.value(forKeyPath: "name") as? String
cell.detailTextLabel?.text = albumItem.value(forKeyPath: "artist") as? String
cell.accessoryType = .disclosureIndicator
// Populate the cell from the object
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = DetailViewController()
let albumItem = model.items[indexPath.row]
vc.iamgeURL = albumItem.value(forKeyPath: "imageUrl") as? String
vc.albumName = albumItem.value(forKeyPath: "name") as? String
navigationController?.pushViewController(vc, animated: true)
}
}
extension BaseViewController: DataReloadTableViewDelegate {
func reloadAlbumsTable(){
DispatchQueue.main.async {
print(self.model.items.count)
self.tableView.reloadData()
}
}
}
CoreDataModel
import Foundation
import CoreData
class CoreDataModel {
weak var delegate: DataReloadTableViewDelegate?
let coreDataController: CoreDataController
var items:[Albums] = []
init(_ coreDataController: CoreDataController) {
self.coreDataController = coreDataController
self.coreDataController.mainContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
}
internal func saveSearchAlbums(responseData: Root) throws {
let newSearch = Searches(context: coreDataController.mainContext)
newSearch.searchQuery = responseData.attr.forField
for (_, element) in responseData.albumMatches.album.enumerated() {
let newAlbum = Albums(context: coreDataController.mainContext)
let artistName = element.artist
let albumName = element.name
let imageUrlTwo = element.image[2].text
let imageUrlZero = element.image[0].text
let imageUrlOne = element.image[1].text
var imageUrl: String = ""
if !JustLetters.blank(text: imageUrlTwo) {
imageUrl = imageUrlTwo
}
if !JustLetters.blank(text: imageUrlZero) {
imageUrl = imageUrlZero
}
if !JustLetters.blank(text: imageUrlOne) {
imageUrl = imageUrlOne
}
if !JustLetters.blank(text: artistName) && !JustLetters.blank(text: albumName) && !JustLetters.blank(text: imageUrl) {
newAlbum.searches = newSearch
newAlbum.artist = artistName
newAlbum.name = albumName
newAlbum.imageUrl = imageUrl
newSearch.addToAlbums(newAlbum)
}
}
// Save context
coreDataController.saveContext()
fetchAlbumsByKeyword(searchTerm: responseData.attr.forField)
}
internal func fetchAlbumsByKeyword(searchTerm: String) {
// Create Fetch Request
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Albums")
// Add Sort Descriptor
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
// Add Predicate
let predicate = NSPredicate(format: "name CONTAINS[c] %#", searchTerm)
fetchRequest.predicate = predicate
do {
items = try coreDataController.mainContext.fetch(fetchRequest) as! [Albums]
} catch {
print(error)
}
delegate!.reloadAlbumsTable()
}
internal func fetchAllAlbums() {
// Create the FetchRequest for all searches
let allAlbums: NSFetchRequest = Albums.fetchRequest()
do {
items = try coreDataController.mainContext.fetch(allAlbums)
} catch {
print(error)
}
}
}
Delegate is assigned/set on the class name and not on any instance identifier so a delegate can only be set on a class with one instance
I am unable to show specific proof, I rely on cause and effect of a single change to make the above statement.
I had more than one instance of CoreDataModel, I set the delegate on the first instance in the viewDidLoad, the second instance is set on the search click. I refactored out the DataManager Class which itself creates and instance of CoreDataModel.
The final result is the delegate is not nil and performs as expected. Repo 'show_album_search_results' branch
This is my first time working with collection views and I am struggling quite a bit. I set up a collection view layout in the "setupView" function, and then implemented the typical view methods, like numberOfSections and cellForRowAtIndexPath. Right now, I'm just trying to get the collection view to work so I'm just using an image that I already have in my workspace, instead of getting pictures from an API (that will come later).
Here is the code I am using. Why is it not displaying anything other than a black screen ?
import UIKit
import Alamofire
import AlamofireImage
class PhotoBrowserCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let goldenWordsYellow = UIColor(red: 247.00/255.0, green: 192.00/255.0, blue: 51.00/255.0, alpha: 0.5)
#IBOutlet weak var menuButton:UIBarButtonItem!
#IBOutlet var picturesCollectionView: UICollectionView!
var pictureObjects = NSMutableOrderedSet(capacity: 1000)
var customRefreshControl = UIRefreshControl()
let PhotoBrowserCellIdentifier = "PhotoBrowserCell"
var dateFormatter = NSDateFormatter()
var nodeIDArray = NSMutableArray()
var timeStampDateString : String!
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView!.registerClass(PhotoBrowserCollectionViewCell.self, forCellWithReuseIdentifier: PhotoBrowserCellIdentifier)
self.revealViewController().rearViewRevealWidth = 280
collectionView!.delegate = self
collectionView!.dataSource = self
customRefreshControl = UIRefreshControl()
customRefreshControl.backgroundColor = goldenWordsYellow
customRefreshControl.tintColor = UIColor.whiteColor()
self.picturesCollectionView!.addSubview(customRefreshControl)
navigationController?.setNavigationBarHidden(false, animated: true)
navigationItem.title = "Pictures"
setupView()
populatePhotos()
self.dateFormatter.dateFormat = "dd/MM/yy"
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5 // setting an arbitrary value in case pictureObjects is not getting correctly populated
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(PhotoBrowserCellIdentifier, forIndexPath: indexPath) as! PhotoBrowserCollectionViewCell
cell.imageView.image = UIImage(named: "mail")
return cell
}
And here is my setupView function.
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("ShowPhoto", sender: (self.pictureObjects.objectAtIndex(indexPath.item) as! PictureElement).imageURL)
}
func setupView() {
navigationController?.setNavigationBarHidden(false, animated: true)
let layout = UICollectionViewFlowLayout()
let itemWidth = (view.bounds.size.width - 2) / 3
layout.itemSize = CGSize(width: itemWidth, height: itemWidth)
layout.minimumInteritemSpacing = 1.0
layout.minimumLineSpacing = 1.0
collectionView!.collectionViewLayout = layout
navigationItem.title = "Pictures"
customRefreshControl.tintColor = UIColor.whiteColor()
customRefreshControl.addTarget(self, action: "handleRefresh", forControlEvents: .ValueChanged)
self.collectionView!.addSubview(customRefreshControl)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(120, 120)
}
Everything looks really messed up in the storyboard. I'm using a UICollectionViewController swift class (not a UIViewController + collectionView combo), and I don't really know which outlets I should be using. Here is my list of connections on the entire view controller:
And finally, here is my very simple view hierarchy.
I truly have no idea why the collection view isn't working properly. Any ideas ?
EDIT 1: I changed a lot of code and ended up getting my collectionView to work. For those who are struggling with a similar issue, here is the entirety of the code I am using for the collectionView. Maybe it will help you fix problems in your own collectionView code
import UIKit
import Alamofire
import AlamofireImage
class PhotoBrowserCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let goldenWordsYellow = UIColor(red: 247.00/255.0, green: 192.00/255.0, blue: 51.00/255.0, alpha: 0.5)
#IBOutlet weak var menuButton:UIBarButtonItem!
#IBOutlet var picturesCollectionView: UICollectionView!
var temporaryPictureObjects = NSMutableOrderedSet(capacity: 1000)
var pictureObjects = NSMutableOrderedSet(capacity: 1000)
var goldenWordsRefreshControl = UIRefreshControl()
var revealViewControllerIndicator : Int = 0
let imageCache = NSCache()
var customView: UIView!
var labelsArray: [UILabel] = []
var isAnimating = false
var currentColorIndex = 0
var currentLabelIndex = 0
var timer : NSTimer!
var populatingPhotos = false
var currentPage = 0
let PhotoBrowserCellIdentifier = "PhotoBrowserCell"
var dateFormatter = NSDateFormatter()
var nodeIDArray = NSMutableArray()
var timeStampDateString : String!
var cellLoadingIndicator = UIActivityIndicatorView()
var scrollViewDidScrollLoadingIndicator = UIActivityIndicatorView()
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView!.registerClass(PhotoBrowserCollectionViewCell.self, forCellWithReuseIdentifier: PhotoBrowserCellIdentifier)
self.cellLoadingIndicator.backgroundColor = goldenWordsYellow
self.cellLoadingIndicator.hidesWhenStopped = true
if self.revealViewController() != nil {
revealViewControllerIndicator = 1
menuButton.target = self.revealViewController()
menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
self.revealViewController().rearViewRevealWidth = 280
collectionView!.delegate = self
collectionView!.dataSource = self
goldenWordsRefreshControl = UIRefreshControl()
goldenWordsRefreshControl.backgroundColor = goldenWordsYellow
goldenWordsRefreshControl.tintColor = UIColor.whiteColor()
self.collectionView!.addSubview(goldenWordsRefreshControl)
navigationController?.setNavigationBarHidden(false, animated: true)
navigationItem.title = "Pictures"
setupView()
populatePhotos()
self.dateFormatter.dateFormat = "dd/MM/yy"
self.cellLoadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
self.cellLoadingIndicator.color = goldenWordsYellow
self.cellLoadingIndicator.center = (self.collectionView?.center)!
self.collectionView!.addSubview(cellLoadingIndicator)
self.collectionView!.bringSubviewToFront(cellLoadingIndicator)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
if goldenWordsRefreshControl.refreshing {
if !isAnimating {
holdRefreshControl()
}
}
}
func holdRefreshControl() {
timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "handleRefresh", userInfo: nil, repeats: true)
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (pictureObjects.count) // setting an arbitrary value in case pictureObjects is not getting correctly populated
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(PhotoBrowserCellIdentifier, forIndexPath: indexPath) as! PhotoBrowserCollectionViewCell
if let pictureObject = pictureObjects.objectAtIndex(indexPath.row) as? PictureElement {
let title = pictureObject.title ?? "" // if pictureObject.title == nil, then we return an empty string
let timeStampDateObject = NSDate(timeIntervalSince1970: NSTimeInterval(pictureObject.timeStamp))
let timeStampDateString = dateFormatter.stringFromDate(timeStampDateObject)
let author = pictureObject.author ?? ""
let issueNumber = pictureObject.issueNumber ?? ""
let volumeNumber = pictureObject.volumeNumber ?? ""
let nodeID = pictureObject.nodeID ?? 0
let imageURL = pictureObject.imageURL ?? "http://goldenwords.ca/sites/all/themes/custom/gw/logo.png"
cell.request?.cancel()
if let image = self.imageCache.objectForKey(imageURL) as? UIImage {
cell.imageView.image = image
} else {
cell.imageView.image = nil
cell.request = Alamofire.request(.GET, imageURL).responseImage() { response in
if let image = response.result.value {
self.imageCache.setObject(response.result.value!, forKey: imageURL)
if cell.imageView.image == nil {
cell.imageView.image = image
}
}
}
}
}
return cell
}
self.performSegueWithIdentifier("ShowPhoto", sender: self)
}
func setupView() {
navigationController?.setNavigationBarHidden(false, animated: true)
let layout = UICollectionViewFlowLayout()
let itemWidth = (view.bounds.size.width) / 3
layout.itemSize = CGSize(width: itemWidth, height: itemWidth)
layout.minimumInteritemSpacing = 1.0
layout.minimumLineSpacing = 1.0
collectionView!.collectionViewLayout = layout
navigationItem.title = "Pictures"
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(((self.collectionView?.frame.width)!*0.5)-2, self.collectionView!.frame.height/3)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowPhoto" {
let detailViewController = segue.destinationViewController as! PhotoViewerViewController
let indexPaths = self.collectionView!.indexPathsForSelectedItems()
let indexPath = indexPaths![0] as! NSIndexPath
let item = indexPath.item
}
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
if (scrollView.contentOffset.y + view.frame.size.height > scrollView.contentSize.height * 0.75) {
populatePhotos()
}
}
func populatePhotos() {
if populatingPhotos {
return
}
populatingPhotos = true
self.cellLoadingIndicator.startAnimating()
self.temporaryPictureObjects.removeAllObjects()
Alamofire.request(GWNetworking.Router.Pictures(self.currentPage)).responseJSON() { response in
if let JSON = response.result.value {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) {
var nodeIDArray : [Int]
if (JSON .isKindOfClass(NSDictionary)) {
for node in JSON as! Dictionary<String, AnyObject> {
let nodeIDValue = node.0
var lastItem : Int = 0
self.nodeIDArray.addObject(nodeIDValue)
if let pictureElement : PictureElement = PictureElement(title: "Picture", nodeID: 0, timeStamp: 0, imageURL: "http://goldenwords.ca/sites/all/themes/custom/gw/logo.png", author: "Staff", issueNumber: "Issue # error", volumeNumber: "Volume # error") {
pictureElement.title = node.1["title"] as! String
pictureElement.nodeID = Int(nodeIDValue)!
let timeStampString = node.1["revision_timestamp"] as! String
pictureElement.timeStamp = Int(timeStampString)!
if let imageURL = node.1["image_url"] as? String {
pictureElement.imageURL = imageURL
}
if let author = node.1["author"] as? String {
pictureElement.author = author
}
if let issueNumber = node.1["issue_int"] as? String {
pictureElement.issueNumber = issueNumber
}
if let volumeNumber = node.1["volume_int"] as? String {
pictureElement.volumeNumber = volumeNumber
}
if self.pictureObjects.containsObject(pictureElement) {
// do not add the pictureElement to the set of pictures
} else {
lastItem = self.temporaryPictureObjects.count // Using a temporary set to not handle the dataSource set directly (safer).
self.temporaryPictureObjects.addObject(pictureElement)
}
let indexPaths = (lastItem..<self.temporaryPictureObjects.count).map { NSIndexPath(forItem: $0, inSection: 0) }
}
}
let timeStampSortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false)
self.temporaryPictureObjects.sortUsingDescriptors([timeStampSortDescriptor])
}
dispatch_async(dispatch_get_main_queue()) {
for object in self.temporaryPictureObjects {
self.pictureObjects.addObject(object)
}
self.temporaryPictureObjects.removeAllObjects()
self.collectionView!.reloadData()
self.cellLoadingIndicator.stopAnimating()
self.goldenWordsRefreshControl.endRefreshing()
self.currentPage++
self.populatingPhotos = false
}
}
}
}
}
func handleRefresh() {
goldenWordsRefreshControl.beginRefreshing()
self.pictureObjects.removeAllObjects()
self.temporaryPictureObjects.removeAllObjects()
self.collectionView!.reloadData()
self.currentPage = 0
self.picturesCollectionView.bringSubviewToFront(cellLoadingIndicator)
self.populatingPhotos = false
populatePhotos()
}
}
Simply reload your collectionView after the data is formatted.
picturesCollectionView.reloadData()
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView!.registerClass(PhotoBrowserCollectionViewCell.self, forCellWithReuseIdentifier: PhotoBrowserCellIdentifier)
self.revealViewController().rearViewRevealWidth = 280
collectionView!.delegate = self
collectionView!.dataSource = self
customRefreshControl = UIRefreshControl()
customRefreshControl.backgroundColor = goldenWordsYellow
customRefreshControl.tintColor = UIColor.whiteColor()
self.picturesCollectionView!.addSubview(customRefreshControl)
navigationController?.setNavigationBarHidden(false, animated: true)
navigationItem.title = "Pictures"
setupView()
populatePhotos()
self.dateFormatter.dateFormat = "dd/MM/yy"
picturesCollectionView.reloadData()
}