I am attempting to download/display a JPEG image from a URL path saved in my MYSQL DB. I can retrieve the path in full and search for it on a browser which verifying it exists within the DB. However, I once I have the URL path, I can not download it properly or display it as a UIImage in a cell on in my TableView Controller.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "activity", for: indexPath) as! CustomCell
let post = activities[indexPath.row]
//print(post["path"])
//let image = images[indexPath.row]
let imaged = post["path"] as! String
//print(imaged)
let image = URL(string: imaged)! //download url
print(image)
let username = post["username"] as? String
let text = post["text"] as? String
let session = URLSession(configuration: .default)
let downloadPicTask = session.dataTask(with: image) {
(data, response, error) in
if let e = error {
print("Error downloading image: \(e)")
} else {
if (response as? HTTPURLResponse) != nil {
if let imageData = data {
let pic = UIImage(data: imageData)
print(pic)
cell.activityImage.image = pic
self.images.append(pic!)
} else{
print("couldn't get image: image is nil")
}
} else {
print("Couldn't get response code")
}
}
}
cell.titleLabel.text = text
cell.usernameLabel.text = username
downloadPicTask.resume()
return cell
}
Related
Code Revised based on suggestion solution but still not working. Videos are uploading as 0 bytes. I have an app where a user should be able to record a video from their phone and when 'Use Video' is selected it uploads the video to our PHP Server. The file is uploading successfully and based on the size shown it is not empty. However when I go to play the video in the app or even directly through the browser it won't play. There's no issue with the playback code as I've hardcoded links to other videos on other sites and it works fine. Code is below, any help is much appreciated.
// Finished recording a video
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print("Got a video")
if let pickedVideo:URL = (info[UIImagePickerControllerMediaURL] as? URL) {
// Save video to the main photo album
let selectorToCall = #selector(CameraVideoViewController.videoWasSavedSuccessfully(_:didFinishSavingWithError:context:))
UISaveVideoAtPathToSavedPhotosAlbum(pickedVideo.relativePath, self, selectorToCall, nil)
imageSelected = true
uuid = UUID().uuidString
if imageSelected == true {
saveFileName = "video-\(uuid).mp4"
}
// Save the video to the app directory so we can play it later
let videoData = try? Data(contentsOf: pickedVideo)
let paths = NSSearchPathForDirectoriesInDomains(
FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentsDirectory: URL = URL(fileURLWithPath: paths[0])
let dataPath = documentsDirectory.appendingPathComponent(saveFileName)
try! videoData?.write(to: dataPath, options: [])
print("Saved to " + dataPath.absoluteString)
imagePicker.dismiss(animated: true, completion: {
// Anything you want to happen when the user saves an video
self.encodeVideo(dataPath: dataPath)
self.uploadVideo(videoData!)
})
} }
// MP4 Conversion of video
func encodeVideo(dataPath: URL){
let avAsset = AVURLAsset(url: dataPath)
let startDate = Date()
let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough)
let docDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let myDocPath = NSURL(fileURLWithPath: docDir).appendingPathComponent("temp.mp4")?.absoluteString
let docDir2 = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL
let filePath = docDir2.appendingPathComponent("rendered-Video.mp4")
//uploadVideo(filePath)
//self.encodeVideo(dataPath: dataPath)
deleteFile(filePath!)
if FileManager.default.fileExists(atPath: myDocPath!){
do{
try FileManager.default.removeItem(atPath: myDocPath!)
}catch let error{
print(error)
}
}
//self.uploadVideo((myDocPath as AnyObject) as! URL)
exportSession?.outputURL = filePath
exportSession?.outputFileType = AVFileType.mp4
exportSession?.shouldOptimizeForNetworkUse = true
let start = CMTimeMakeWithSeconds(0.0, 0)
let range = CMTimeRange(start: start, duration: avAsset.duration)
exportSession?.timeRange = range
exportSession!.exportAsynchronously{() -> Void in
switch exportSession!.status{
case .failed:
print("\(exportSession!.error!)")
case .cancelled:
print("Export cancelled")
case .completed:
let endDate = Date()
let time = endDate.timeIntervalSince(startDate)
print(time)
print("Successful")
print(exportSession?.outputURL ?? "")
default:
break
}
}
}
func deleteFile(_ filePath:URL) {
guard FileManager.default.fileExists(atPath: filePath.path) else {
return
}
do {
try FileManager.default.removeItem(atPath: filePath.path)
}catch{
fatalError("Unable to delete file: \(error) : \(#function).")
}
}
// Create Params
func createBodyWithParams(_ parameters: [String: String]?, filePathKey: String?, videoData: Data, boundary: String) -> Data {
var body = ""
if let params = parameters {
for (key, value) in params {
body += "--\(boundary)\r\n"
body += "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n"
body += "\(value)\r\n"
}
}
var filename = ""
if imageSelected {
filename = "video-\(uuid).mp4"
}
let mimetype = "video/mp4"
body += "--\(boundary)\r\n"
body += "Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n"
body += "Content-Type: \(mimetype)\r\n\r\n"
body += String(data: videoData, encoding: .utf8)!
body += "\r\n"
body += "--\(boundary)--\r\n"
return Data(body.utf8)
}
// function sending request to PHP to upload a file
func uploadVideo(_ videoData: Data) {
let id = user!["id"] as! String
uuid = UUID().uuidString
let url = URL(string: "http://www.foo.com/videoposts.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let param = [
"id" : id,
"uuid" : uuid
]
// body
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
// if picture is selected, compress it by half
let imageData = Data()
// ... body
request.httpBody = createBodyWithParams(param, filePathKey: "file", videoData: imageData, boundary: boundary)
// launch session
URLSession.shared.dataTask(with: request) { data, response, error in
// get main queu to communicate back to user
DispatchQueue.main.async(execute: {
if error == nil {
do {
// json containes $returnArray from php
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
// declare new var to store json inf
guard let parseJSON = json else {
print("Error while parsing")
return
}
// get message from $returnArray["message"]
let message = parseJSON["message"]
// if there is some message - post is made
if message != nil {
// reset UI
self.postBtn.alpha = 0.4
self.imageSelected = false
// switch to another scene
self.tabBarController?.selectedIndex = 4
}
} catch {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = "\(error)"
appDelegate.infoView(message: message, color: colorSmoothRed)
})
return
}
} else {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = error!.localizedDescription
appDelegate.infoView(message: message, color: colorSmoothRed)
})
return
}
})
}.resume()
}
Okay, I did code something to actually encode and upload a video.
Unfortunately, doing a multipart/form-data in URLSession is actually pretty hard, so I used Alamofire to actually upload the video.
This is the code in parts:
UIImagePickerControllerDelegate (this is the one you should modify, check the //TODO comments)
// MARK: UIImagePickerControllerDelegate
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
print("Cancelled video operation.")
dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
guard let mediaType = info[UIImagePickerControllerMediaType] as? String else {
print("Error with media type. Cancelling.")
dismiss(animated: true, completion: nil)
return;
}
print("Media type: \(mediaType)")
if ( mediaType == "public.movie" ) { // Video selected
let videoURL: URL
if info[UIImagePickerControllerMediaURL] != nil {
videoURL = info[UIImagePickerControllerMediaURL] as! URL
}
else {
videoURL = info[UIImagePickerControllerReferenceURL] as! URL
}
if ( picker.sourceType == .camera ) {
// The original video came from the camera, so it's considered new
// Save it to the photo library
saveVideo(url: videoURL, albumName: "MyApp")
}
// Dismiss the media picker and then re-encode the video
dismiss(animated: true) {
self.exportVideoToMP4(url: videoURL) { (exportedVideoURL) in
guard let tempURL = exportedVideoURL else {
print("ERROR: Unknown error. The exported video URL is nil.")
return
}
print("Temporary video successfully exported to: \(tempURL.absoluteString)")
// TODO: Add your own POST parameters
let uuid = UUID().uuidString
let params = [
"uuid" : uuid,
"user" : "myUserNameOrWhatever"
]
// TODO: Change the parameters for uploading
self.upload( to: "http://yourweb.com/uploadVideo.php", // The URL to send the upload to
videoURL: tempURL, // The file URL of the temporary video file
parameters: params, // The POST parameters you want to send along with the upload
fileName: "vid-\(uuid).mp4", // The filename you want the server to receive.
fieldName: "video_file" // This is "name" from <input type="file" name="video_file" ... /> in HTML
) { (response) in
guard let resp = response else {
print("ERROR: Empty or unrecognizable response from server.")
return
}
print("Video uploaded. RESPONSE: \(resp)")
//: TODO Parse the server response after uploading
}
}
}
}
}
Photo Library helper methods
// MARK: Photo Library
func saveVideo(url: URL, albumName: String) {
// Check authorization status before trying to save the video
switch PHPhotoLibrary.authorizationStatus() {
case .notDetermined:
PHPhotoLibrary.requestAuthorization() { (status) in
switch status {
case .authorized:
self.saveVideo(url: url, albumName: albumName) // Re-try to save the video after authorization
return
default:
return
}
}
case .authorized:
// Save the video to the Photo Library here
if let assetCollection = assetCollection(albumName: albumName) {
// Asset collection exists, insert directly
insertVideo(url: url, assetCollection: assetCollection)
}
else {
// Asset collection doesn't exist, create it and then insert
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: albumName)
}, completionHandler: { (success, error) in
guard success else {
print("ERROR: \(error!.localizedDescription)")
return
}
let createdAssetCollection = self.assetCollection(albumName: albumName)!
self.insertVideo(url: url, assetCollection: createdAssetCollection)
})
}
return
default:
// Not authorized
print("Not authorized to save a video to the Photo Library.")
return
}
}
func assetCollection(albumName: String) -> PHAssetCollection? {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format:"title == '\(albumName)'")
let fetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: fetchOptions)
return fetchResult.firstObject
}
func insertVideo(url: URL?, assetCollection: PHAssetCollection) {
guard let videoURL = url else {
print("ERROR: The URL to insert into the Photo Library is empty.")
return
}
PHPhotoLibrary.shared().performChanges({
let createAssetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
let assetPlaceholder = createAssetRequest?.placeholderForCreatedAsset
let changeRequest = PHAssetCollectionChangeRequest(for: assetCollection)
let enumeration: NSArray = [assetPlaceholder!]
changeRequest?.addAssets(enumeration)
}, completionHandler: { (success, error) in
guard success else {
print("ERROR: \(error!.localizedDescription)")
return
}
print("Video saved successfully to the Photo Library album.")
})
}
Video Uploading (using Alamofire, installed from CocoaPods)
// MARK: Video upload
func upload(to uploadAddress: String, videoURL: URL, parameters: [String:Any]?, fileName: String, fieldName: String, _ completion: ((String?) -> Void)?) {
Alamofire.upload(multipartFormData: { (multipartFormData) in
// Add the video file (if data is correct)
if let videoData = FileManager.default.contents(atPath: videoURL.path) {
multipartFormData.append(videoData, withName: fileName)
}
// Add the post params (if available)
if let params = parameters {
for (key, value) in params {
multipartFormData.append( (value as! String).data(using: .utf8)! , withName: key)
}
}
}, to: uploadAddress)
{ (result) in
switch result {
case .success(let upload, _, _):
upload.responseString { (response) in
if let completionHandler = completion {
completionHandler(response.result.value)
}
}
case .failure(let encodingError):
print("ERROR: \(encodingError.localizedDescription)")
if let completionHandler = completion {
completionHandler(nil)
}
}
}
}
AVFoundation (the encoding method)
// MARK: AVFoundation
func exportVideoToMP4(url: URL, _ completion: #escaping ((URL?) -> Void)) {
// Show some sort of indicator here, as this could take a while
// Generate a temporary URL path to export the video
let relativePath = "myAppTempVideoExport.mp4";
let outputFilePath = NSTemporaryDirectory() + relativePath;
print("Temp file path: \(outputFilePath)")
// If there's any temp file from before at that path, delete it
if FileManager.default.fileExists(atPath: outputFilePath) {
do {
try FileManager.default.removeItem(atPath: outputFilePath)
}
catch {
print("ERROR: Can't remove temporary file from before. Cancelling export.")
completion(nil)
return
}
}
// Export session setup
let outputFileURL = URL(fileURLWithPath: outputFilePath)
let asset = AVAsset(url: url) // Original (source) video
// The original video codec is probably HEVC, so we'll force the system to re-encode it at the highest quality in MP4
// You probably want to use medium quality if this video is intended to be uploaded (as this example is doing)
if let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) {
exportSession.outputURL = outputFileURL
exportSession.outputFileType = .mp4
exportSession.exportAsynchronously {
// Hide the indicator for the export session
switch exportSession.status {
case .completed:
print("Video export completed.")
completion(outputFileURL)
return
case .failed:
print("ERROR: Video export failed. \(exportSession.error!.localizedDescription)")
completion(nil)
return
case .cancelled:
print("Video export cancelled.")
completion(nil)
return
default:
break
}
}
}
else {
print("ERROR: Cannot create an AVAssetExportSession.")
return
}
}
Now, for this to work, you obviously have to record a video, import frameworks and specify that your view controller conforms to protocols, so:
import AVFoundation
import Photos
import Alamofire
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
let imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
imagePicker.sourceType = .camera
imagePicker.delegate = self
imagePicker.showsCameraControls = true
imagePicker.allowsEditing = true
imagePicker.mediaTypes = ["public.movie"]
DispatchQueue.main.async {
self.present(self.imagePicker, animated: true, completion: nil)
}
}
}
I hope this is useful to you, feel free to ask.
So I'm in the middle of designing a social media application that displays posts, comprised of a picture and a description, on a newsfeed. I am able to upload posts to our database, however, I am having problems pulling the image from the database. Specifically, I am getting an error around the code
let image = post["path"] as! UIImage
Saying:
"Thread 1: signal SIGABRT", while the compiler says "Could not cast
value of type '__NSCFString' to 'UIImage'."
Here's the entirety of my code:
import UIKit
class SecondViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var activities = [AnyObject]()
var images = [UIImage]()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//tableView.contentInset = UIEdgeInsetsMake(2, 0, 0, 0)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return activities.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "activity", for: indexPath) as! CustomCell
let post = activities[indexPath.row]
print(post["path"])
let image = post["path"] as! UIImage
let username = post["username"] as? String
let text = post["text"] as? String
cell.titleLbl.text = text
cell.userLbl.text = username
cell.activityImage.image = image //as! UIImage
//cell.dateLbl.text = activities[indexPath.row]
//cell.activityImage.image = images[indexPath.row]
DispatchQueue.main.async {
}
return cell
}
override func viewWillAppear(_ animated: Bool) {
loadActivities()
}
func loadActivities() {
let id = user!["id"] as! String
let url = URL(string: "https://cgi.soic.indiana.edu/~team7/posts.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "id=\(id)&text=&uuid="
request.httpBody = body.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
// clean up
self.activities.removeAll(keepingCapacity: false)
self.images.removeAll(keepingCapacity: false)
//self.tableView.reloadData()
guard let parseJSON = json else {
print("Error while parsing")
return
}
guard let posts = parseJSON["posts"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
self.activities = posts
for i in self.activities.indices {
print("printed")
let path = self.activities[i]["path"] as? String
if let actualPath = path, !actualPath.isEmpty, let url = URL(string: actualPath) {
if let imageData = try? Data(contentsOf: url) {
if let image = UIImage(data: imageData) {
self.images.append(image)
print(self.images)
}
}
}
/*
if path != "" {
//let url = URL(string: path!)!
let url = URL(fileURLWithPath: path!)
let imageData = try? Data(contentsOf: url)
//let imageData = try? Data(contentsOf: url)
let image = UIImage(data: imageData!)!
self.images.append(image)
*/
else {
let image = UIImage()
self.images.append(image)
}
}
self.tableView.reloadData()
} catch {
}
} else {
}
})
}.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension NSMutableData {
func appendString(_ string : String) {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
append(data!)
}
}
If there is anything you think I should do to improve or make the code run correctly, please let me know!
I am currently in the process of designing an application that has a newsfeed. Images and text are stored in our database but I am having trouble pulling the image and displaying it. The code I have should work but I am getting a fatal error saying "THREAD 1: EXC_BAD_INSTRUCTION" around
let imageData = try? Data(contentsOf: url)
let image = UIImage(data: imageData!)!
and the compiler displays this message - "fatal error: unexpectedly found nil while unwrapping an Optional value".
I'm getting the error in this snippet of code:
if!path!.isEmpty {
let url = URL(string: path!)!
let imageData = try? Data(contentsOf: url)
let image = UIImage(data: imageData!)!
self.images.append(image)
} else {
let image = UIImage()
self.images.append(image)
}
Being very new to programming, I've been stuck on this for quite a while now. If there is anything you suggest, let me know! Thanks!
Also, the photo I am trying to retrieve is a .jpg, not sure if that may be important.
(Here's the rest of the code if it is needed)
import UIKit
class SecondViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var activities = [AnyObject]()
var images = [UIImage]()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//tableView.contentInset = UIEdgeInsetsMake(2, 0, 0, 0)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return activities.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "activity", for: indexPath) as! CustomCell
let post = activities[indexPath.row]
let image = images[indexPath.row]
let username = post["username"] as? String
let text = post["text"] as? String
cell.titleLbl.text = text
cell.userLbl.text = username
cell.activityImage.image! = image
//cell.dateLbl.text = activities[indexPath.row]
//cell.activityImage.image = images[indexPath.row]
return cell
}
override func viewWillAppear(_ animated: Bool) {
loadActivities()
}
func loadActivities() {
let id = user!["id"] as! String
let url = URL(string: "https://cgi.soic.indiana.edu/~team7/posts.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "id=\(id)&text=&uuid="
request.httpBody = body.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
// clean up
self.activities.removeAll(keepingCapacity: false)
self.images.removeAll(keepingCapacity: false)
self.tableView.reloadData()
guard let parseJSON = json else {
print("Error while parsing")
return
}
guard let posts = parseJSON["posts"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
self.activities = posts
for i in 0 ..< self.activities.count {
let path = self.activities[i]["path"] as? String
if !path!.isEmpty {
let url = URL(string: path!)!
let imageData = try? Data(contentsOf: url)
let image = UIImage(data: imageData!)!
self.images.append(image)
} else {
let image = UIImage()
self.images.append(image)
}
}
self.tableView.reloadData()
} catch {
}
} else {
}
})
}.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension NSMutableData {
func appendString(_ string : String) {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
append(data!)
}
}
Why are you doing all of this ? Get the URLs of the images from your server and then load them asynchronously in cellForRowAtIndexPath using SDWebImage. It is a library you can find it here. https://github.com/rs/SDWebImage. This way you won't need to store your images in an array and it will be faster to load.
Or you can also implement your own async loading of images.
you could try something like this to avoid runtime errors:
if let actualPath = path, !actualPath.isEmpty, let url = URL(string: actualPath) {
if let imageData = try? Data(contentsOf: url) {
if let image = UIImage(data: imageData) {
self.images.append(image)
}
}
}
instead of this
if!path!.isEmpty {
let url = URL(string: path!)!
let imageData = try? Data(contentsOf: url)
let image = UIImage(data: imageData!)!
self.images.append(image)
}
I am currently working on developing an application that pulls data (Address and location information) from a php web service and displays the data as a string in a tableview cell. My tableview is embedded in a Navigation controller, when a cell is clicked a segue links to a map with several hard-coded pins on the map.
I am interested in creating a segue that links a tableview cell (Address) to a specific pin/annotation in a map as a direct reference to the address.
Additionally, I am interested in creating a custom annotation that displays data (Name, Address, photo etc.) regarding its location.
Ideally I am trying to recreate a simpler version of SpotHero.
Thanks!
Heres Some of my code:
TableController:
class TableController: UITableViewController {
var TableData:Array< String > = Array < String >()
var garagedata :Array<String> = Array < String >()
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url("http://cgi.soic.indiana.edu/~team20/service.php")
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BasicCell", for: indexPath)
cell.textLabel?.text = TableData[indexPath.row]
//Wrap text in tableview
cell.textLabel?.numberOfLines=0
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
//Automatic sizing of tableviewcells
return UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Segue to the second view controller
self.performSegue(withIdentifier: "detailSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// get a reference to the second view controller
let mapController = segue.destination as! MapController
// set a variable in the second view controller with the data to pass
mapController.receivedData = "Hello"
}
func get_data_from_url(_ link:String)
{
let url:URL = URL(string: link)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
self.extract_json(data!)
})
task.resume()
}
func extract_json(_ data: Data)
{
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch
{
return
}
guard let data_list = json as? NSArray else
{
return
}
if let garages_list = json as? NSArray
{
let locations: NSMutableArray = NSMutableArray()
for i in 0 ..< data_list.count
/*{
print(TableData)
if let garage_obj = garages_list[i] as? NSDictionary
{
if let garage_name = garage_obj["GName"] as? String
{
if let garage_address = garage_obj["Address"] as? String
{
if let garage_code = garage_obj["Count"] as? String
{
if let garage_cap = garage_obj["Capacity"] as? String
{
TableData.append(garage_name + "\n"
+ garage_address + " [" + garage_code + "/" + garage_cap+"]")
garagedata.append(garage_name + "\n"
+ garage_address + " [" + garage_code + "/" + garage_cap+"]")
}
}
}
}
}}*/
{
let location = GarageModel()
if let garage_obj = garages_list[i] as? NSDictionary
{
//the following insures none of the JsonElement values are nil through optional binding
if let name = garage_obj["GName"] as? String,
let address = garage_obj["Address"] as? String,
let latitude = garage_obj["Latitude"] as? String,
let longitude = garage_obj["Longitude"] as? String,
let count = garage_obj["Count"] as? String
{
location.name = name
location.address = address
location.latitude = latitude
location.longitude = longitude
location.count = count
}
locations.add(location)
print(locations)
}
}}
DispatchQueue.main.async(execute: {self.do_table_refresh()})
}
func do_table_refresh()
{
self.tableView.reloadData()
}
}
GarageModel:
class GarageModel: NSObject {
//properties
var name: String?
var address: String?
var latitude: String?
var longitude: String?
var count: String?
var capacity: String?
//empty constructor
override init()
{
}
//construct with #name, #address, #latitude, and #longitude parameters
init(name: String, address: String, latitude: String, longitude: String, count: String, capacity: String) {
self.name = name
self.address = address
self.latitude = latitude
self.longitude = longitude
self.count = count
self.capacity = capacity
}
//prints object's current state
override var description: String {
return "Name: \(name), Address: \(address), Latitude: \(latitude), Longitude: \(longitude), Count: \(count), Capacity: \(capacity)"
}
}
I have hardcoded some pins for aesthetic/testing purposes but I do not have these dynamically appearing.
Map Controller:
class MapController : UIViewController {
#IBOutlet weak var mapView: MKMapView!
var receivedData = ""
override func viewDidLoad() {
super.viewDidLoad()
print(receivedData)
let latitude: CLLocationDegrees = 39.173294
let longitude: CLLocationDegrees = -86.520244
let latitude2: CLLocationDegrees = 39.163589
let longitude2: CLLocationDegrees = -86.526266
let latitude3: CLLocationDegrees = 39.167250
let longitude3: CLLocationDegrees = -86.515059
let latDelta: CLLocationDegrees = 0.05
let lonDelta: CLLocationDegrees = 0.05
let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let location2 = CLLocationCoordinate2D(latitude: latitude2, longitude: longitude2)
let location3 = CLLocationCoordinate2D(latitude: latitude3, longitude: longitude3)
let region = MKCoordinateRegion(center: location, span: span)
mapView.setRegion(region, animated: true)
let garage1 = MKPointAnnotation()
garage1.title = "Fee Lane Parking Garage"
garage1.subtitle = "Count XX/150 \n 11th and Fee"
garage1.coordinate = location
mapView.addAnnotation(garage1)
let garage2 = MKPointAnnotation()
garage2.title = "Henderson Parking Garage"
garage2.subtitle = "Count XX/150 Fess and Atwater"
garage2.coordinate = location2
mapView.addAnnotation(garage2)
let garage3 = MKPointAnnotation()
garage3.title = "Jordan Parking Garage"
garage3.subtitle = "Count XX/150 North Jordan Street"
garage3.coordinate = location3
mapView.addAnnotation(garage3)
}
}
I been trying to implement a Pull to Refresh to my tableview to refresh JSON Data from server. On what I have tried on the debug Area screen it shows me the data reloads but the Cell labels and images doesn't refresh when I make changes on the PHP file on server .... I'M USING THE CODE BELOW:
import UIKit
class JSONData: UITableViewController {
var newsList = [News]()
override func viewDidLoad() {
super.viewDidLoad()
self.loadJSONDATA()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return newsList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! NewsStrings
let s = newsList[indexPath.row] as News
cell.labellName.text = s.newsName
cell.labelDesc.text = s.newsDesc
cell.labelDate.text = s.newsDate
cell.imgvImage.image = UIImage(named: "BgIcon.jpg")
if let img = UIImage(data: s.newsImage)
{
cell.imgvImage.image = img
}else
{
self.loadImages(s, indexPath: indexPath)
}
return cell
}
///////////////// JSON DATA ////////////////////////
func loadJSONDATA()
{
let urlString = "http://example.com/folder/JSON.php"
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate:nil, delegateQueue: nil)
if let url = NSURL(string: urlString)
{
let request = NSURLRequest(URL: url)
let taskData = session.dataTaskWithRequest(request, completionHandler: {
(data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
if (data != nil)
{
var parseError:NSError?
let parsedNews = (try! NSJSONSerialization.JSONObjectWithData(data!, options: [])) as! NSDictionary
//print("JSON Data \n \(parsedNews)")
if let news:AnyObject = parsedNews["News"]
{
self.parseJSON(news)
}
} else
{
}
})
taskData.resume()
}
}
/////////// LODING JSON DATA //////////////
func parseJSON(jsonData:AnyObject)
{
if let newsData = jsonData as? [[NSObject:AnyObject]]
{
var news:News
for s in newsData {
news = News()
if let sId:AnyObject = s["NewsID"]
{
if let NewsID = sId as? String
{
print("News id = \(NewsID)")
}
}
if let sn:AnyObject = s["newsName"]
{
if let newsName = sn as? String
{
news.newsName = newsName
//println("Store id = \(storeName)")
}
}
if let sn:AnyObject = s["newsDate"]
{
if let newsDate = sn as? String
{
news.newsDate = newsDate
//println("Store id = \(storeName)")
}
}
if let sn:AnyObject = s["newsIcon"]
{
if let newsIcon = sn as? String
{
news.newsImageName = newsIcon
//println("News Icon = \(newsIcon)")
}
}
if let sn:AnyObject = s["newsDesc"]
{
if let newsIcon = sn as? String
{
news.newsDesc = newsIcon
}
}
newsList += [news]
}
NSOperationQueue.mainQueue().addOperationWithBlock(){
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
self.tableView.reloadData()
}
}
}
/////////// LODING IMAGES FROM JSON DATA //////////////
func loadImages(news:News, indexPath:NSIndexPath)
{
let urlString = news.newsImageName
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate:nil, delegateQueue: nil)
if let url = NSURL(string: urlString)
{
let request = NSURLRequest(URL: url)
let taskData = session.dataTaskWithRequest(request, completionHandler: {
(data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
if (data != nil)
{
news.newsImage = data!
let image = UIImage(data: data!)
dispatch_async(dispatch_get_main_queue(),{
if let cell = self.tableView.cellForRowAtIndexPath(indexPath) as? NewsStrings {
cell.imgvImage.image = image
}
})
} else
{
}
})
taskData.resume()
}
}
}
You can add pull to refresh within your table view like below code,
var refreshControl = UIRefreshControl()
In view did load,
self.refreshControl.addTarget(self, action: Selector("loadJSONDATA"), forControlEvents: UIControlEvents.ValueChanged)
self.addSubview(self.refreshControl) // OR self.myTable?.addSubview(self.refreshControl)
Then add this line after self.parseJSON(news)
self.refreshControl.endRefreshing()
This will be help full to you. :)
I got something to make it work, this is what I have on my viewDidLoad:
self.refreshControl?.addTarget(self, action: #selector(NewsList.handleRefresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
The Function for the handleRefresh:
func handleRefresh(refreshControl: UIRefreshControl) {
newsList.removeAll() //<-This clear the whole tableview(labels, images,ect...)making table view blank
if self.refreshControl!.refreshing
{
self.loadRecords() //<-Reloads All data & labels
self.refreshControl!.endRefreshing()
}
}
The only thing is that the app crashes after doing the "Pull to refresh" multiples