I am trying to implement the UIProgressView!. I am downloading some data from a website and I am not wrapping my head around some of the fundamentals of how to write this code. I have the label called message which will be updated when the data is downloaded, but I want to show the progress of the data being downloaded.
import UIKit
class ViewController: UIViewController {
//They user types in the city they want to recieve the weather from
#IBOutlet weak var city: UITextField!
//The label that is being updated when the data finally reaches the phone
#IBOutlet weak var message: UILabel!
//The progress bar
#IBOutlet weak var downloadProgress: UIProgressView!
//My 'What's the weather?' button
#IBAction func buttonPressed(sender: AnyObject) {
//When you touch the button, the keyboard goes away
self.view.endEditing(true)
//setting the urlString to the address of the website, it will add the city that you type into the city text field
var urlString = "http://www.weather-forecast.com/locations/" + city.text.stringByReplacingOccurrencesOfString(" ", withString: "") + "/forecasts/latest"
//Setting the url to the urlString
var url = NSURL(string: urlString)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!){(data, response, error) in
var urlContent = NSString(data: data, encoding: NSUTF8StringEncoding)
var contentArray = urlContent!.componentsSeparatedByString("<span class=\"phrase\">")
var newContentArray = contentArray[1].componentsSeparatedByString("</span>")
//Updating the message text with the content that I want from the HTML source
self.message.text = (newContentArray[0] as! String)
}
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
self.downloadProgress.progress = 0.0
}
func makeMyProgressBarMoving {
var recievedData : Float
var expectedTotalSize : Float
var actual : Float = downloadProgress.progress
if (actual < 1) {
downloadProgress.progress = actual + (recievedData/expectedTotalSize)
[NSTimer .scheduledTimerWithTimeInterval(0.05, invocation: self, repeats: false)]
}
}
You cannot update the progress because you do not have any progress. You've implemented the wrong way of downloading the data. Use the other way - the one where you get delegate messages. One of them tells you the progress. In particular, you'll implement
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData {
Here you'll accumulate the data in an NSMutableData, and each time, you can compare the size of that accumulated data to the size of the expected total data, and thus you have the basis for updating the UIProgressView.
Related
I'm really new into swift & currently learning API by doing a project that shows list of games from rawg.io referring to the website's doc. I created GameFeed.swift & GameDetail.swift to pull name, release date, and rating from it and working fine in my console.
GameFeed.swift :
struct GameFeed: Codable {
let results:[GameDetail]
}
GameDetail.swift :
struct GameDetail: Codable {
let name:String
let released:String
let rating:Double
}
Now i'm trying to put the results to a simple UIlabel like gameName.text, gameReleased.text & gameRating.text from ViewController.swift so it will be show in Main.Storyboard
i did research on google about how to show it to these UIlabel by using DispatchQueue.main.async but when i'm declaring it, it receiving error :
Value of type 'GameFeed' has no member 'name'
same error messages also happened to released & rating. This is my ViewController.Swift :
class ViewController: UIViewController {
#IBOutlet weak var gameName: UILabel!
#IBOutlet weak var gameReleased: UILabel!
#IBOutlet weak var gameRating: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Defining API Site
let urlString = "https://api.rawg.io/api/games"
let url = URL(string: urlString)
guard url != nil else {
return
}
// Calling API
let session = URLSession.shared
let dataTask = session.dataTask(with: url!){
(data, response, error) in
if error == nil && data != nil {
let decoder = JSONDecoder()
do {
let gameFeed = try decoder.decode(GameFeed.self, from: data!)
print(gameFeed)
DispatchQueue.main.async {
self.gameName.text = gameFeed.name
self.gameReleased.text = gameFeed.released
self.gameRating.text = gameFeed.rating
}
}
catch {
print("Error Parsing JSON")
}
}
}
dataTask.resume()
}
}
What should i do to make it possible to parse the data to labels?
The GameFeed contains an Array of GameDetails. But you are trying to set a single GameDetail on those labels. You should first pull out a single GameDetail from that array, then assign it in a way you like.
DispatchQueue.main.async {
let gameDetail = gameFeed.results.first // <- This will return the first one
self.gameName.text = gameDetail?.name
self.gameReleased.text = gameDetail?.released
self.gameRating.text = gameDetail?.rating
}
I am trying to retrieve certain data from my Firebase Database - the profile image. As you can see, this is from a UITableViewCell. I have an #IBOutlet for my imageView I want to cover.
As the view awakens, you can see that I go through, and make sure that I can get the information. I know how to retrieve data from Firebase, but not photo URLs, and then convert to the photo itself.
I'm not sure why it isn't working. I am getting an error, and will show it below. There is a possibility it is because of the URL unwrapping stuff, or as if the Firebase isn't formatted correctly, which I think it is, though.
Error Message : Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
import UIKit
import FirebaseAuth
import FirebaseDatabase
import Firebase
class ProfileCellControler: UITableViewCell {
#IBOutlet var name : UILabel!
#IBOutlet var rating : UILabel!
#IBOutlet var imageViewPro : UIImageView!
var databaseRefer : DatabaseReference!
var databaseHandle : DatabaseHandle!
override func awakeFromNib() {
super.awakeFromNib()
var urlString = ""
let urll = URL(string: urlString)!
databaseRefer = Database.database().reference()
let userID = Auth.auth().currentUser!.uid
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Profile Name").observe(.value, with: { (data) in
print(String((data.value as? String)!))
self.name.text = "\(String((data.value as? String)!))"
print("Done")
})
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Stars").observe(.value, with: { (data) in
print(String((data.value as? String)!))
if ((String((data.value as? String)!)) == "N/A") {
self.rating.text = "No Rating"
} else {
self.rating.text = "\(String((data.value as? String)!)) ★"
}
print("Done")
})
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Profile Image").observe(.value, with: { (data) in
print(String((data.value as? String)!))
print("Done \(String((data.value as? String)!))")
urlString = (String((data.value as? String)!))
})
ImageService.downloadImage(withURL: urll) { (image) in
self.imageViewPro.image = image
}
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
The string for the URL is found nil because you are creating the call to download the image for your url before the urll has been initialized with a value from the database in:
databaseHandle = databaseRefer.child("Users").child(userID).child("Profile").child("Profile Image").observe(.value, with: { (data) in
print(String((data.value as? String)!))
print("Done \(String((data.value as? String)!))")
urlString = (String((data.value as? String)!))
})
observe(.value, with: ) Is an asynchronous operation thus
ImageService.downloadImage(withURL: urll) { (image) in
self.imageViewPro.image = image
}
Is being called before observe(.value, with:) is resolved. I would recommend moving the callback for the download URL inside of the completion for .observe(:value, :with) or using grand central dispatch to control the flow better.
As a side note, I highly recommend SDWebImage for handling your image downloading needs as it is configurable with a default image for situations such as this when the image fails to load.
Import KingFisher to make your life easier and then..
Download string representation of image from Firebase asynchronically.
Assign downloaded image to imageView with .kf.setImage method.
I'm making an application using swift that calls onto a Google Places api to generate a JSON file of locations, including images of the location generated. These images are given as URLs that I need to convert to UIImage and then append these images to an array. When opening the contents of the URL, I am able to see the images, but these images are not appending to the array. Here is my view controller's class that is attempting to generate said images:
import Foundation
import UIKit
import WebKit
class ImageViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var photos: [Photo]?
var uiImages: [UIImage]?
override func viewDidLoad() {
for photo in photos! {
let url = URL(string:"https://maps.googleapis.com/maps/api/place/photo?photoreference=\(photo.reference)&sensor=false&maxheight=\(photo.height)&maxwidth=\(photo.width)&key=AIzaSyC_SoYT7VnYnyz3GAb7qqbXjZeLFG5GE70")
let data = try? Data(contentsOf: url!)
let image: UIImage = UIImage(data: data!)!
self.uiImages?.append(image)
print(image)
print(self.uiImages)
}
}
}
In this loop, I tell the code to print "image" and then the array "uiImages" after the append occurs. Yet, I am returning nil when printing the array of images, but not nil for the image itself.
I feel this could have something to do with the asynchrony of the method, but I also tried appending on the main thread and this did not change anything. Additionally, the "photos" variable is not nil, it is set when the view controller is instantiated.
Here is the code for the Photo class:
import Foundation
struct Photo {
var height: Int
var width: Int
var reference: String
init?(height: Int, width: Int, reference: String) {
self.height = height
self.width = width
self.reference = reference
}
}
EDIT:
Here is what my ImageViewController class looked like after making the suggested changes:
import Foundation
import UIKit
import WebKit
class ImageViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var photos: [Photo]?
var uiImages = [UIImage]()
override func viewDidLoad() {
for photo in photos! {
let url = URL(string:"https://maps.googleapis.com/maps/api/place/photo?photoreference=\(photo.reference)&sensor=false&maxheight=\(photo.height)&maxwidth=\(photo.width)&key=AIzaSyC_SoYT7VnYnyz3GAb7qqbXjZeLFG5GE70")
let task = URLSession.shared.dataTask(with: url!) {(data, response, error) in
let image: UIImage = UIImage(data: data!)!
self.uiImages.append(image)
print(image)
print(self.uiImages)
}
task.resume()
}
}
}
You never initialize your array, you only declare it.
Change:
var uiImages: [UIImage]?
to:
var uiImages = [UIImage]()
Then change:
self.uiImages?.append(image)
to:
self.uiImages.append(image)
On a side note, never load remote data using Data(contentsOf:). Use URLSession and a dataTask. Your code is going to cause all kinds of problems due to slow remote data access on the main queue.
I'm a relatively new coder who wants to try out the iOS development language "Swift". The app I am creating is a weather app that will show the degrees in celsius and possibly some advice which is where the if statement comes in. However, I am encountering an issue with my code which I do not know how to solve.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var weatherLabel: UILabel!
#IBOutlet weak var quote: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let apiKey = "60e0655f404d7bf1"
let url = NSURL(string: "https://api.wunderground.com/api/eb56be2ac82d34b5/geolookup/conditions/forecast/q/Canada/Toronto.json")
let data = NSData(contentsOfURL: url!)
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
let weather = json["current_observation"]!!["temp_c"]
weatherLabel.text = "\(weather!!)"
var quote = weather
var celsius = 20
if celsius >= quote {
print("Its cold outside, bring a jacket!")
}else{
print("Its hot outside, remember to drink water!") //if the weather in celsius is over 20 degrees, say ("Its hot outside, remember to drink water!")
}
} catch {
print("error serializing JSON: \(error)")
}
// Do any additional setup after loading the view, typically from a nib.
}
}
Does anyone know how I can set the quote in one specific text label and the degrees in the weatherLabel?
I also have an error saying that I can't convert AnyObject to an int.
Var quote = Int(weather) use this.
I am trying to change my UIWebView Url depending on the variables I get from my Parse.com database.
First, I split the three worded String into three parts then I place the parts into the url.
However, I am getting an error! It is very strange:
Here is the code incase you are unable to see it:
import UIKit
import Parse
import ParseUI
class myBookingsItemTableViewController: UITableViewController {
var object: PFObject!
#IBOutlet weak var typeOfBookingLabel: UILabel!
#IBOutlet weak var typeOfBookingQRCode: UIWebView!
var ticketId = String()
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if (self.object != nil) {
self.typeOfBookingLabel?.text = self.object["booking"] as? String
var ticketID = self.object["ticketId"] as? String
self.ticketId = ticketID!
var ticketIdArr = split(ticketId) {$0 == " "}
var first: String = ticketIdArr[0]
var second: String? = ticketIdArr.count > 1 ? ticketIdArr[1] : nil
var third: String? = ticketIdArr.count > 2 ? ticketIdArr[2] : nil
let url = NSURL (string: "http://chart.apis.google.com/chart?chl=\(first)+\(second)+\(third)&chs=200x200&cht=qr&chld=H%7C0")
let requestObj = NSURLRequest(URL: url!)
typeOfBookingQRCode.loadRequest(requestObj)
} else {
self.object = PFObject(className: "Bookings")
}
}
}
You have to make sure that your first, second and third do not contain any whitespaces - otherwise you will not be able to create a URL from it - it will return nil and your unwrapping fails.
You can do that using
first = first.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
Additionally I would recommend against using nil in your situation but rather either exclude the second and third argument if they would be nil or replace the nil with an actual value - that way you can change their type to String and do not have to worry about the optionals any more.
The following code snippet escapes all three values and always generates an URL:
func urlEncode(par:String!) -> String! {
return par.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
}
var first: String = urlEncode(ticketIdArr[0])
var second: String = ticketIdArr.count > 1 ? urlEncode(ticketIdArr[1]) : "nil"
var third: String = ticketIdArr.count > 2 ? urlEncode(ticketIdArr[2]) : "nil"