Sorry to do this again, but my code has yet ANOTHER problem. When I run the program in the simulator, if I move the slider from its default position and hit the submit button, it will calculate the mpg. However, if I leave the slider at its default position of 500, then choose how many gallons I used (like normal), and hit "Submit", I get a value of 0.0 for the mpg. I know that it isn't right. My instructor suggested that I put the values that were originally in the milesDrivenSliderValueChanged function and put them in their own function. When I did, I got a bunch of errors. I fixed them, but I still get the same results. I would like to know what I am doing wrong.
Here is my code for the slider and the new function it calls:
class SecondViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate
{
var BlopSoundURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Blop", ofType: "mp3")!)
var soundAudioPlayer = AVAudioPlayer()
var miles = 0.0
var gallonsUsed = 0.0
var mpg: Double = 0.0
var currentSelection: Double?
#IBOutlet weak var milesDrivenLabel: UILabel!
#IBOutlet weak var milesDrivenSlider: UISlider!
#IBAction func milesDrivenSliderValueChanged(sender: UISlider)
{
currentSelection = Double(sender.value)
sliderMoved()
}
func sliderMoved()
{
milesDrivenLabel.text = "\(round(currentSelection! * 100) / 100)"
println("Current Selection = \(currentSelection)")
miles = currentSelection!
}
The rest of the code is for the Picker (which I don't think has any problems), the submit button, and the other functions:
#IBOutlet weak var gallonsUsedPicker: UIPickerView!
let pickerData = [1,2,3,4,5,6,7,8,9,10,11,
12,13,14,15,16,17,18,19,20,21,
22,23,24,25,26,27,28,29,30,31,
32,33,34,35,36,37,38,39,40,41,
42,43,44,45,46,47,48,49,50,51,
52,53,54,55,56,57,58,59,60,61,
62,63,64,65,66,67,68,69,70,71,
72,73,74,75,76,77,78,79,80,81,
82,83,84,85,86,87,88,89,90,91,
92,93,94,95,96,97,98,99,100]
#IBAction func submitButton(sender: UIButton)
{
soundAudioPlayer.play()
let rowValue = gallonsUsedPicker.selectedRowInComponent(0) + 1
gallonsUsed = Double(rowValue)
mpg = Double(round((miles * 100) / gallonsUsed) / 100)
let message = "Your MPG is approximately: \(mpg)."
let alert = UIAlertController(
title: "Your MPG",
message: message,
preferredStyle: .Alert)
let action = UIAlertAction(
title: "Ok",
style: .Default,
handler: nil)
alert.addAction(action)
presentViewController(alert, animated: true, completion: nil)
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
return "\(row+1)"
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 100
}
override func viewDidLoad()
{
super.viewDidLoad()
gallonsUsedPicker.dataSource = self
gallonsUsedPicker.delegate = self
soundAudioPlayer = AVAudioPlayer(contentsOfURL: BlopSoundURL, error: nil)
// Do any additional setup after loading the view, typically from a nib.
} // End of viewDidLoad
This is really frustrating. It's probably a simple fix, but I just can't see it. I spent 3 hours on this last night and 2 more hours just this morning. Any help would be appreciated. Thank you for your time and efforts.
I'm a doofus. I just had to change miles to 500, instead of zero. Now it works. Sorry, guys.
Related
I am able to parse JSON and adding cells in Collectionview.. but if i move from this Viewcontroller and coming to viewcontroller then collectionview is not showing.. but added data in JSON
code for adding collectionview and JSON parsing:
class ImageItemModel{
var title: String?
var profileImage: UIImage?
var pic_id: Double?
init(title: String?, imgTitle: UIImage?, pic_id: Double?) {
self.title = title
self.profileImage = imgTitle
self.pic_id = pic_id
}
}
class EditProfileImageViewController: UIViewController {
#IBOutlet weak var titleTextfield: UITextField!
private var imageProfile : UIImage?
private var imagePicker : EasyImagePicker?
#IBOutlet weak var collectionView: UICollectionView!
var arrImageItems = [ImageItemModel]()
#IBAction func imgtitleSaveBtn(_ sender: Any) {
postServiceCall()
}
fileprivate func postServiceCall(){
if titleTextfield.text?.trim() == ""{
return self.view.makeToast("please add service title")
}
let parameters = ["image_title" : titleTextfield.text?.trim() ?? ""]
APIReqeustManager.sharedInstance.uploadMultipartFormData(param: parameters, url: CommonUrl.edit_profile_images, image: imageProfile, fileName: "image", vc: self, isHeaderNeeded: true) {(responseData) in
print("edit profile result \(responseData)")
if let result = responseData.dict?["result"] as? NSDictionary{
let success = result["status"] as? [String : Any]
let message = success?["message"] as? String
if message == "Success"{
let image = result["image"] as? [String : Any]
let picId = image?["id"]
self.arrImageItems.append(ImageItemModel(title: self.titleTextfield.text, imgTitle: self.imageProfile, pic_id: picId as! Double))
self.collectionView.reloadData()
}
else{
self.view.makeToast(CommonMessages.somethingWentWrong)
}
}
}
}
extension EditProfileImageViewController : UICollectionViewDelegate,UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrImageItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCollectionViewCell", for: indexPath) as! ImageCollectionViewCell
cell.imgView.image = arrImageItems[indexPath.item].profileImage
cell.lblTitle.text = arrImageItems[indexPath.row].title
cell.deleteButton.tag = indexPath.row
cell.deleteButton.addTarget(self, action: #selector(deleteService(sender:)), for: UIControl.Event.touchUpInside)
return cell
}
}
with the above code i am able to add collectionview cells and able to store data in JSON but.. if i move from this viewcontroller and coming back to this viewcontroller then collectionview is not showing, why? whats wrong? please do help me with code.. i got stuck here from long time.
There are couple of issues that you should fix for this to work properly. I will give you reason for each.-
You are loading your data with the postServiceCall() method which has an asynchronous network call. There is no way to know when the controller is done fetching the data to the arrImageItems array. So, you should have used a completion handler.
Now you are updating the collectionView within the asynchronous dataTask which a background thread. BIG mistake. Whenever you have any UI related task, you do it under the main thread. So, you could refactor the APIReqeustManager.sharedInstance.uploadMultipartFormData() part of your code following way-
APIReqeustManager.sharedInstance.uploadMultipartFormData(param: parameters, url: CommonUrl.edit_profile_images, image: imageProfile, fileName: "image", vc: self, isHeaderNeeded: true) {(responseData) in
print("edit profile result \(responseData)")
if let result = responseData.dict?["result"] as? NSDictionary{
let success = result["status"] as? [String : Any]
let message = success?["message"] as? String
if message == "Success"{
let image = result["image"] as? [String : Any]
let picId = image?["id"]
self.arrImageItems.append(ImageItemModel(title: self.titleTextfield.text, imgTitle: self.imageProfile, pic_id: picId as! Double))
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
else{
DispatchQueue.main.async {
self.view.makeToast(CommonMessages.somethingWentWrong)
}
}
}
}
Now unless you want your viewcontroller to show the data in your collectionView only when the action, imgtitleSaveBtn(_:) is triggered, you need to get data everytime, when your view controller appeared on screen. To fix that issue, you should get the data in the viewWillAppear(_:) method like-
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
postServiceCall()
}
Now, the above two edits should fix your issue depending on how you want to load your collectionview but your code is breaking quite some coding standards. Coding standards sounds like a clique but trust me you want to follow those if you ever want to update the capability of your app without breaking it. The following is just some hints-
Whenever you are in an asynchronous call, you should consider calling a completion handler for returning your data.
Should look into your methods, you are dangerously breaking the single responsibility principal.
In more than one place, you force unwrapped. Bad idea. You need your system to have a fail safe rather than just crashing on you.
Update 2:
Updates with a design pattern:
Compartmentalise your code in MVC pattern. Put the ImageItemModel class in its own file. See the image below to understand the design-
Customize the collectionViewCell within the ImageCollectionViewCell. Let's assume your custom cell has only the outlets.
class ImageCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imgView: UIImageView!
#IBOutlet weak var lblTitle: UILabel!
#IBOutlet weak var deleteButton: UIButton!{
didSet{
deleteButton.addTarget(self, action: #selector(deleteService(_:)), for: .touchUpInside)
}
}
// however this could easily be done with IBAction
#objc func deleteService(_ sender: UIButton){
}
}
Update the postServiceCall and return the data to your controller with a completion handler, means when the postServiceCall is done executing, an array of images or an empty array should be returned based on success or failure. Then the controller can decide what to do with the data, in your case update UI. with couple of refactoring, here is the updated controller code.
import UIKit
import EasyImagePicker
class EditProfileImageViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!{ //for troubleshooting
didSet{ //purpose, do it from code
collectionView.delegate = self
collectionView.dataSource = self
}
}
#IBOutlet weak var titleTextfield: UITextField!
private var imageProfile : UIImage?
private var imagePicker : EasyImagePicker? // you never used this var.
var arrImageItems = [ImageItemModel]()
// any time a view controller appears on screen this method gets called.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
reloadMyCollectionView()
}
#IBAction func imgtitleSaveBtn(_ sender: Any) {
reloadMyCollectionView()
}
fileprivate func reloadMyCollectionView(){
postServiceCall{ images in
self.arrImageItems = images
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
// look into escaping closures to understand, why you need it here
fileprivate func postServiceCall(completed: #escaping(_ images: [ImageItemModel])->Void){
// don't force unwrap, get optional values safely with guard let
guard let titleText = titleTextfield.text, titleText == "" else{
return
}
let parameters = ["image_title" : titleText]
APIReqeustManager.sharedInstance.uploadMultipartFormData(param: parameters, url: CommonUrl.edit_profile_images, image: imageProfile, fileName: "image", vc: self, isHeaderNeeded: true) {(responseData) in
print("edit profile result \(responseData)")
//capture the data in local scope and return that array with a completion handler
var imageItems = []
if let result = responseData.dict?["result"] as? NSDictionary{
let success = result["status"] as? [String : Any]
let message = success?["message"] as? String
if message == "Success"{
let image = result["image"] as? [String : Any]
let picId = image?["id"]
imageItems.append(ImageItemModel(title: self.titleTextfield.text, imgTitle: self.imageProfile, pic_id: picId as! Double))
}
}
self.completed(imageItems) // if there is nothing in result,
//imageItems will be empty, otherwise it will have imageItemModel data
}
}
}
extension EditProfileImageViewController : UICollectionViewDelegate,UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrImageItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCollectionViewCell", for: indexPath) as? ImageCollectionViewCell{
cell.imgView.image = arrImageItems[indexPath.item].profileImage
cell.lblTitle.text = arrImageItems[indexPath.row].title
cell.deleteButton.tag = indexPath.row
//cell.deleteButton.addTarget(self, action: #selector(deleteService(sender:)), for: UIControl.Event.touchUpInside)
return cell
}
else{
return UICollectionViewCell()
}
}
}
Notice postServiceCall and cellForItemAt methods.
If you still have the same issue then you need to show your whole code to get any further help.
I am building an iOS app as a new interface to our club website.
Since I'm fairly new to this I watched several videos and looked on various websites for help. However, populating username, and password still does not happen:
Clicking the Sign In button works.
This is the login website: https://leamingtonst.aspsystems.co.uk/bookitasp/bookitasp.dll/cookielog
This is my code so far:
import UIKit
import WebKit
class ViewController: UIViewController {
#IBOutlet weak var usernameTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
let webView = WKWebView()
var counter = 0
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://leamingtons.aspsystems.co.uk/bookitasp/bookitasp.dll/cookielog")!
let request = URLRequest(url: url)
webView.frame = CGRect(x: 0, y: 350, width: 300, height: 400)
webView.load(request)
view.addSubview(webView)
}
#IBAction func submitTapped(_ sender: UIButton) {
print("Value: \(usernameTextField.text!)")
print("Value: \(passwordTextField.text!)")
switch counter {
case 0:
// Populate username and password
webView.evaluateJavaScript("document.getElementsByClass('username').value = '\(usernameTextField.text!)';", completionHandler: { (res, error) -> Void in
if res != nil {
print(res!)
} })
webView.evaluateJavaScript("document.getElementsByClass('password').value = '\(passwordTextField.text!)';", completionHandler: { (res, error) -> Void in
if res != nil {
print(res!)
} })
webView.evaluateJavaScript("document.getElementById('pabutton').click();", completionHandler: nil)
case 1: break
case 2: break
case 3: break
case 4: break
case 5: break
default: break
}
counter += 1
}
}
I am grateful for any suggestions or ideas.
I've seen that it's possible to use the frequency tracker in a function, but I can't pull it off myself. There's an example file that can display frequency, but I haven't found anything that calls a function when the tracker hits a certain frequency.
I'm trying to call the AKAudioPlayer.pause() function when the Frequency drops below 40hz. I edited a test .mp3 file so that there is a section of 32hz in the audio file. I can even see on the frequency tracker text, that the tracker is reading the 32 hz, but I don't have any success in getting it to pause, even when I write the function to be dependent on this text.
This is a modified version of the microphone tracker, for tracking an audio file.
I receive the error: "Unexpectedly found nil while unwrapping an Optional value" both when trying to form the function directly with the tracker.frequency value, and when using a variable, converted to a string based off of the tracker.frequency.
This leads me to believe that the tracker frequency is my problematic nil optional value and to the conclusion that, that's what brings the whole thing down in flames. But I can't figure out what to do about it. I know there is a function that works to display the frequency, but I can't recreate its success.
In the below code (mostly an Audiokit example file) I've tried to use the Tracker.Frequency to trigger the pause function:
import AudioKit
import AudioKitUI
import UIKit
class ViewController: UIViewController {
#IBOutlet private var frequencyLabel: UILabel!
#IBOutlet private var amplitudeLabel: UILabel!
#IBOutlet private var noteNameWithSharpsLabel: UILabel!
#IBOutlet private var noteNameWithFlatsLabel: UILabel!
#IBOutlet private var audioInputPlot: EZAudioPlot!
#IBAction func Play(_ sender: Any) {
if input.isStarted == false
{
input.play()
}
}
#IBAction func Pause(_ sender: Any)
{
if input.isStarted
{
input.pause()
}
}
var input: AKAudioPlayer!
var song = try! AKAudioFile(readFileName: "TEST.mp3")// ... the error is here
var tracker: AKFrequencyTracker!
var silence: AKBooster!
let noteFrequencies = [16.35, 17.32, 18.35, 19.45, 20.6, 21.83, 23.12, 24.5, 25.96, 27.5, 29.14, 30.87]
let noteNamesWithSharps = ["C", "C♯", "D", "D♯", "E", "F", "F♯", "G", "G♯", "A", "A♯", "B"]
let noteNamesWithFlats = ["C", "D♭", "D", "E♭", "E", "F", "G♭", "G", "A♭", "A", "B♭", "B"]
func setupPlot() {
let plot = AKNodeOutputPlot(input, frame: audioInputPlot.bounds)
plot.plotType = .rolling
plot.shouldFill = true
plot.shouldMirror = true
plot.color = UIColor.blue
audioInputPlot.addSubview(plot)
}
override func viewDidLoad() {
super.viewDidLoad()
pauseOnQueue()
AKSettings.audioInputEnabled = true
do {input = try AKAudioPlayer(file: song)}
catch {print("ERROR")}
tracker = AKFrequencyTracker(input)
silence = AKBooster(tracker, gain: 1)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
AudioKit.output = silence
AudioKit.start()
setupPlot()
Timer.scheduledTimer(timeInterval: 0.1,
target: self,
selector: #selector(ViewController.updateUI),
userInfo: nil,
repeats: true)
}
#objc func updateUI() {
if tracker.amplitude > 0.1 {
frequencyLabel.text = String(format: "%0.1f", tracker.frequency)
var frequency = Float(tracker.frequency)
while frequency > Float(noteFrequencies[noteFrequencies.count - 1]) {
frequency /= 2.0
}
while frequency < Float(noteFrequencies[0]) {
frequency *= 2.0
}
var minDistance: Float = 10_000.0
var index = 0
for i in 0..<noteFrequencies.count {
let distance = fabsf(Float(noteFrequencies[i]) - frequency)
if distance < minDistance {
index = i
minDistance = distance
}
}
let octave = Int(log2f(Float(tracker.frequency) / frequency))
noteNameWithSharpsLabel.text = "\(noteNamesWithSharps[index])\(octave)"
noteNameWithFlatsLabel.text = "\(noteNamesWithFlats[index])\(octave)"
}
amplitudeLabel.text = String(format: "%0.2f", tracker.amplitude)
}
func pauseOnQueue() {
frequencyLabel.text = String(format: "%0.1f", tracker.frequency)
let frequency = Float(tracker.frequency)
if frequency < 50 && frequency > 20 && input.isStarted == true
{ input.pause() }
}
}
I've answered a similar question here: AudioKit (iOS) - Add observer for frequency / amplitude change
There's a decision that you have to make about how you want to detect this frequency - is it something that happens fast and needs to be in the DSP code or can you just poll for frequency at semi-regular intervals from Swift.
I don't think you should add !s to the AudioKit node definitions because they might not exist at the time you're expecting them to even though it seems like it should from the view controller life cycle. In general, don't rely on view controller code to controller your audio. Perhaps put all the audio related stuff in a singleton and let it manage itself?
I have a function that returns parsed information out of a JSON string.
var info = [AppModel]()
func getEarthquakeInfo(completion: (results : NSArray?) ->Void ){
DataManager.getEarthquakeDataFromFileWithSuccess {
(data) -> Void in
let json = JSON(data: data)
if let JsonArray = json.array {
for appDict in JsonArray {
var ids: String? = appDict["id"].stringValue
var title: String? = appDict["title"].stringValue
var time: String? = appDict["time"].stringValue
var information = AppModel(idEarth: ids, title: title, time: time)
self.info.append(information)
completion(results: self.info)
}
}
}
}
This function uses SwiftyJSON and calls the web service using my DataManager class. When I print it out outside the function I get all the information I need. Now I want to use the title information to populate my TableView. Inside my cellForRowAtIndexPath I've tried filtering out my Earthinformation so that I could get just the title and put that into an array, and populate my tableView with that array. So far I've been unsuccessful, and I've been looking everywhere on how to do this and nothing I've tried or found worked. Can someone point me in the right direction on how to do this?
What I've done so far:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as UITableViewCell
getEarthquakeInfo( { (info) -> Void in
var Earthinformation = self.info as NSArray
let titleArray = Earthinformation["TITLE"] as NSArray // error: type int does not conform to protocol "StringLiteralConvertible"
cell.textLabel!.text = titleArray[indexPath.row]
})
return cell
}
3 records I get when I print out Earthinformation I get: ID: 146323, TITLE: M 1.6 - 27km E of Coso Junction, California, TIME: 2015-04-15 14:08:20 UTC,
, ID: 146346, TITLE: M 1.8 - 26km E of Coso Junction, California, TIME: 2015-04-15 14:08:20 UTC,
, ID: 146324, TITLE: M 2.4 - 26km NW of Anchor Point, Alaska, TIME: 2015-04-15 13:33:36 UTC,
Edit:
Sorry I should have included this before:
My AppModel.swift:
class AppModel: NSObject, Printable {
let idEarth: String
let title: String
let time: String
override var description: String {
return "ID: \(idEarth), TITLE: \(title), TIME: \(time), \n"
}
init(idEarth: String?, title: String?, time: String?) {
self.idEarth = idEarth ?? ""
self.title = title ?? ""
self.time = time ?? ""
}
}
And my DataManager.swift file:
let earthquakeURL = "http://www.kuakes.com/json/"
class DataManager {
class func getEarthquakeDataFromFileWithSuccess(success: ((websiteData: NSData) -> Void)) {
//1
loadDataFromURL(NSURL(string: earthquakeURL)!, completion:{(data, error) -> Void in
//2
if let urlData = data {
//3
success(websiteData: urlData)
}
})
}
class func loadDataFromURL(url: NSURL, completion:(data: NSData?, error: NSError?) -> Void) {
var session = NSURLSession.sharedSession()
// Use NSURLSession to get data from an NSURL
let loadDataTask = session.dataTaskWithURL(url, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if let responseError = error {
completion(data: nil, error: responseError)
} else if let httpResponse = response as? NSHTTPURLResponse {
if httpResponse.statusCode != 200 {
var statusError = NSError(domain:"com.kuakes", code:httpResponse.statusCode, userInfo:[NSLocalizedDescriptionKey : "HTTP status code has unexpected value."])
completion(data: nil, error: statusError)
} else {
completion(data: data, error: nil)
}
}
})
loadDataTask.resume()
}
}
You are calling your getEarthquakeInfo function every time a cell is to be created. This is very unnecessary and may cause unintentional behavior (seeing as the getEarthquakeInfo function is asynchronous). I recommend utilizing the fact that you have a model array info already to use to populate your tableview. In viewDidLoad call your asynchronous function to retrieve the data for the model array. Example:
override func viewDidLoad() {
super.viewDidLoad()
getEarthquakeInfo { (info) in
// Your model array is now populated so we should reload our data
self.tableView.reloadData()
}
}
Now, adjust your UITableViewDataSource functions to properly handle the model array. Note that I am assuming that your info array is a property of the UITableViewController that is populating the tableView. Example:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier",
forIndexPath: indexPath) as UITableViewCell
// Note that I have no idea how you access the title of your AppModel
// object so you may have to adjust the below code
cell.textLabel!.text = self.info[indexPath.row].title
return cell
}
override func numberOfSectionsInTableView() {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) {
return info.count
}
My Current View Controller is like this
import UIKit
import Alamofire
class ViewController: UIViewController , UIPickerViewDelegate, UIPickerViewDataSource{
#IBOutlet var venuePicker : UIPickerView?
var result = [String:String]()
var resultArray = [String]()
override func viewDidLoad() {
self.venuePicker?.delegate = self
Alamofire.request(.POST, "http://example.com/xxx/xx/xx").responseJSON() {
(request, response, jsonData, error) in
var venues = JSON(jsonData!)
let d = venues.dictionaryValue
for (k, v) in venues {
self.result[k] = v.arrayValue[0].stringValue
}
self.resultArray = self.result.values.array
self.venuePicker?.reloadAllComponents()
}
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return resultArray.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
return resultArray[row]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I don't know how to show value to UIPickerView from my json dictionary.i don't want key to show at UIPickerView.I am now stuck at "?????" statement.
This is my output for result
[C2517: ORIX Kobe Nyusatsu, C2510: NPS Sendai Nyusatsu, C2033: JU Gunma, C2053: KAA Kyoto, C2035: JU Ibaraki, C2077: USS Gunma, C2024: ISUZU Kobe, C2505: NAA Osaka Nyusatsu, C2529: SMAP Sapporo Nyusatsu, C2502: L-Up PKobeNyusatsu, C2005: ARAI Sendai, C2072: TAA Minami Kyushu]
Please help!!!
If you wanted to show data to a picker view, use declare 2 arrays as properties instead.
var resultKeys = [String]()
var resultValues = [String]()
Inside your viewDidLoad function
var venues = JSON(jsonData!)
let d = venues.dictionaryValue
for (k, v) in venues {
resultKeys.append(k)
resultValues.append(v)
}
venuePicker.dataSource = self
venuePicker.reloadAllComponents()
Then inside titleForRow
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
return resultValues[row]
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return resultValues.count
}
This way resultKeys[pickerView.selectedRowInComponent(0)] will return the key for the selected value.
Alamofire is totally async. It means that the block code will be executed when the data is ready. By then, the PickerView is already loaded, with an empty Array. That's the reason why the PickerView is empty. So in order to display the data we want, we need to force the PickerView to reload:
override func viewDidLoad() {
self.venuePicker?.delegate = self // This should be outside the block, my bad.
//...
Alamofire.request(...) {in
//...
venuePicker?.reloadAllComponents() // hopefully this will work
}
If you're not familliar with the concept of async programming, you can also choose to use sync way. Using this code I wrote https://github.com/skyline75489/swift-playground/blob/master/swift-playground%2FRequests.swift, we can easily send sync requests.
if let jsonData = Requests.get("YOUR_URL") {
let venues = JSON(jsonData)
//...
}
The reason why we have async is sometimes the data can be big and using sync way will cause the viewDidLoad to take forever to load. That's awful for user experience. However, if the data is small. Sync way can finish the task fast enough that users won't feel it. It's totally OK to use sync ways.