i am new in swift.
i have make a collectionview in nib file and i have a subview of that in main view controller.
i want show array in collectionview but i could not.
first i make a model of day:
struct Days {
let day: String
let Image: String
let temp: Double
}
then in daycell:
class DayCell: UICollectionViewCell {
#IBOutlet weak var lblDay: UILabel!
#IBOutlet weak var imgWeather: KBImageView!
#IBOutlet weak var lblTemp: UILabel!
func updateViews(day: Days) {
lblDay.text = day.day
imgWeather.setImageWithKingFisher(url: day.Image)
lblTemp.text = String(day.temp)
}
}
then in public class, i get json data with alamofire and decode that and put them in my model:
public class Publics {
static let instance = Publics()
func showInfo(code: String, completion: #escaping ([Days]) -> Void) {
let DaysUrl = "http://api.openweathermap.org/data/2.5/forecast?id=\(code)&appid=3e28385cde03f6ee26c83b629ca274cc"
Alamofire.request(DaysUrl, method: .get, parameters: nil, encoding: URLEncoding.httpBody).responseJSON { response in
if let data = response.data {
do {
self.myJson = try JSONDecoder().decode(JsonForecast.Response.self, from: data)
let counter = (self.myJson?.list.count)! - 1
let myDay1 = self.myJson?.list[counter-32]
let myDay2 = self.myJson?.list[counter-24]
let myDay3 = self.myJson?.list[counter-16]
let weekDay1 = self.getDate(date: self.getDayOfWeek((myDay1?.dt_txt)!)!)
let weekDay2 = self.getDate(date: self.getDayOfWeek((myDay2?.dt_txt)!)!)
let weekDay3 = self.getDate(date: self.getDayOfWeek((myDay3?.dt_txt)!)!)
let DaysArray = [
Days(day: weekDay1, Image: (myDay1?.weather[0].icon)!, temp: (myDay1?.main?.temp)!) ,
Days(day: weekDay2, Image: (myDay2?.weather[0].icon)!, temp: (myDay2?.main?.temp)!) ,
Days(day: weekDay3, Image: (myDay3?.weather[0].icon)!, temp: (myDay3?.main?.temp)!)
]
completion(DaysArray)
} catch {
print(error)
}
}
}
}
till here i do not have problem but now i want to show DaysArray in collectionview but i can not and my collectionview class is below:
class DayCollection: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet var contentView: UIView!
#IBOutlet weak var collectionDay: UICollectionView!
var days = [Days]()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DayCell", for: indexPath) as! DayCell
Publics.instance.showInfo(code: "112931") { result in
self.days = result
print(self.days)
DispatchQueue.main.async {
self.collectionDay.reloadData()
}
}
let day = days[indexPath.item]
cell.updateViews(day: day)
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return days.count
}
override func awakeFromNib() {
super.awakeFromNib()
self.collectionDay.dataSource = self
self.collectionDay.delegate = self
self.collectionDay.register(UINib(nibName: "DayCell", bundle: nil), forCellWithReuseIdentifier: "DayCell")
}
}
what should i do in mainVC class?
(maybe i should i use from protocol delegate or no?)
First of all if you want to have constants in a struct declare them as constants. private(set) is horrible.
struct Days {
let day: String
let dImage: String
let temp: Double
}
And never ever declare struct members as implicit unwrapped optionals which are initialized with non-optional values in an init method. The init method in a struct is not needed anyway.
You have to add a completion handler
public func showInfo(code: String, completion: #escaping ([Days]) -> Void) {
...
let daysArray = [
Days(day: weekDay1, Image: (myDay1?.weather[0].icon)!, temp: (myDay1?.main?.temp)!) ,
Days(day: weekDay2, Image: (myDay2?.weather[0].icon)!, temp: (myDay2?.main?.temp)!) ,
Days(day: weekDay3, Image: (myDay3?.weather[0].icon)!, temp: (myDay3?.main?.temp)!)
]
completion(daysArray)
}
Then in the class of the collection view add a data source array
var days = [Days]()
and get the data
Publics.instance.showInfo(code: "Foo") { result in
self.days = result
DispatchQueue.main.async {
self.collectionDay.reloadData()
}
}
and return days.count in numberOfItemsInSection
Further force unwrap the cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DayCell", for: indexPath) as! DayCell
If the code crashes you made a design mistake. With the optional binding the code doesn't crash but you don't see anything and you don't know why
and get a day
let day = days[indexPath.item]
cell.updateViews(day)
Related
I faced such problem. When I launch the ios application, I get a white screen and the data that I take from Firebase is not displayed. How can i fix this problem? I would be grateful for your favorite recommendations for solving my problem
This is my ViewController
class ViewController: UIViewController {
#IBOutlet weak var cv: UICollectionView!
var channel = [Channel]()
override func viewDidLoad() {
super.viewDidLoad()
self.cv.delegate = self
self.cv.dataSource = self
let db = Firestore.firestore()
db.collection("content").getDocuments() {( quarySnapshot, err) in
if let err = err {
print("error")
} else {
for document in quarySnapshot!.documents {
if let name = document.data()["title"] as? Channel {
self.channel.append(name)
}
if let subtitle = document.data()["subtitle"] as? Channel {
self.channel.append(subtitle)
}
}
self.cv.reloadData()
}
}
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return channel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ContentCell
let channel = channel[indexPath.row]
cell.setup(channel: channel)
return cell
}
}
This is my Model
struct Content {
let contents: [Channel]
}
struct Channel {
let title: String
let subtitle: String
}
This is my Cell
class ContentCell: UICollectionViewCell {
#IBOutlet weak var channelText: UILabel!
#IBOutlet weak var subtitle: UITextView!
func setup(channel: Channel) {
channelText.text = channel.title
subtitle.text = channel.subtitle
}
}
The data retrieved from Firestore can't just magically be cast to your custom type (Channel); it's a simple dictionary. You eighter need to use Codable or do it manually like so:
I can't tell how exactly to convert it as you have not shared the structure of your data in Firestore, but I assume this will work:
db.collection("content").getDocuments() { (snapshot, error) in
if let error = error {
print("error: \(error.localizedDescription)")
} else if let snapshot = snapshot {
for document in snapshot.documents {
let data = document.data()
if let title = data["title"] as? String,
let subtitle = data["subtitle"] as? String {
self.channel.append(Channel(title: title, subtitle: subtitle))
}
}
}
self.cv.reloadData()
}
I created four label inside the table view cell to fetch the data from Api . The link is https://coinmap.org/api/v1/venues/ . I have two swift file one is to define the tabelview property and other one is for Label with cell . When I run the app it displaying the tableview cell but not displaying the data with label . Here is the model .
// MARK: - Welcome
struct Coin: Codable {
let venues: [Venue]
}
// MARK: - Venue
struct Venue: Codable {
let id: Int
let lat, lon: Double
let category, name: String
let createdOn: Int
let geolocationDegrees: String
enum CodingKeys: String, CodingKey {
case id, lat, lon, category, name
case createdOn = "created_on"
case geolocationDegrees = "geolocation_degrees"
}
}
Here is the Network Manager .
class NetworkManager {
func getCoins(from url: String, completion: #escaping (Result<Coin, NetworkError>) -> Void ) {
guard let url = URL(string: url) else {
completion(.failure(.badURL))
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(.other(error)))
return
}
if let data = data {
//decode
do {
let response = try JSONDecoder().decode(Coin.self, from: data)
completion(.success(response))
} catch let error {
completion(.failure(.other(error)))
}
}
}
.resume()
}
}
Here is the presenter .
class VenuePresenter : VanueProtocol{
// creating instance of the class
private let view : VanueViewProtocol
private let networkManager: NetworkManager
private var vanues = [Venue]()
var rows: Int{
return vanues.count
}
// initilanize the class
init(view:VanueViewProtocol , networkmanager:NetworkManager = NetworkManager()){
self.view = view
self.networkManager = networkmanager
}
func getVanue(){
let url = "https://coinmap.org/api/v1/venues/"
networkManager.getCoins(from: url) { result in
switch result {
case.success(let respone):
self.vanues = respone.venues
DispatchQueue.main.async {
self.view.resfreshTableView()
}
case .failure(let error):
DispatchQueue.main.async {
self.view.displayError(error.errorDescription ?? "")
print(error)
}
}
}
}
func getId(by row: Int) -> Int {
return vanues[row].id
}
func getLat(by row: Int) -> Double {
return vanues[row].lat
}
func getCreated(by row: Int) -> Int {
return vanues[row].createdOn
}
func getLon(by row: Int) -> Double? {
return vanues[row].lon
}
}
Here is the view controller .
class ViewController: UIViewController{
private var presenter : VenuePresenter!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
setUpUI()
presenter = VenuePresenter(view: self)
presenter.getVanue()
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "DisplayView1")
// Do any additional setup after loading the view.
}
private func setUpUI() {
tableView.dataSource = self
tableView.delegate = self
}
}
extension ViewController : VanueViewProtocol{
func resfreshTableView() {
tableView.reloadData()
}
func displayError(_ message: String) {
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
let doneButton = UIAlertAction(title: "Done", style: .default, handler: nil)
alert.addAction(doneButton)
present(alert, animated: true, completion: nil)
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
presenter.rows
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: DisplayView.identifier, for: indexPath) as? DisplayView
else { return UITableViewCell() }
let row = indexPath.row
let id = presenter.getId(by: row)
let lat = presenter.getLat(by: row)
guard let lon = presenter.getLon(by: row) else { return UITableViewCell() }
let createdOn = presenter.getCreated(by: row)
cell.configureCell(id: id, lat: lat, lon: lon, createdOn: createdOn)
return cell
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
Here is the display view swift .
class DisplayView: UITableViewCell{
static let identifier = "DisplayView1"
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
#IBOutlet weak var label3: UILabel!
#IBOutlet weak var label4: UILabel!
func configureCell(id: Int ,lat : Double , lon : Double , createdOn: Int){
label1.text = String(id)
label2.text = String(lat)
label3.text = String(lon)
label4.text = String(createdOn)
}
}
Here is the screenshot is empty not displaying the data .
You are registering a generic cell:
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "DisplayView1")
You need to register your custom cell:
self.tableView.register(DisplayView.self, forCellReuseIdentifier: DisplayView.identifier)
I have global array named newSystems which contains objects of following System class:
class System: NSObject, NSCoding {
var systemView: UIViewController?
private let _systemName: String!
private let _systemType: String!
let _systemImplForCustomerID: String!
init(name: String, type: String, systemID: String) {
_systemName = name
_systemType = type
_systemImplForCustomerID = systemID
systemView = System.setSystemView(_systemType: type)
}
private static func setSystemView(_systemType: String) -> UIViewController{
var sysView: UIViewController!
switch _systemType {
case "Room":
sysView = mainStoryboard.instantiateViewController(withIdentifier: "View1") as! UINavigationController
case "Kitchen":
sysView = mainStoryboard.instantiateViewController(withIdentifier: "View2") as! KitchenVC
default:
break;
}
return sysView
}
}
Now in TabBarController I am assigning the systemView property to viewControllers array:
for system in newSystems {
if let newView = system.systemView {
self.viewControllers?.insert(newView, at: 0)
}
}
So now TabBarController shows 2 items in tabBar, both are accessible and works fine.
But now in Room I would like to use _systemImplForCustomerID
Example of RoomVC:
class RoomVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = UICollectionViewCell()
*** Here I need the _systemImplForCustomerID ***
return cell
}
My question is how should I get _systemImplForCustomerID property? Or which way should I follow to have it there. As temporary solution I have created function which filtering global array newSystems and works fine, but is it correct solution?
func findCustomerSystemImplID(view: UIViewController) -> String? {
var id: String?
let currentSystem = newSystems { $0.systemView?.view.accessibilityIdentifier == view.view.accessibilityIdentifier }
if let sysID = currentSystem.first?.customerSystemImplID {
id = sysID
}
return id
}
I find myself stuck upon the implementation of a model for getting data from a firebase database.
I'm not sure what I've done so far is correct but as far as my knowledge of swift is concerned (I'm new to swift) I think I've followed the right path.
So I have a collection view which get the data from a firebase database.
The database structure is like so:
-SwimManager
--SwimmingPools
---SwimPoolName 1
-----Capacity: "2000"
-----PhotoUrl: "https//www.test"
---SwimPoolName 2
-----Capacity: "3000"
-----PhotoUrl: "https//www.test"
I'll show the code for the view controller, the model and the cell.
Here's my ViewController:
#IBOutlet weak var collectionView: UICollectionView!
var swimRef = Database.database().reference().child("SwimmingPools")
var swimmingPools = [SwimmingPool]()
verride func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
getSwimPoolInfo()
}
func getSwimPoolInfo() {
fishRef.observeSingleEvent(of: .value) { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let swimNameFb = snap.key
let value = snap.value
let swim = Fish(swimName: swimNameFb, photoUrl: "")
self.swimmingPools.append(swim)
// Not sure how to add the picture
}
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SwimCell", for: indexPath) as? SwimCell {
let swim: SwimminPool!
swim = swimmingPools[indexPath.row]
cell.configureCell(swim)
return cell
} else {
return UICollectionViewCell()
Model:
class SwimmingPool {
private var _swimName: String!
private var _photourl: String!
private var _capacity: String!
var swimName: String {
if _swimName == nil {
_swimName = ""
}
return _swimName
}
...............
init(swimName: String, photoUrl: String) {
self._SwimName = swimName
self._photourl = photoUrl
}
func getData() {
//perform action the get the data from the single swimmingPool (e.g. swimPoolName 1)
}
}
And finally, here's the cell:
class SwimCell: UICollectionViewCell {
#IBOutlet weak var swimThumb: UIImageView!
#IBOutlet weak var swimNameLbl: UILabel!
var swim: SwimmingPool!
func configureCell(_ swim: SwimmingPool) {
self.swim = swim
swimNameLbl.text = self.swim.swimName.capitalized
var url = URL(string: self.swim.photoUrl)
if url == nil {
url = URL(string: "")
}
swimThumb.sd_setImage(with: url)
}
}
In the Viewcontroller the func getSwimPooInfo is triggered after viewDidLoad and so the array swimminPools is empty... Honestly it seems I cannot figure where my mistake is....
Thx!
I have a problem with reload data in tableView in my simple swift app for iOS.
If I for the first time enter the city name into the cityTextField and press the getDataButton, so the data displays correctly, but If I enter the new city name into cityTextField and press button, so data are still the same like for the first city.
ViewController
import UIKit
class ViewController: UIViewController,UITableViewDelegate {
var arrDict :NSMutableArray=[]
#IBOutlet weak var cityTextField: UITextField!
#IBOutlet weak var weatherTableView: UITableView!
#IBAction func getDataButton(sender: AnyObject) {
weatherDataSource("http://api.openweathermap.org/data/2.5/forecast?q=" + cityTextField.text! + "&appid=<app id>")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func weatherDataSource(urlString: String) {
let urlUTF = urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let url = NSURL(string: urlUTF!)
let query = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) in dispatch_async(dispatch_get_main_queue(), { ()
self.loadDataWeather(data!)
self.weatherTableView.reloadData()
})
}
query.resume()
}
func loadDataWeather(dataPocasi: NSData){
do {
if let json = try NSJSONSerialization.JSONObjectWithData(dataPocasi, options: []) as? NSDictionary {
print(json)
for var i = 0 ; i < (json.valueForKey("list") as! NSArray).count ; i++
{
arrDict.addObject((json.valueForKey("list") as! NSArray) .objectAtIndex(i))
}
}
} catch {
print(error)
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return arrDict.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell : TableViewCell! = tableView.dequeueReusableCellWithIdentifier("Cell") as! TableViewCell
if(cell == nil)
{
cell = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)[0] as! TableViewCell;
}
let strTitle : NSNumber=arrDict[indexPath.row] .valueForKey("dt") as! NSNumber
let epocTime = NSTimeInterval(strTitle)
let myDate = NSDate(timeIntervalSince1970: epocTime)
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "hh:mm"
let dateString = dateFormatter.stringFromDate(myDate)
cell.dayLabel.text=dateString
let strDescription : NSDictionary=arrDict[indexPath.row] .objectForKey("main") as! NSDictionary
if let bla = strDescription["temp"]{
cell.tempLabel.text=bla.stringValue
}
return cell as TableViewCell
}
}
TableViewCell
import UIKit
class TableViewCell: UITableViewCell{
#IBOutlet weak var dayLabel: UILabel!
#IBOutlet weak var tempLabel: UILabel!
}
You are not instantiating your tableView delegate. Make sure you call self.weatherTableView.delegate = self inside viewDidLoad().
Also, you should create a new arrDict every time you load your data. self.arrDict = [].
In case the above ajustments dont work you should get some time debugging it. Make sure the second request is loading the data and, if so, your self.weatherTableView.reloadData() might not being called. You could try moving it to loadDataWeather().
You can reload tableview in "loadDataWether()" function.
Like,
func loadDataWeather(dataPocasi: NSData){
do {
if let json = try NSJSONSerialization.JSONObjectWithData(dataPocasi, options: []) as? NSDictionary {
print(json)
for var i = 0 ; i < (json.valueForKey("list") as! NSArray).count ; i++
{
arrDict.addObject((json.valueForKey("list") as! NSArray) .objectAtIndex(i))
}
}
} catch {
print(error)
}
self.weatherTableView.reloadData()
}