I am using a pod called SpreadsheetView to show data, from a json, in a grid but I don't know how to show them because with this pod it is necessary to invoke an array for each column.
I would like to order this data from my json in the corresponding columns and also when touching a cell in a row I was taken to a view to show the related data.
Attached the code of what I have done.
The view that I use to display the data
import UIKit
import SpreadsheetView
class TiendasViewController: UIViewController, SpreadsheetViewDataSource, SpreadsheetViewDelegate, ConsultaModeloProtocol{
let headers = ["Sucursal", "Venta total", "Tickets", "Piezas", "Pzs/Ticket", "Ticket prom.", "Utilidad", "Última venta"]
var feedItems = [DetallesConsulta]()
func itemConsulta(LaConsulta: [DetallesConsulta]) {
feedItems = LaConsulta
self.tablaTiendas.reloadData()
}
var selectDato : DetallesConsulta = DetallesConsulta()
private let tablaTiendas = SpreadsheetView()
override func viewDidLoad() {
super.viewDidLoad()
let consultaModelo = ConsultaModelo()
consultaModelo.ElDelegado = self
consultaModelo.downloadConsulta()
tablaTiendas.dataSource = self
tablaTiendas.delegate = self
tablaTiendas.contentInset = UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0)
tablaTiendas.intercellSpacing = CGSize(width: 4, height: 1)
tablaTiendas.gridStyle = .none
tablaTiendas.gridStyle = .solid(width: 1, color: .blue)
tablaTiendas.register(SucursalesCell.self, forCellWithReuseIdentifier: String(describing: SucursalesCell.self))
tablaTiendas.register(DateCell.self, forCellWithReuseIdentifier: String(describing: DateCell.self))
view.addSubview(tablaTiendas)
print("Imprimiendo los feeditems: ", feedItems)
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tablaTiendas.frame = CGRect(x: 0, y:216, width: view.frame.size.width, height: view.frame.size.height-100)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tablaTiendas.flashScrollIndicators()
}
func spreadsheetView(_ spreadsheetView: SpreadsheetView, cellForItemAt indexPath: IndexPath) -> Cell? {
if case (0...(headers.count), 0) = (indexPath.column, indexPath.row) {
let cell = spreadsheetView.dequeueReusableCell(withReuseIdentifier: String(describing: DateCell.self), for: indexPath) as! DateCell
cell.label.text = headers[indexPath.column - 0]
return cell
} else if case(0, 1...(sucursales.count + 1)) = (indexPath.column, indexPath.row){
let cell = spreadsheetView.dequeueReusableCell(withReuseIdentifier: String(describing: SucursalesCell.self), for: indexPath) as! SucursalesCell
cell.label.text = sucursales[indexPath.row - 1]
return cell
}
/*
let cell = tablaTiendas.dequeueReusableCell(withReuseIdentifier: MyLabelCell.identifier, for: indexPath) as! MyLabelCell
if indexPath.row == 0 {
cell.setup(with: headers[indexPath.column])
cell.backgroundColor = .systemBlue
}
return cell
*/
return nil
}
func numberOfColumns(in spreadsheetView: SpreadsheetView) -> Int {
return headers.count
}
func numberOfRows(in spreadsheetView: SpreadsheetView) -> Int {
return 1 + sucursales.count
}
func spreadsheetView(_ spreadsheetView: SpreadsheetView, widthForColumn column: Int) -> CGFloat {
return 200
}
func spreadsheetView(_ spreadsheetView: SpreadsheetView, heightForRow row: Int) -> CGFloat {
if case 0 = row{
return 24
}else{
return 55
}
}
func frozenColumns(in spreadsheetView: SpreadsheetView) -> Int {
return 1
}
}
class MyLabelCell: Cell {
private let label = UILabel()
public func setup(with text: String){
label.text = text
label.textAlignment = .center
contentView.addSubview(label)
}
override func layoutSubviews() {
super.layoutSubviews()
label.frame = contentView.bounds
}
}
class DateCell: Cell {
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
label.frame = bounds
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
label.font = UIFont.boldSystemFont(ofSize: 15)
label.textAlignment = .center
contentView.addSubview(label)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
class SucursalesCell: Cell {
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
label.frame = bounds
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
label.font = UIFont.monospacedDigitSystemFont(ofSize: 12, weight: UIFont.Weight.medium)
label.textAlignment = .center
contentView.addSubview(label)
}
override var frame: CGRect {
didSet {
label.frame = bounds.insetBy(dx: 6, dy: 0)
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
The model to download the json
import UIKit
import Foundation
protocol ConsultaModeloProtocol: AnyObject {
func itemConsulta (LaConsulta: [DetallesConsulta])
}
var fechaPresente: String = ""
var fechaPasada: String = ""
let elToken : String = UserDefaults.standard.string(forKey: "token")!
let helper = Helper()
class ConsultaModelo: NSObject {
weak var ElDelegado : ConsultaModeloProtocol!
let URLPath = helper.host+"tiendas"
func downloadConsulta(){
var request = URLRequest(url: URL(string: URLPath)!)
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("Bearer \(elToken)", forHTTPHeaderField: "Authorization")
request.httpMethod = "POST"
let SessionDefault = Foundation.URLSession(configuration: URLSessionConfiguration.ephemeral)
URLCache.shared.removeAllCachedResponses()
let task = SessionDefault.dataTask(with: request){
(data, response, error)in
if error != nil {
print("Error al descargar la consulta")
}else{
print("Datos descargados")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data: Data){
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! NSArray
}catch let error as NSError{
print(error)
}
var jsonElement = NSDictionary()
var detalles = [DetallesConsulta]()
for i in 0 ..< jsonResult.count{
jsonElement = jsonResult[i] as! NSDictionary
let detalle = DetallesConsulta()
let Fecha = jsonElement["Fecha"]
let Sucursal = jsonElement["Sucursal"]
let Suc = jsonElement["Suc"]
let VentaTotal = jsonElement["Venta_Total"]
let NoFolios = jsonElement["N_Folios"]
let Piezas = jsonElement["Piezas"]
let PzaxTicket = jsonElement["PzaxTicket"]
let TicketPromedio = jsonElement["TicketPromedio"]
detalle.Fecha = Fecha as? String
detalle.Sucursal = Sucursal as? String
detalle.Suc = Suc as? String
detalle.VentaTotal = VentaTotal as? String
detalle.NoFolios = NoFolios as? Int
detalle.Piezas = Piezas as? String
detalle.PzaxTicket = PzaxTicket as? String
detalle.TicketPromedio = TicketPromedio as? String
detalles.append(detalle)
}
DispatchQueue.main.async(execute: { ()-> Void in
self.ElDelegado.itemConsulta(LaConsulta: detalles)
})
}
}
The details
import UIKit
class DetallesConsulta: NSObject {
var Fecha: String?
var Sucursal: String?
var Suc: String?
var VentaTotal: String?
var NoFolios: Int?
var Piezas: String?
var PzaxTicket: String?
var TicketPromedio: String?
override init(){
}
init(Fecha: String, Sucursal: String, Suc: String, VentaTotal: String, NoFolios: Int, Piezas: String, PzaxTicket: String, TicketPromedio: String){
self.Fecha = Fecha
self.Sucursal = Sucursal
self.Suc = Suc
self.VentaTotal = VentaTotal
self.NoFolios = Int(NoFolios)
self.Piezas = Piezas
self.PzaxTicket = PzaxTicket
self.TicketPromedio = TicketPromedio
}
override var description: String{
return "Fecha: \(Fecha), Sucursal: \(Sucursal), Suc: \(Suc), VentaTotal: \(VentaTotal), NoFolios: \(NoFolios), Piezas: \(Piezas), PzaxTicket: \(PzaxTicket), TicketPromedio: \(TicketPromedio)"
}
}
JSON Response
[
{
"Fecha": "2022-11-17",
"Sucursal": "SCALPERS PUEBLA",
"Suc": "004",
"Venta_Total": "xxxxxx.xxxxxxxxx",
"N_Folios": 12,
"Piezas": "xx.000",
"PzaxTicket": "x.x",
"TicketPromedio": "xxxx.x"
},
{
"Fecha": "2022-11-17",
"Sucursal": "SCALPERS SATELITE",
"Suc": "005",
"Venta_Total": "xxxxx.xxx",
"N_Folios": xx,
"Piezas": "xx.000",
"PzaxTicket": "x.x",
"TicketPromedio": "xxxxx.xxxx"
},
{
"Fecha": "2022-11-17",
"Sucursal": "SCALPERS OUTLET QUERETARO",
"Suc": "006",
"Venta_Total": "xxx.xxxxxxxxxx",
"N_Folios": 4,
"Piezas": "6.000",
"PzaxTicket": "1.5",
"TicketPromedio": "1419.5"
},
{
"Fecha": "2022-11-17",
"Sucursal": "SCALPERS ONLINE",
"Suc": "xxxx",
"Venta_Total": "xxxxx.xxxxxxx",
"N_Folios": 15,
"Piezas": "45.000",
"PzaxTicket": "3.0",
"TicketPromedio": "1930.5"
}
]
I hope you can help me.
Thank you.
I downloaded the data from the json and would like to have it displayed in a grid table with this pod.
Your code is too complicated and too objective-c-ish. Never use NSArray/NSDictionary in Swift (except in a few rare CoreFoundation APIs)
To decode the data use Decodable. This is just a small part of the JSON as your class and the hard-coded data are quite different
let jsonString = """
[{"Sucursal":"Hamleys", "Venta_Total":"$1", "Piezas":"10"},
{"Sucursal":"Bobby Brown", "Venta_Total":"$1", "Piezas":"20"}]
"""
Create a struct conforming to Decodable and add CodingKeys to get rid of the capitalized keys. Maybe it's even possible with the .convertFromSnakeCase strategy
struct DetallesConsulta: Decodable {
var sucursal: String
var ventaTotal: String
var piezas: String
private enum CodingKeys: String, CodingKey {
case sucursal = "Sucursal", ventaTotal = "Venta_Total", piezas = "Piezas"
}
}
Then decode the data and to get arrays of the properties map the data.
do {
let result = try JSONDecoder().decode([DetallesConsulta].self, from: Data(jsonString.utf8))
let sucursales = result.map(\.sucursal)
let vtaTotal = result.map(\.ventaTotal)
let piezas = result.map(\.piezas)
print(sucursales, vtaTotal, piezas)
} catch {
print(error)
}
Related
I have ViewModel for getting data from API. I want to pass this data to my UICollectionViewCell and show it on my ViewController but I don't know how.
I'm trying to delete extra information and leave useful information in code below:
My ViewModel:
class DayWeatherViewModel {
let url = "https://api.openweathermap.org/data/2.5/weather?q=London&appid=APIKEY"
func viewDidLoad() {
getData(from: url)
}
func getData(from url: String) {
guard let url = URL(string: url) else {
print("Failed to parse URL")
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print("something went wrong")
return
}
var result: CitiesWeather?
do {
result = try JSONDecoder().decode(CitiesWeather.self, from: data)
self.weatherDidChange?(result!)
}
catch {
print("failed to convert \(error)")
}
guard let json = result else {
return
}
print(json.coord?.latitude)
print(json.coord?.longitude)
print(json.weather)
print(json.wind?.speed)
}
task.resume()
}
var weatherDidChange: ((CitiesWeather) -> Void)?
}
My UICollectionViewCell:
class DayWeatherCell: UICollectionViewCell, UIScrollViewDelegate {
struct Model {
let mainTemperatureLabel: Double
}
var mainTemperatureLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "Rubik-Medium", size: 36)
label.text = "10"
label.textColor = .white
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
func configure(with model: Model) {
mainTemperatureLabel.text = String(model.mainTemperatureLabel)
}
My ViewController:
class MainScrenenViewController: UIViewController {
let viewModel: DayWeatherViewModel
private var main: Double? {
didSet {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.register(DayWeatherCell.self, forCellWithReuseIdentifier: "sliderCell")
collectionView.layer.cornerRadius = 5
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.backgroundColor = UIColor(red: 0.125, green: 0.306, blue: 0.78, alpha: 1)
return collectionView
}()
init(viewModel: DayWeatherViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.dataSource = self
collectionView.delegate = self
setupConstraints()
viewModel.weatherDidChange = { result in
self.main = result.main?.temp
}
viewModel.viewDidLoad()
}
extension MainScrenenViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "sliderCell", for: indexPath) as! DayWeatherCell
if let myd: String? = String(main ?? 1.1) {
cell.mainTemperatureLabel.text = myd
}
return cell
}
}
extension MainScrenenViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
}
}
My Struct for parse JSON:
struct CitiesWeather: Decodable {
let coord : Coordinate?
let cod, visibility, id : Int?
let name : String?
let base : String?
let weather: [Weather]?
let sys: Sys?
let main: Main?
let wind: Wind?
let clouds: Clouds?
let dt: Date?
var timezone: Int?
}
struct Coordinate: Decodable {
var longitude: Double?
var latitude: Double?
}
struct Weather: Decodable {
let id: Int?
let main: MainEnum?
let description: String?
let icon: String?
}
struct Sys : Decodable {
let type, id : Int?
let sunrise, sunset : Date?
let message : Double?
let country : String?
}
struct Main : Decodable {
let temp, tempMin, tempMax : Double?
let pressure, humidity : Int?
}
struct Wind : Decodable {
let speed : Double?
let deg : Int?
}
struct Clouds: Decodable {
let all: Int?
}
enum MainEnum: String, Decodable {
case clear = "Clear"
case clouds = "Clouds"
case rain = "Rain"
}
I've been practicing networking with swift 5, but I seem to always get stuck on parsing json to the UITableViewCells. I've looked at many tutorials, but can't seem to figure out the problem.
What I'm looking to do is retrieve the name of the country and place it in the tableview cells.
I use this url to request json: https://restcountries.eu/rest/v2/
The view models:
struct Countries: Codable {
let name: String?
let capital: String?
let region: String?
let population: String?
let currencies: [Currency]?
let languages: [Language]?
let flag: URL?
}
struct Currency: Codable {
let code: String?
let name: String?
let symbol: String?
}
struct Language: Codable {
let name: String?
}
My API request:
class Service {
static let shared = Service()
func fetchCountriesData(completed: #escaping (Result<Countries, ErrorMessages>) -> Void) {
let urlString = "https://restcountries.eu/rest/v2/"
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
// error
if let _ = error {
completed(.failure(.invalidData))
return
}
// response
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
// data
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
//decoder.keyDecodingStrategy = .convertFromSnakeCase
let results = try decoder.decode(Countries.self, from: data)
completed(.success(results))
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
}
}
My MainViewController:
class MainViewController: UITableViewController {
var cellId = "cellId"
var countryData = [Countries]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
style()
tableView.delegate = self
tableView.dataSource = self
}
func style() {
navigationItem.title = "Search country information"
tableView.register(CountryViewCell.self, forCellReuseIdentifier: cellId)
//tableView.separatorStyle = .none
}
}
extension MainViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? CountryViewCell else {
return UITableViewCell()
}
let results = countryData[indexPath.row].name
cell.titleLabel.text = results
return cell
}
}
And the custom TableViewCell:
class CountryViewCell: UITableViewCell {
private var countryResults: Countries? {
didSet {
self.titleLabel.text = countryResults?.name
}
}
var titleLabel: UILabel = {
let label = UILabel()
label.text = "countries"
label.font = .boldSystemFont(ofSize: 16)
label.numberOfLines = 0
label.textColor = .black
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
configure()
layout()
//displayResults()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension CountryViewCell {
func configure() {
selectionStyle = .gray
titleLabel.translatesAutoresizingMaskIntoConstraints = false
}
func layout() {
addSubview(titleLabel)
NSLayoutConstraint.activate([
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
titleLabel.heightAnchor.constraint(equalToConstant: 20),
titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
}
I have a function called :'fetchPosts' that fetches data from a parse server into a UicollectionViewController, When i add this function to any other methods like view did appear or make a button to update the view i get '
terminating with uncaught exception of type NSException
' Not sure what's the problem here.
import UIKit
import AVKit
import AVFoundation
import CoreLocation
import Parse
class NewsfeedCollectionViewController : UICollectionViewController, CLLocationManagerDelegate
{
var searchController: UISearchController!
var posts: [Post]?
override func viewDidLoad() {
super.viewDidLoad()
if let user = PFUser.current() {
PFGeoPoint.geoPointForCurrentLocation { (geopoint, error) in
if geopoint != nil {
let geo = geopoint
user["location"] = geo
user.saveInBackground()
}
}
}
self.fetchPosts()
collectionView?.contentInset = UIEdgeInsets(top: 12, left: 4, bottom: 12, right: 4)
if let layout = collectionView?.collectionViewLayout as? PinterestLayout {
layout.delegate = self
}
}
func fetchPosts()
{
DispatchQueue.global(qos: .userInteractive).async {
self.posts = Post.fetchPosts(viewController: self)
DispatchQueue.main.async {
self.collectionView?.reloadData()
}
}
}
#IBAction func reloadbtnPressed(_ sender: Any) {
fetchPosts()
}
}
extension NewsfeedCollectionViewController
{
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let posts = posts {
return posts.count
} else {
return 0
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PostCell", for: indexPath) as! PostCollectionViewCell
cell.post = self.posts?[indexPath.item]
return cell
}
}
extension NewsfeedCollectionViewController : PinterestLayoutDelegate
{
func collectionView(collectionView: UICollectionView, heightForPhotoAt indexPath: IndexPath, with width: CGFloat) -> CGFloat
{
if let post = posts?[indexPath.item], let _ = post.image {
let boundingRect = CGRect(x: 0, y: 0, width: width, height: CGFloat(MAXFLOAT))
// do {
// try postImageView.image = UIImage(data: image!.getData())
// }
// catch {
//
// }
do {
var image: UIImage
try image = UIImage(data: post.image!.getData())!
let rect = AVMakeRect(aspectRatio: image.size, insideRect: boundingRect)
return rect.size.height
}
catch {
return 1;
}
}
return 0
}
func collectionView(collectionView: UICollectionView, heightForCaptionAt indexPath: IndexPath, with width: CGFloat) -> CGFloat
{
if let post = posts?[indexPath.item] {
let topPadding = CGFloat(8)
let bottomPadding = CGFloat(12)
let captionFont = UIFont.systemFont(ofSize: 15)
let captionHeight = self.height(for: post.caption!, with: captionFont, width: width)
let profileImageHeight = CGFloat(36)
let height = topPadding + captionHeight + topPadding + profileImageHeight + bottomPadding
return height
}
return 0.0
}
func height(for text: String, with font: UIFont, width: CGFloat) -> CGFloat
{
let nsstring = NSString(string: text)
let maxHeight = CGFloat(64.0)
let textAttributes = [NSFontAttributeName : font]
let boundingRect = nsstring.boundingRect(with: CGSize(width: width, height: maxHeight), options: .usesLineFragmentOrigin, attributes: textAttributes, context: nil)
return ceil(boundingRect.height)
}
}
extension NewsfeedCollectionViewController : PostProtocol
{
func didFinishFetch(posts: [Post]?)
{
if (posts != nil) {
self.posts = posts
}
self.collectionView?.reloadData()
}
}
ANd here is the Model Post File that i use to fetch data from server :
import UIKit
import Parse
protocol PostProtocol: class {
func didFinishFetch(posts: [Post]?)
}
struct Post
{
var ID: String
var createdBy: User
var timeAgo: String?
var caption: String?
// var image: UIImage?
var image: PFFile?
var numberOfLikes: Int?
var numberOfComments: Int?
var numberOfShares: Int?
static weak var delegate: PostProtocol?
static func fetchPosts(viewController: PostProtocol) -> [Post]
{
var posts = [Post]()
delegate = viewController
let query = PFQuery(className: "posts");
if let latitude = (PFUser.current()?["location"] as AnyObject).latitude {
if let longitude = (PFUser.current()?["location"] as AnyObject).longitude {
let geoPoint = PFGeoPoint(latitude: latitude, longitude: longitude)
query.whereKey("postLocation", nearGeoPoint: geoPoint, withinKilometers: 1)
}
}
query.findObjectsInBackground { (objects: [PFObject]?, error: Error?) in
if error == nil {
for object in objects! {
let pfObject = object as PFObject
let ID = pfObject["uuid"] as! String
let caption = pfObject["title"] as! String
let likes = pfObject["likes"] as? Int
let comments = pfObject["comments"] as? Int
let shares = pfObject["shares"] as? Int
let image = pfObject["pic"] as? PFFile
let profileImage = pfObject["ava"] as? PFFile
let username = pfObject["username"] as? String
let user = User(username: username, profileImage: profileImage);
let from = pfObject.createdAt
let now = Date()
let components : NSCalendar.Unit = [.second, .minute, .hour, .day, .weekOfMonth]
let difference = (Calendar.current as NSCalendar).components(components, from: from! as Date, to: now, options: [])
var timeAgo: String = ""
// logic what to show: seconds, minuts, hours, days or weeks
if difference.second! <= 0 {
timeAgo = "now"
}
if difference.second! > 0 && difference.minute! == 0 {
timeAgo = "\(difference.second!)s."
}
if difference.minute! > 0 && difference.hour! == 0 {
timeAgo = "\(difference.minute!)m."
}
if difference.hour! > 0 && difference.day! == 0 {
timeAgo = "\(difference.hour!)h."
}
if difference.day! > 0 && difference.weekOfMonth! == 0 {
timeAgo = "\(difference.day!)d."
}
if difference.weekOfMonth! > 0 {
timeAgo = "\(difference.weekOfMonth!)w."
}
let post = Post(ID: ID, createdBy: user, timeAgo: timeAgo, caption: caption, image: image, numberOfLikes: likes, numberOfComments: comments, numberOfShares: shares)
posts.append(post);
}
delegate?.didFinishFetch(posts: posts)
}
}
return posts
}
}
struct User
{
var username: String?
var profileImage: PFFile?
}
I am having the problem in refreshing my JSON data through pull to refresh.When I launch my application, then it is displaying the data but when I pull to refresh then it is not refreshing.
I have implemented the Refresh Control to refresh data. I am able to see the wheel icon of the pull to refresh, but it's not updating the JSON data.
Here is my code:
struct JSONData {
let country: String
let indicename: String
let currPrice: String
let chg: String
let perChg: String
init?(dictionary: [String:Any]) {
guard let country = dictionary["Country"] as? String,
let indicename = dictionary["Indicename"] as? String,
let currPrice = dictionary["CurrPrice"] as? String,
let chg = dictionary["Chg"] as? String,
let perChg = dictionary["perChg"] as? String else {
return nil
}
self.country = country
self.indicename = indicename
self.currPrice = currPrice
self.chg = chg
self.perChg = perChg
}
}
class SouthAViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,IndicatorInfoProvider {
var datas = [JSONData]()
var refreshControl = UIRefreshControl()
#IBOutlet var tableview: UITableView!
var arrowupimage : UIImage = UIImage(named : "arrowup")!
var arrowdownimage : UIImage = UIImage(named : "arrowdown")!
// I haven't shared the URL of parsing for security reasons
let url=NSURL(string:"*******************************")
let stringurl = "***************************"
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 10.0, *) {
tableview.refreshControl = refreshControl
} else {
tableview.addSubview(refreshControl)
}
self.refreshControl.attributedTitle = NSAttributedString(string: "Pull to refresh")
self.refreshControl.addTarget(self, action: #selector(SouthAViewController.refresh), for: UIControlEvents.valueChanged)
navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
self.downloadJsonWithURL()
tableview.delegate = self
tableview.dataSource = self
}
func refresh(){
self.downloadJsonWithURL()
}
// downloading JSON data to display on this class
func downloadJsonWithURL() {
let task = URLSession.shared.dataTask(with: URL(string: stringurl)!) { (data, response, error) in
if error != nil {
// print(error?.localizedDescription)
return
}
if let contdata = (try? JSONSerialization.jsonObject(with: data!, options: [])) as? [String:Any] {
if let arrJSON = contdata["data"] as? [[String:Any]] {
self.datas = arrJSON.flatMap(JSONData.init)
//Reload tableView and endRefreshing the refresh control
DispatchQueue.main.async {
self.tableview.reloadData()
self.refreshControl.endRefreshing()
}
}
}
}
task.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return datas.count
}
//setting data on the table view cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "southacell", for: indexPath) as! southacell
cell.Indicename?.text = datas[indexPath.row].indicename
cell.Country?.text = datas[indexPath.row].country
cell.CurrPrice?.text = datas[indexPath.row].currPrice
return cell
}
}
json sample :
{ "data":[
{
"Country":"China",
"Indicename":"SZSE COMPONENT INDEX",
"date":"2017-06-23 14:53:57",
"zone":"GMT+8",
"CurrPrice":"10355.3",
"Chg":"90.07",
"PerChg":"0.88",
"prev_close":"10265.2"
},
{
"Country":"China",
"Indicename":"Shanghai Composite",
"date":"2017-06-23 14:52:54",
"zone":"GMT+8",
"CurrPrice":"3155.9",
"Chg":"8.44",
"PerChg":"0.27",
"prev_close":"3147.45"
}
]
}
Don't use Data(contentsOf:) to retrieve data from URL what you need to use is URLSession with datatask to retrieve data from URL after that you need to reload the tableView and ending animation of RefreshControl inside the completion block of datatask(with:). Also instead of handling multiple array what you need to do is create one array of custom class or struct
struct Data {
var country: String
var indicename: String
var currPrice: String
var chg: String
var perChg: String
init?(dictionary: [String:Any]) {
guard let country = dictionary["Country"] as? String,
let indicename = dictionary["Indicename"] as? String,
let currPrice = dictionary["CurrPrice"] as? String,
let chg = dictionary["Chg"] as? String,
let perChg = dictionary["PerChg"] as? String else {
return nil
}
self.country = country
self.indicename = indicename
self.currPrice = currPrice
self.chg = chg
self.perChg = perChg
}
}
Now declare simply one array of type [Data] and use this will your tableView methods.
var datas = [Data]()
Now simply use this single array to store all the data and display data in tableView.
func refresh(){
self.downloadJsonWithURL()
}
func downloadJsonWithURL() {
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error?.localizedDescription)
return
}
if let contdata = (try? JSONSerialization.jsonObject(with: data!, options: [])) as? [String:Any] {
if let arrJSON = contdata["data"] as? [[String:Any]] {
self.datas = arrJSON.flatMap(Data.init)
//Reload tableView and endRefreshing the refresh control
DispatchQueue.main.async {
self.tableView.reloadData()
self.refreshControl.endRefreshing()
}
}
}
}
task.resume()
}
//tableView methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
//setting data on the table view cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "southacell", for: indexPath) as! southacell
let curpricefloat : Double = Double(datas[indexPath.row].currPrice)!
let Chgfloat : Double = Double(datas[indexPath.row].chg)!
let perchgfloat : Double = Double(datas[indexPath.row].perChg)!
cell.Indicename?.text = datas[indexPath.row].indicename
cell.Indicename.font = UIFont.boldSystemFont(ofSize: 19.0)
cell.Country?.text = datas[indexPath.row].country
cell.PerChg?.text = "(" + String(format: "%.2f", perchgfloat) + "%)"
cell.CurrPrice?.text = String(format: "%.2f", curpricefloat)
cell.Chg?.text = String(format: "%.2f", Chgfloat)
if Float((cell.Chg?.text)!)! < 0 {
datas[indexPath.row].chg = datas[indexPath.row].chg.replacingOccurrences(of: "-", with: "")
datas[indexPath.row].perChg = datas[indexPath.row].perChg.replacingOccurrences(of: "-", with: "")
cell.PerChg?.text = "(" + datas[indexPath.row].perChg + "%)"
cell.Chg?.text = datas[indexPath.row].chg
cell.Chg?.textColor = UIColor.red
cell.PerChg?.textColor = UIColor.red
cell.arrow?.image = UIImage(named : "arrowdown")
} else
{
cell.Chg?.textColor = UIColor.green
cell.PerChg?.textColor = UIColor.green
cell.arrow?.image = UIImage(named : "arrowup")
}
if cell.Country!.text! == "Argentina"{
cell.countryFlag?.image = UIImage(named : "argentina")
}
else if cell.Country!.text! == "Brazil"{
cell.countryFlag?.image = UIImage(named : "brazil")
}
else if cell.Country!.text! == "Peru"{
cell.countryFlag?.image = UIImage(named : "peru")
}
else{
cell.countryFlag?.image = UIImage(named : "unknown")
}
return cell
}
Try the following:
override func viewDidLoad() {
super.viewDidLoad()
// MARK: Refresh control
updateData.backgroundColor = .black
updateData.tintColor = .white
updateData.attributedTitle = NSAttributedString(string: "Updating Tap
List...", attributes: [NSForegroundColorAttributeName: UIColor(red:
255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 1.0)])
updateData.addTarget(self, action:
#selector(ViewController.loadNewData), for: UIControlEvents.valueChanged)
tapListTableView.addSubview(updateData)
tapListTableView.sendSubview(toBack: updateData)
DispatchQueue.main.async {
self.tableview.reloadData()
}
}
}
This project also click on the textfield pickerview opens, but the data shown above can be entered in the section. How can I turn it off? So just come to my choices below.
If the textfield is clicked, I open the pickerview:
import UIKit
import NVActivityIndicatorView
struct kategoriData {
var text : String?
var id : String?
}
class KonuEkleViewController: UIViewController,UITextFieldDelegate , NVActivityIndicatorViewable , UIPickerViewDataSource , UIPickerViewDelegate{
#IBOutlet weak var txtBaslik: UITextField!
#IBOutlet weak var txtYazi: UITextView!
#IBOutlet weak var txtLink: UITextField!
#IBOutlet weak var btnKonuAc: UIButton!
#IBOutlet weak var txtKategoriSecimi: UITextField!
let size = CGSize(width: 30, height: 30)
let singleton = Global.sharedGlobal
let ConnectString = Connect.ConnectInfo
var kategoriList = [kategoriData]()
let GirisView = GirisViewController()
var secilenKategori : String?
var dbKategoriIsim : String?
var dbKategoriId:String?
override func viewDidLoad() {
super.viewDidLoad()
txtKategoriSecimi.text = "Kategoriyi Seçiniz"
KategoriGetir()
pickerGrafik()
txtYazi.layer.cornerRadius = 10
btnKonuAc.layer.cornerRadius = 10
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(GirisViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
#IBAction func btnKonuEkle(_ sender: Any) {
GirisView.LoadingIcon(tur: 0)
if txtBaslik.text != "" && txtYazi.text != "" && secilenKategori != nil && txtKategoriSecimi.text != "Kategoriyi Seçiniz"
{
KonuEkle(uye: singleton.username, baslik: txtBaslik.text!, mesaj: txtYazi.text, kategori: secilenKategori!, link: txtLink.text!)
}
else
{
GirisView.Bildiri(baslik: "UYARI", mesaj: "Boş Alanları Doldurunuz", tur: 1)
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0) {
self.GirisView.LoadingIcon(tur: 1)
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func KonuEkle(uye:String , baslik:String ,mesaj:String , kategori:String , link:String)
{
var request = URLRequest(url: URL(string:ConnectString.conString + "/KonuEkle")!)
request.httpMethod = "POST"
var postString = "uye="+uye+"&&baslik="+baslik+"&&mesaj="+mesaj+"&&kategori="+kategori+"&&link="+link
postString = postString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil
{
print("error")
}
if let urlContent = data
{
do
{
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(jsonResult)
if let gelenDizi = jsonResult as? NSArray
{
for i in 0..<gelenDizi.count
{
if let gelenMesaj = (gelenDizi[i] as? NSDictionary)?["mesaj"] as? String
{
DispatchQueue.main.async {
self.Sonuc(mesaj:gelenMesaj)
}
}
}
}
}
catch
{
print("server hatası")
}
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0) {
self.GirisView.LoadingIcon(tur: 1)
}
}
task.resume()
}
func Sonuc(mesaj:String)
{
var mesajSonuc = ""
var mesajBaslik = ""
if mesaj == "basarili"
{
mesajSonuc = "Konu Açıldı"
mesajBaslik = "Bilgi"
}
else
{
mesajSonuc = "Bir Hata Oluştu Lütfen Destek Bölümünden Bize Ulaşın"
mesajBaslik = "Uyarı"
}
let appearance = SCLAlertView.SCLAppearance(showCloseButton: false)
let alert = SCLAlertView(appearance: appearance)
_ = alert.addButton("Tamam") {
self.txtLink.text = ""
self.txtYazi.text = ""
self.txtBaslik.text = ""
self.navigationController!.popViewController(animated: true)
}
if mesajBaslik == "Bilgi"
{
_ = alert.showSuccess(mesajBaslik, subTitle:mesajSonuc)
}
else
{
_ = alert.showNotice(mesajBaslik, subTitle:mesajSonuc)
}
}
func pickerGrafik()
{
let pickerView = UIPickerView()
pickerView.delegate = self
txtKategoriSecimi.inputView = pickerView
let toolBar = UIToolbar(frame: CGRect(x: 0, y: self.view.frame.size.height/6, width: self.view.frame.size.width, height: 40.0))
toolBar.layer.position = CGPoint(x: self.view.frame.size.width/2, y: self.view.frame.size.height-20.0)
toolBar.barStyle = UIBarStyle.blackTranslucent
toolBar.tintColor = UIColor.white
toolBar.backgroundColor = UIColor.black
let defaultButton = UIBarButtonItem(title: "İptal", style: UIBarButtonItemStyle.plain, target: self, action: #selector(KonuEkleViewController.tappedToolBarBtn))
let doneButton = UIBarButtonItem(title: "Tamam", style: UIBarButtonItemStyle.done, target: self, action: #selector(KonuEkleViewController.donePressed))
let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: self, action: nil)
let label = UILabel(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width / 3, height: self.view.frame.size.height))
label.font = UIFont(name: "Helvetica", size: 12)
label.backgroundColor = UIColor.clear
label.textColor = UIColor.white
label.text = "Kategoriyi Seçiniz"
label.textAlignment = NSTextAlignment.center
let textBtn = UIBarButtonItem(customView: label)
toolBar.setItems([defaultButton,flexSpace,textBtn,flexSpace,doneButton], animated: true)
txtKategoriSecimi.inputAccessoryView = toolBar
}
func donePressed(_ sender: UIBarButtonItem) {
txtKategoriSecimi.resignFirstResponder()
}
func tappedToolBarBtn(_ sender: UIBarButtonItem) {
txtKategoriSecimi.resignFirstResponder()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return kategoriList.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return kategoriList[row].text
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
txtKategoriSecimi.text = kategoriList[row].text
secilenKategori = kategoriList[row].id
}
func KategoriGetir()
{
kategoriList.removeAll()
//SADECE 1 DEFA EKLENIYOR
self.kategoriList.append(kategoriData.init(text: "Kategoriyi Seçiniz", id: "1"))
GirisView.LoadingIcon(tur: 0)
var request = URLRequest(url: URL(string:ConnectString.conString + "/KategoriGetir")!)
request.httpMethod = "POST"
var postString = "tur=1&&kategori="
postString = postString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil
{
print("error")
}
if let urlContent = data
{
do
{
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(jsonResult)
if let gelenDizi = jsonResult as? NSArray
{
for i in 0..<gelenDizi.count
{
if let gelenKategori = (gelenDizi[i] as? NSDictionary)?["kategoriisim"] as? String
{
self.dbKategoriIsim = gelenKategori
}
if let gelenId = (gelenDizi[i] as? NSDictionary)?["id"] as? String
{
self.dbKategoriId = gelenId
}
self.kategoriList.append(kategoriData.init(text: self.dbKategoriIsim, id: self.dbKategoriId))
}
}
}
catch
{
print("error")
}
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
self.GirisView.LoadingIcon(tur: 1)
}
}
task.resume()
}
}
You can implement the UITextFieldDelegate method:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
To return false so that entering any character by hand would be prevented.
This isn't an actual problem when running on a real iPhone device, since no keyboard will be available to do the typing :)
This is only seems like an issue when running on the iOS simulator, since you can then use your Mac keyboard as well.
Having said, you definitely should go with Mr. Hedgehog answer if you still want to block text input to your field.
If, besides blocking input, you may also want to hide the caret on the text field, also try this:
class PickerBasedTextField: UITextField {
override func caretRect(for position: UITextPosition) -> CGRect { .zero }
}
If you want to prevent user from using paste action, you can make a subclass of UITextField to override canPerformAction.
public class PickerUITextField: UITextField {
override public func canPerformAction(_ action: Selector, withSender
sender: Any?) -> Bool {
if action == #selector(UIResponderStandardEditActions.paste(_:)) {
return false
}
return super.canPerformAction(action, withSender: sender)
}
}
Here is a doc for list of actions.