How do I used a string value from a function in a another class to update an UILabel on my ViewController?
Here is my code:
View controller:
import UIKit
class ViewController: UIViewController, dataEnterdDelegate {
#IBOutlet weak var auaTempLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let weather2 = WeatherService2()
weather2.getWeatherData("Oranjestad,AW")
}
**func userDidEnterInformation(info: NSString)
{
testLabel!.text = info as String
}**
func setLabel2(information: String)
{
auaTempLabel.text = information
}
The other class named WeatherService2 contain the following codes:
**protocol dataEnterdDelegate{
func userDidEnterInformation(info:NSString)
}**
Class WeatherService2{
var currentTempeture:String?
let targetVC = ViewController()
**var delegate: dataEnterdDelegate?**
func getWeatherData(urlString:String)
{
let url = NSURL(string: urlString)!
let sqlQuery = "select * from weather.forecast where woeid in (select woeid from geo.places(1) where text=\"\(url)\")"
let endpoint = "https://query.yahooapis.com/v1/public/yql?q=\(sqlQuery)&format=json"
let testString = (String(endpoint))
getData(testString)
}
func getData(request_data: String)
{
let requestString:NSString = request_data.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
let url_with_data = NSURL(string: requestString as String)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url_with_data){
(data, response, error) in dispatch_async(dispatch_get_main_queue(), {
if data == nil
{
print("Failed loading HTTP link")
}else{
self.setLabel(data!)
}
})
}
task.resume()
}
func setLabel(weatherData:NSData)
{
enum JSONErrors: ErrorType
{
case UserError
case jsonError
}
do{
let jsonResults = try NSJSONSerialization.JSONObjectWithData(weatherData, options: .AllowFragments)
if let city = jsonResults["query"] as? NSDictionary
{
if let name = city["results"] as? NSDictionary
{
if let channel = name["channel"] as? NSDictionary
{
if let item = channel["item"] as? NSDictionary
{
if let condition = item["condition"] as? NSDictionary
{
if let temp = condition["temp"] as? String
{
setTemp(temp)
**delegate!.userDidEnterInformation(temp)**
}
}
}
}
}
}
}
catch {
print("Failed to load JSON Object")
}
}
func setTemp(tempeture:String)
{
self.currentTempeture = tempeture
}
func getTemp() ->String
{
return self.currentTempeture!
}
}
The code runs fine and everything but I get an error "Fatal error: unexpectedly found nil while unwrapping an Optional value" when I try to update the UILabel in my ViewController.
When I used the print("The return value is: "+information) in the view controller class it print the return value correctly.
This is the reason I'm confused right now because I don't know why I still getting the "Fatal error: unexpectedly found nil while unwrapping an Optional value" when trying to use this value to update my UILabel.
Can anyone help me with this problem?
Thanks in advance
For that you have to create delegate method.
In viewController you create delegate method and call it from where you get response and set viewController.delegate = self
I could not explain more you have to search for that and it will works 100% .
All the best.
I manage to fix this issue by doing the following:
I create the following class
- Item
- Condition
- Channel
These classes implement the JSONPopulator protocol.
The JSONPopulator protocol:
protocol JSONPopulator
{
func populate(data:AnyObject)
}
Item class:
class Item: JSONPopulator
{
var condition:Condition?
func getCondition() ->Condition
{
return condition!
}
func populate(data: AnyObject)
{
condition = Condition()
condition?.populate(data)
}
}
Condition class:
class Condition:JSONPopulator
{
var arubaTemp:String?
var channel:NSDictionary!
func getArubaTemp()->String
{
return arubaTemp!
}
func getBonaireTemp() ->String
{
return bonaireTemp!
}
func getCuracaoTemp()->String
{
return curacaoTemp!
}
func populate(data: AnyObject)
{
if let query = data["query"] as? NSDictionary
{
if let results = query["results"] as? NSDictionary
{
if let channel = results["channel"] as? NSDictionary
{
self.channel = channel
if let location = channel["location"] as? NSDictionary
{
if let city = location["city"] as? String
{
if city.containsString("Oranjestad")
{
switch city
{
case "Oranjestad":
arubaTemp = getTemp()
print(arubaTemp)
default:
break
}
}
}
}
}
}
}
}
func getTemp() ->String
{
var temp:String?
if let item = self.channel["item"] as? NSDictionary
{
if let condition = item["condition"] as? NSDictionary
{
if let tempeture = condition["temp"] as? String
{
print(tempeture)
temp = tempeture
}
}
}
print(temp)
return temp!
}
}
Channel class:
class Channel: JSONPopulator
{
var item:Item?
var unit:Unit?
var request_city:String?
func setRequestCity(request_city:String)
{
self.request_city = request_city
}
func getRequestCity() ->String
{
return request_city!
}
func getItem() -> Item
{
return item!
}
func getUnit() -> Unit
{
return unit!
}
func populate(data: AnyObject)
{
item = Item()
item?.populate(data)
}
}
The WeatherService class that handles the function of parsing the JSON object. This class implement a WeatherServiceCallBack protocol.
The WeatherServiceCallBack protocol:
protocol WeatherServiceCallBack
{
func arubaWeatherServiceService( channel:Channel)
func arubaWeatherServiceFailure()
}
WeatherService class:
class WeatherService
{
var weatherServiceCallBack:WeatherServiceCallBack
var requestCity:String?
init(weatherServiceCallBack: WeatherServiceCallBack)
{
self.weatherServiceCallBack = weatherServiceCallBack
}
internal func checkCity(city:String)
{
switch (city)
{
case "Oranjestad,AW":
requestCity = city
getWeatherData(requestCity!)
default:
break
}
}
func getWeatherData(urlString:String)
{
let url = NSURL(string: urlString)!
let sqlQuery = "select * from weather.forecast where woeid in (select woeid from geo.places(1) where text=\"\(url)\")"
let endpoint = "https://query.yahooapis.com/v1/public/yql?q=\(sqlQuery)&format=json"
let testString = (String(endpoint)
executeTask(testString)
}
func executeTask(request_data: String)
{
let requestString:NSString = request_data.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
let url_with_data = NSURL(string: requestString as String)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url_with_data){
(data, response, error) in dispatch_async(dispatch_get_main_queue(), {
if data == nil
{
print("Failed loading HTTP link")
}else{
self.onPost(data!)
}
})
}
task.resume()
}
func onPost(data:NSData)
{
enum JSONErrors: ErrorType
{
case UserError
case jsonError
}
do{
let jsonResults = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
print(jsonResults)
if let city = jsonResults["query"] as? NSDictionary
{
if let name = city["count"] as? Int
{
if name == 0
{
weatherServiceCallBack.arubaWeatherServiceFailure()
}
}
}
if let requestCity_check = jsonResults["query"] as? NSDictionary
{
if let results = requestCity_check["results"] as? NSDictionary
{
if let channel = results["channel"] as? NSDictionary
{
if let location = channel["location"] as? NSDictionary
{
if let city = location["city"] as? String
{
requestCity = city
let channel = Channel()
channel.setRequestCity(requestCity!)
channel.populate(jsonResults)
weatherServiceCallBack.arubaWeatherServiceService(channel)
}
}
}
}
}
}catch {
print("Failed to load JSON Object")
}
}
}
In the ViewController class (I add some animation to the UILabel so it can flip from Fahrenheit to Celsius):
class ViewController: UIViewController, WeatherServiceCallBack
{
var weather:WeatherService?
var aua_Tempeture_in_F:String?
var aua_Tempeture_in_C:String?
var timer = NSTimer()
#IBOutlet var aua_Temp_Label: UILabel!
let animationDuration: NSTimeInterval = 0.35
let switchingInterval: NSTimeInterval = 5 //10
override func viewDidLoad() {
super.viewDidLoad()
weather = WeatherService(weatherServiceCallBack: self)
weather?.checkCity("Oranjestad,AW")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func animateTemptext()
{
self.timer = NSTimer.scheduledTimerWithTimeInterval(7.0, target: self, selector: Selector("tempConvertionTextSwitch"), userInfo: nil, repeats: true)
}
func setTempinCelsius(temp_string:String)
{
aua_Tempeture_in_F = "\(temp_string)°F"
let convertedString = convertFahrenheittoCelsius(temp_string)
aua_Tempeture_in_C = "\(convertedString)°C"
aua_Temp_Label.text = aua_Tempeture_in_C
animateTemptext()
}
func convertFahrenheittoCelsius(currentTemp:String) ->String
{
let tempTocelsius = (String(((Int(currentTemp)! - 32) * 5)/9))
return tempTocelsius
}
#objc func tempConvertionTextSwitch()
{
CATransaction.begin()
CATransaction.setAnimationDuration(animationDuration)
CATransaction.setCompletionBlock{
let delay = dispatch_time(DISPATCH_TIME_NOW,Int64(self.switchingInterval * NSTimeInterval(NSEC_PER_SEC)))
dispatch_after(delay, dispatch_get_main_queue())
{
}
}
let transition = CATransition()
transition.type = kCATransitionFade
if aua_Temp_Label.text == aua_Tempeture_in_F
{
aua_Temp_Label.text = aua_Tempeture_in_C
}else if aua_Temp_Label.text == aua_Tempeture_in_C
{
aua_Temp_Label.text = aua_Tempeture_in_F
}else if aua_Temp_Label == ""
{
aua_Temp_Label.text = aua_Tempeture_in_C
}
aua_Temp_Label.layer.addAnimation(transition, forKey: kCATransition)
CATransaction.commit()
}
func arubaWeatherServiceFailure() {
}
func arubaWeatherServiceService(channel: Channel)
{
let requested_city = channel.getRequestCity()
let items = channel.getItem()
let aua_Temp = items.getCondition().getArubaTemp()
setTempinCelsius(aua_Temp)
}
}
Reference:
iOS 8 Swift Programming Cookbook Solutions Examples for iOS Apps book
iOS 8 Programming Fundamentals with Swift Swift, Xcode, and Cocoa Basics book
Hope it help the once that had the same problem
Related
I have run into a problem where I can save and load into and from CoreData in Swift for my iOS app, but I run into a problem where I have tried to guard for duplicate entries, but it does not seem to work. can anyone tell me where I went wrong? Thanks!
My ViewController class:
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet weak var headerLabel:UILabel!
#IBOutlet weak var myTableView: UITableView!
var lenders = [LenderData]()
var lendersTemp = [LenderData]()
override func viewDidLoad() {
super.viewDidLoad()
self.myTableView.rowHeight = 90
myTableView.delegate = self
myTableView.dataSource = self
let fetchRequest: NSFetchRequest<LenderData> = LenderData.fetchRequest()
do {
let lenders = try PersistenceService.context.fetch(fetchRequest)
self.lenders = lenders
} catch {
// Who cares....
}
downloadJSON {
for tempLender in self.lendersTemp {
if !self.lenders.contains(where: {$0.id == tempLender.id}) {
self.lenders.append(tempLender)
}
}
self.lendersTemp.removeAll()
PersistenceService.saveContext()
self.myTableView.reloadData()
}
}
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://api.kivaws.org/v1/loans/newest.json")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("JSON not downloaded")
} else {
if let content = data {
do {
let myJSONData = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
var imageID:Int64 = -1
var country:String = "N/A"
var latLongPair:String = "0.000000 0.000000"
var town:String = "N/A"
if let loans = myJSONData["loans"] as? NSArray {
for i in 0...loans.count-1 {
if let lender = loans[i] as? NSDictionary {
if let imageData = lender["image"] as? NSDictionary { imageID = imageData["id"] as! Int64 }
if let countryData = lender["location"] as? NSDictionary {
country = countryData["country"] as! String
town = countryData["town"] as! String
if let geo = countryData["geo"] as? NSDictionary {
latLongPair = geo["pairs"] as! String
}
}
let newLender = LenderData(context: PersistenceService.context)
newLender.id = lender["id"] as! Int64
newLender.name = lender["name"] as? String
newLender.image_id = imageID
newLender.activity = lender["activity"] as? String
newLender.use = lender["use"] as? String
newLender.loan_amount = lender["loan_amount"] as! Int32
newLender.funded_amount = lender["funded_amount"] as! Int32
newLender.country = country
newLender.town = town
newLender.geo_pairs = latLongPair
self.lendersTemp.append(newLender)
}
}
}
DispatchQueue.main.async {
completed()
}
} catch {
print("Error occured \(error)")
}
}
}
}
task.resume()
}
}
EDIT
Added the part of the code where I populate the lendersTemp array
I quote matt on this one from the comments:
So... You are appending to self.lendersTemp on a background thread but reading it on the main thread. Instead, get rid of it and just pass the data right thru the completed function.
Which is exactly what I did. And this worked
I called data from the web for infinite-scrolling and "push to refresh", now infinite-scrolling work well, but "Pull to refresh" is not work because I do not know what kind of code I have to write in refreshAction func.
NetworkRequestAPI Request
import Foundation
class NetworkRequestAPI {
static func getPropductListByCategory(productId : Int, pageNo : Int , completion: #escaping ([Product]? , Error?) -> ()){
let url = URL(string: Configuration.BASE_URL+"/product-by/type?product_type_id="+String(productId)+"&page="+String(pageNo))
var categoryObject = [Product]()
URLSession.shared.dataTask(with:url!) { (urlContent, response, error) in
if error != nil {
}
else {
do {
let json = try JSONSerialization.jsonObject(with: urlContent!) as! [String:Any]
let products = json["products"] as? [String: Any]
// productCount = (json["product_count"] as? Int)!
let items = products?["data"] as? [[String:Any]]
items?.forEach { item in
let oProduct = Product()
oProduct.product_id = item["product_id"] as? Int
oProduct.product_name = item["product_name"] as? String
oProduct.product_image = item["product_image"] as? String
let ratingItem = item["rating_info"] as? [String: AnyObject]
let rating = RatingInfo()
rating.final_rating = ratingItem?["final_rating"] as? String
oProduct.rating_info = rating
categoryObject.append(oProduct)
}
completion(categoryObject, nil)
} catch let error as NSError {
print(error)
completion(nil, error)
}
}
}.resume()
}
}
ListTableView Class
class ListTableView: UITableViewController {
var isInitUILoad = true
var arrProduct = [[Product]]()
var product_id:Int = 0
var pageNo = 1
override func viewDidLoad() {
super.viewDidLoad()
self.initUILoad()
let refreshControl = UIRefreshControl()
if #available(iOS 10.0, *) {
tableView.refreshControl = refreshControl
} else {
tableView.addSubview(refreshControl)
}
refreshControl.addTarget(self, action: #selector(refreshWeatherData(_:)), for: .valueChanged)
}
#objc private func refreshWeatherData(_ sender: Any) {
//
// what codes have to add here
//
self.refreshControl?.endRefreshing()
}
func initUILoad(){
ActivityIndicator.customActivityIndicatory(self.view, startAnimate: true)
NetworkRequestAPI.getPropductListByCategory(productId: product_id, pageNo: pageNo) { (products, error) in
DispatchQueue.main.async(execute: {
if products != nil {
// self.totalItemLabel.text = String(self.product_count) + " products"
self.pageNo += 1
// self.product_count = productCount
//self.arrProduct = products!
self.arrProduct.append(products!)
print(self.arrProduct.count)
// self.tableView?.reloadData()
}else{
print(error.debugDescription)
}
ActivityIndicator.customActivityIndicatory(self.view, startAnimate: false)
// self.totalItemLabel.text = String(self.product_count) + " products"
self.tableView?.reloadData()
self.isInitUILoad = false
})
}
}
func loadMore(complition:#escaping (Bool) -> ()) {
NetworkRequestAPI.getPropductListByCategory(productId: product_id, pageNo: pageNo) { (products, error) in
DispatchQueue.main.async(execute: {
if error != nil{
print(error.debugDescription)
}
if products != nil && products?.count ?? 0 > 0{
// self.totalItemLabel.text = String(self.product_count) + " products"
self.pageNo += 1
// self.product_count = productCount
// self.arrProduct = products!
self.arrProduct.append(products!)
self.tableView?.insertSections([self.arrProduct.count - 1], with: .fade)
// self.tableView?.reloadData()
}else{
print("no product left")
}
complition(true)
})
}
}
}
#objc private func refreshWeatherData(_ sender: Any) {
NetworkRequestAPI.getPropductListByCategory(productId: product_id, pageNo: 1) { (products, error) in
DispatchQueue.main.async(execute: {
if products != nil {
// self.totalItemLabel.text = String(self.product_count) + " products"
self.arrProduct = products!
print(self.arrProduct.count)
}else{
print(error.debugDescription)
}
self.refreshControl?.endRefreshing()
// self.totalItemLabel.text = String(self.product_count) + " products"
self.tableView?.reloadData()
self.isInitUILoad = false
})
}
}
I'm still new in iOS development. The idea is I want to put images values from banner (data model). So I need to called func getAllBanner to get all values. Then only called func turnToPage.
The problem is let controller = controllers[index] is
fatal error: Index out of range
When I debug, I noticed that lazy var controllers: [UIViewController]. Why is that ?
Any help is really appreciated.
var images: [UIImage]?
var banner: [Banner]?
// Variable that not create onDidLoad
lazy var controllers: [UIViewController] = {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var controllers = [UIViewController]()
if let images = self.images {
for image in images {
let sliderImageVC = storyboard.instantiateViewController(withIdentifier: Storyboard.sliderImageViewController)
controllers.append(sliderImageVC)
}
}
self.pageViewControllerDelegate?.setupPageController(numberOfPages: controllers.count)
return controllers
}()
override func viewDidLoad() {
super.viewDidLoad()
automaticallyAdjustsScrollViewInsets = false
dataSource = self
delegate = self
self.getAllBanner(strAppID: "1", locationid: "2")
self.turnToPage(index: 0)
}
// To allow to turn page
func turnToPage(index: Int) {
let controller = controllers[index]
var direction = UIPageViewControllerNavigationDirection.forward
if let currentVC = viewControllers?.first {
let currentIndex = controllers.index(of: currentVC)
if currentIndex! > index {
direction = .reverse
}
}
self.configureViewDisplaying(viewController: controller)
setViewControllers([controller], direction: direction, animated: true, completion: nil)
}
// Get Banner
func getAllBanner(strAppID: String, locationid: String) {
Banner.getBanner(strAppID: strAppID, locationID: locationid) { [weak self] banner in
guard let `self` = self else {
return
}
self.banner = banner
// Update UI here
if let banners = self.banner {
for banner in banners {
let endPoint = URL(string: banner.ImageURL)
do {
let data = try Data(contentsOf: endPoint!)
self.images = [UIImage(data: data)!]
} catch {
print("Error")
}
}
}
}
}
You can use this snippet.
func getAllBanner(strAppID: String, locationid: String) {
Banner.getBanner(strAppID: strAppID, locationID: locationid) { [weak self] banner in
guard let `self` = self else {
return
}
self.banner = banner
// Update UI here
if let banners = self.banner {
var images: [UIImage] = []
for banner in banners {
let endPoint = URL(string: banner.ImageURL)
do {
let data = try Data(contentsOf: endPoint!)
images.append(UIImage(data: data)!)
} catch {
print("Error")
}
}
self.images = images
// call turnpage method here
}
}
}
So I have created a model to download data (images, title, desc) using alamofire. But having problem to pass the data and update it in the viewController. If I put the functions in viewController's viewDidLoad then it is working very fine. But I want to use the MVC model. Here is the code for the model:
class PageControllerView {
var _titleName : String!
var _titleDesc : String!
var _image : UIImage!
var titleName : String {
if _titleName == nil {
_titleName = ""
print("titlename is nil")
}
return _titleName
}
var titleDesc : String {
if _titleDesc == nil {
print("tittledesc is nile")
_titleDesc = ""
}
return _titleDesc
}
var image : UIImage {
if _image == nil {
print("Image view is nill")
_image = UIImage(named: "q")
}
return _image
}
func getPageControllerData(_ page : Int) {
Alamofire.request("\(BASE_URL)movie/now_playing?api_key=\(API_KEY)&language=en-US&page=1").responseJSON { (response) in
if let JSON = response.result.value {
if let json = JSON as? Dictionary<String, Any> {
if let results = json["results"] as? [Dictionary<String, Any>] {
if let overview = results[page]["overview"] as? String {
self._titleDesc = overview
}
if let releaseDate = results[page]["release_date"] as? String {
if let title = results[page]["title"] as? String {
let index = releaseDate.index(releaseDate.startIndex, offsetBy: 4)
self._titleName = "\(title) (\(releaseDate.substring(to: index)))"
}
}
if let image_url = results[page]["poster_path"] as? String{
Alamofire.request("\(BASE_URL_IMAGE)\(IMAGE_SIZE)\(image_url)").downloadProgress(closure: { (progress) in
print(progress.fractionCompleted)
}).responseData(completionHandler: { (response) in
print("completed downloading")
if let imageData = response.result.value {
self._image = UIImage(data: imageData)
}
})
}
}
}
}
}
}
}
And this is the viewControllers code (It is working fine but i want to pass the model. The alamofirefuntion is also present in the viewcontroller):
override func viewDidLoad() {
super.viewDidLoad()
print("viewdidload")
getPageControllerData(13)
self.updatePageControllerUI()
}
func updatePageControllerUI() {
pageControllerMovieLabel.text = pageControllerView.titleName
pageControllerSubLabel.text = pageControllerView.titleDesc
pageControlImageView.image = pageControllerView.image
}
func getPageControllerData(_ page : Int) {
Alamofire.request("\(BASE_URL)movie/now_playing?api_key=\(API_KEY)&language=en-US&page=1").responseJSON { (response) in
if let JSON = response.result.value {
if let json = JSON as? Dictionary<String, Any> {
if let results = json["results"] as? [Dictionary<String, Any>] {
if let overview = results[page]["overview"] as? String {
self.pageControllerSubLabel.text = overview
}
if let releaseDate = results[page]["release_date"] as? String {
if let title = results[page]["title"] as? String {
let index = releaseDate.index(releaseDate.startIndex, offsetBy: 4)
self.pageControllerMovieLabel.text = "\(title) (\(releaseDate.substring(to: index)))"
}
}
if let image_url = results[page]["poster_path"] as? String{
Alamofire.request("\(BASE_URL_IMAGE)\(IMAGE_SIZE)\(image_url)").downloadProgress(closure: { (progress) in
print(progress.fractionCompleted)
}).responseData(completionHandler: { (response) in
if let imageData = response.result.value {
self.pageControlImageView.image = UIImage(data: imageData)
}
})
}
}
}
}
}
}
My question is how to pass the model so that i can use like this, by using the PageControllerView object.
override func viewDidLoad() {
super.viewDidLoad()
print("viewdidload")
pageControllerView.getPageControllerData(13)
self.updatePageControllerUI()
}
Now I have checked that this code works but the image is still not shown at firstgo since it has not been downloaded completely but the title and description is showing.
override func viewDidLoad() {
super.viewDidLoad()
print("viewdidload")
pageControllerView.getPageControllerData(4)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.updatePageControllerUI()
}
How do automatically stop activity indicator after the refresh button is pressed and loading of content is done in UITableview JSON
I already have to the code to start the spinning and the button
Here is full code
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var json_data_url = "http://www.howtotechworld.com/json_table_view_images%20(1).json"
var image_base_url = ""
var isProgressShowing = true;
var TableData:Array< datastruct > = Array < datastruct >()
enum ErrorHandler:ErrorType
{
case ErrorFetchingResults
}
struct datastruct
{
var imageurl:String?
var description:String?
var image:UIImage? = nil
init(add: NSDictionary)
{
imageurl = add["url"] as? String
description = add["description"] as? String
}
}
#IBOutlet var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
tableview.delegate = self
get_data_from_url(json_data_url)
}
override func viewWillAppear(animated: Bool) {
let barButtonItem = UIBarButtonItem(title: "Refresh", style: .Plain, target: self, action: "refreshTapped");
self.navigationItem.rightBarButtonItem = barButtonItem;
}
func refreshTapped() {
addProgressIndicator(isProgressShowing);
get_data_from_url(json_data_url)
}
func addProgressIndicator(show : Bool) {
isProgressShowing = !show;
if(show) {
let myActivityIndicator = UIActivityIndicatorView(activityIndicatorStyle:UIActivityIndicatorViewStyle.Gray)
myActivityIndicator.startAnimating()
let barButtonItem = UIBarButtonItem(customView: myActivityIndicator)
self.navigationItem.rightBarButtonItem = barButtonItem
} else {
self.navigationItem.rightBarButtonItem = nil;
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
let data = TableData[indexPath.row]
cell.textLabel?.text = data.description
if (data.image == nil)
{
cell.imageView?.image = UIImage(named:"image.jpg")
load_image(image_base_url + data.imageurl!, imageview: cell.imageView!, index: indexPath.row)
}
else
{
cell.imageView?.image = TableData[indexPath.row].image
}
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return TableData.count
}
func get_data_from_url(url:String)
{
let url:NSURL = NSURL(string: url)!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let task = session.dataTaskWithRequest(request) {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
dispatch_async(dispatch_get_main_queue(), {
self.extract_json(data!)
return
})
}
task.resume()
}
func extract_json(jsonData:NSData)
{
let json: AnyObject?
do {
json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
} catch {
json = nil
return
}
if let list = json as? NSArray
{
for (var i = 0; i < list.count ; i++ )
{
if let data_block = list[i] as? NSDictionary
{
TableData.append(datastruct(add: data_block))
}
}
do
{
try read()
}
catch
{
}
do_table_refresh()
}
}
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.tableview.reloadData()
return
})
}
func load_image(urlString:String, imageview:UIImageView, index:NSInteger)
{
let url:NSURL = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
let task = session.downloadTaskWithURL(url) {
(
let location, let response, let error) in
guard let _:NSURL = location, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let imageData = NSData(contentsOfURL: location!)
dispatch_async(dispatch_get_main_queue(), {
self.TableData[index].image = UIImage(data: imageData!)
self.save(index,image: self.TableData[index].image!)
imageview.image = self.TableData[index].image
return
})
}
task.resume()
}
func read() throws
{
do
{
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName: "Images")
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest)
for (var i=0; i < fetchedResults.count; i++)
{
let single_result = fetchedResults[i]
let index = single_result.valueForKey("index") as! NSInteger
let img: NSData? = single_result.valueForKey("image") as? NSData
TableData[index].image = UIImage(data: img!)
}
}
catch
{
print("error")
throw ErrorHandler.ErrorFetchingResults
}
}
func save(id:Int,image:UIImage)
{
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let entity = NSEntityDescription.entityForName("Images",
inManagedObjectContext: managedContext)
let options = NSManagedObject(entity: entity!,
insertIntoManagedObjectContext:managedContext)
let newImageData = UIImageJPEGRepresentation(image,1)
options.setValue(id, forKey: "index")
options.setValue(newImageData, forKey: "image")
do {
try managedContext.save()
} catch
{
print("error")
}
}
}
You will need to keep a reference to myActivityIndicator and stop it in do_table_refresh() as shown below.
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.tableview.reloadData()
myActivityIndicator.stopAnimating()
return
})
}
Edit: Just had a quick thought after looking at your code in the question.
If you would like to hide myActivitIndicator using addProgressIndicator(isProgressShowing) , you will have to make sure that isProgressShowing is set to FALSE, then in the code that I have given above, replace myActivityIndicator.stopAnimating() with addProgressIndicator(isProgressShowing).
You will also need to update the addProgressIndicator function and add myActivityIndicator.stopAnimating() to the else condition.
If I understand your question right then I think below is what you want.
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath row] == ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row){
//end of loading
//for example [activityIndicator stopAnimating];
}
}
and for swift it is like this
func cellForRowAtIndexPath(_ indexPath: NSIndexPath) -> UITableViewCell?{
if(indexPath.row = tableView.indexPathsForVisibleRows().lastObject().row{
//end of loading
//for example [activityIndicator stopAnimating];
}
return yourCell;
}
I don't know swift much, but i guess you will have got the idea how to do it.
Add this code after do_table_refresh(): addProgressIndicator(!isProgressShowing);