Justin Witham Justin Witham - 2 months ago 9
iOS Question

Making a TableView show values based on current selection in PickerView?

I'm trying to make an iOS app that displays a PickerView and a TableView. The PickerView contains the 50 U.S. states, and the TableView is supposed to show the current weather alerts for the selected state, based on an XML that the National Weather Service provides. The URL to the XML is https://alerts.weather.gov/cap/fl.php?x=0 for Florida, https://alerts.weather.gov/cap/ca.php?x=0 for California, etc. In other words, the URL for each state's XML varies based on the two-letter state abbreviation.

So far, I've gotten the app to correctly show all the 50 states in the PickerView, and when each state is selected, the weather alerts for that state do indeed appear in the TableView. However, once the TableView has been populated with the weather alert(s), they never go away after that. I want the TableView to only show the alerts for the state currently selected in the PickerView. As of right now, the weather alerts for each state just keep getting added to the TableView on top of each other. Also, if I go back to a state I selected earlier, the weather alerts will be added to the end of the TableView again as duplicates. I'm not sure how to make the TableView properly populate so that it only shows the alerts for each individual state at a time. Can anyone help me out?

Here's my code:

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITableViewDataSource, UITableViewDelegate, XMLParserDelegate {

@IBOutlet var statePicker: UIPickerView!
@IBOutlet var alertsTable: UITableView!


var titleFound:Bool = false
var descFound:Bool = false
var alerts:[String] = []
var exps:[String] = []
var abbv:String = ""
var parser = XMLParser()
var states = ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"]
var abbvsList = ["al", "ak", "az", "ar", "ca", "co", "ct", "de", "fl", "ga", "hi", "id", "il", "in", "io", "ks", "ky", "la", "me", "md", "ma", "mi", "mn", "ms", "mo", "mt", "ne", "nv", "nh", "nj", "nm", "ny", "nc", "nd", "oh", "ok", "or", "pa", "ri", "sc", "sd", "tn", "tx", "ut", "vt", "va", "wa", "wv", "wi", "wy"]

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int)->Int
{
return states.count
}

func pickerView(_ pickerView:UIPickerView, titleForRow row: Int, forComponent component: Int)->String?
{
return states[row]
}

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}

func pickerView(_ pickerView:UIPickerView, didSelectRow row:Int, inComponent component:Int)
{
var currentState = abbvsList[statePicker.selectedRow(inComponent: 0)]
parser = XMLParser(contentsOf:(NSURL(string:"http://alerts.weather.gov/cap/\(currentState).php?x=0") as! URL))!
parser.delegate = self
parser.parse()
alertsTable.reloadData()
}

override func viewDidLoad() {
super.viewDidLoad()
statePicker.delegate = self
statePicker.dataSource = self
self.alertsTable.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
alertsTable.delegate = self
alertsTable.dataSource = self
}

func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
if(elementName == "cap:event") {
titleFound = true
}
if(elementName == "cap:expires") {
descFound = true
}
}

func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if(elementName == "cap:event") {
titleFound = false
}
if(elementName == "cap:expires") {
descFound = false
}
}

func parser(_ parser: XMLParser, foundCharacters string: String) {
if(titleFound) {
alerts.append(string)
}
if(descFound) {
exps.append(string)
}
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return alerts.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = UITableViewCell(style:UITableViewCellStyle.subtitle, reuseIdentifier: "cell")
cell.textLabel!.text = alerts[indexPath.row]
cell.detailTextLabel!.text = "Expires: "+exps[indexPath.row]
return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//What happens when the cell is tapped`
}
}

Answer

You are continuously appending to alerts and exps and never reinitializing them.

In pickerView(_:didSelectRow:inComponent), set alerts and exps back to empty before parsing the new input:

func pickerView(_ pickerView:UIPickerView, didSelectRow row:Int, inComponent component:Int)
{
    var currentState = abbvsList[statePicker.selectedRow(inComponent: 0)]
    parser = XMLParser(contentsOf:(NSURL(string:"http://alerts.weather.gov/cap/\(currentState).php?x=0") as! URL))!

    alerts = []
    exps = []

    parser.delegate = self
    parser.parse()
    alertsTable.reloadData()
}
Comments