I'm creating E-Commerce type app where user select zones from the list using textfield.
I want to that when user click on text Filed and write zone (like 55) and show dropdown all the data from the array which starting to 55 or having number of 5 in array (like 20051).
After showing all data in the form of dropdown. User select desired zone code and added in table view.
Use the code below to add search bar to UItableviewController and get searchBar on selecting UITextfield.
class ViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource,UITextFieldDelegate{
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var txtName: UITextField!
var originalArr = [[String:Any]]();
var searchArrRes = [[String:Any]]()
var searching:Bool = false
override func viewDidLoad() {
super.viewDidLoad()
//Assign delegate don't forget
txtName.delegate = self
tableView.delegate = self
tableView.dataSource = self
//my array
originalArr = [
["name": "abdul", "number": "+8800000001"],
["name": "abdin", "number": "+8800000002"],
["name": "Enamul", "number": "+8800000003"],
["name": "enam", "number": "+8800000004"],
["name": "Rafi", "number": "+8800000005"],
["name": "Ehaque", "number": "+8800000006"],
["name": "ab", "number": "+8800000007"],
["name": "Emon", "number": "+8800000008"],
["name": "enamu1", "number": "+8800000009"],
["name": "rafi", "number": "+88000000010"],
["name": "karim", "number": "+88000000011"],
["name": "radev", "number": "+88000000012"],
["name": "da", "number": "+88000000013"],
["name": "aa", "number": "+88000000014"],
["name": "rafi", "number": "+88000000010"],
["name": "karim", "number": "+88000000011"],
["name": "radev", "number": "+88000000012"],
["name": "da", "number": "+88000000013"],
["name": "aa", "number": "+88000000014"]
]
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
public func textField(_ textField: UITextField, shouldChangeCharactersIn range:
NSRange, replacementString string: String) -> Bool{
//input text
let searchText = textField.text! + string
//add matching text to arrya
searchArrRes = self.originalArr.filter({(($0["name"] as! String).localizedCaseInsensitiveContains(searchText))})
if(searchArrRes.count == 0){
searching = false
}else{
searching = true
}
self.tableView.reloadData();
return true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//check search text & original text
if( searching == true){
return searchArrRes.count
}else{
return originalArr.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//custom cell Custom_cell
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! Custom_cell
//check search text & original text
if( searching == true){
var dict = searchArrRes[indexPath.row]
cell.label_name.text = dict["name"] as? String
cell.label_number.text = dict["number"] as? String
}else{
var dict = originalArr[indexPath.row]
cell.label_name.text = dict["name"] as? String
cell.label_number.text = dict["number"] as? String
}
return cell
}
}
CustomCell Class
class Custom_cell: UITableViewCell {
#IBOutlet weak var label_name: UILabel!
#IBOutlet weak var label_number: UILabel!
}
Related
I am working on JSON. My Json data print into the tableview. I want to filtered that data with searchbar. So I put Textfield for using the Searchbar. I use reference from this website
http://findnerd.com/list/view/How-to-create-your-own-search-bar-in-Swift-Not-using-UISearchBar/20577/
My Search bar is working but not properly. I want to filter data after I write 3 Words in searchbar.If I write "Ku" then my tableview remain hide. If I write "kus" in searchbar then searchbar started searching and show me filtered data in tableview started from "kus". my searchbar related code are these
struct PatientData:Decodable {
var ID : String
var dt_bod : String
var e_gender : String
var int_glcode : String
var var_email : String
var var_fname : String
var var_phoneno : String
var var_uname : String
init(userdata : [String:Any]) {
self.ID = userdata["ID"] as! String
self.dt_bod = userdata["dt_bod"] as! String
self.e_gender = userdata["e_gender"] as! String
self.int_glcode = userdata["int_glcode"] as! String
self.var_email = userdata["var_email"] as! String
self.var_fname = userdata["var_fname"] as! String
self.var_phoneno = userdata["var_phoneno"] as! String
self.var_uname = userdata["var_uname"] as! String
}
var tabledata = [String]()
var tableFilterData = [String]()
var patientDetails = [PatientData]()
#IBAction func textfieldchanged(_ sender: Any) {
tableview.isHidden = true
}
my textfield change character function
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool{
let searchText = textField.text! + string
if searchText.count >= 3 {
tableview.isHidden = false
tableFilterData = tabledata.filter({ (result) -> Bool in
return result.range(of: searchText, options: .caseInsensitive) != nil
})
print(tableFilterData) // I got filtered data here but how to show this data into the tableview
tableview.reloadData()
}
else{
tableFilterData = []
}
return true
}
tableview part is
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return patientDetails.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as UITableViewCell!
let aa = patientDetails[indexPath.row].var_fname + " , " + patientDetails[indexPath.row].dt_bod + " , " + patientDetails[indexPath.row].var_phoneno
self.tabledata.append(aa)
cell.textLabel?.text = aa
cell.textLabel?.font = searchTextfield.font
return cell
}
Try this:
#objc func textFieldActive() {
tableView.isHidden = tableFilterData.count > 0 ? false : true
}
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool{
let searchText = textField.text! + string
if searchText.count >= 3 {
tableView.isHidden = false
tableFilterData = tabledata.filter({ (result) -> Bool in
return result.range(of: searchText, options: .caseInsensitive) != nil
})
tableView.reloadData()
}
else{
tableFilterData = []
}
return true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return tableFilterData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = tableFilterData[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = data
return cell
}
In Swift 4 or Swift 5, you can use like bellow..
Your tableview like bellow
Create a project
Create add textfield, tableView connect to viewcontroller
add bellow code..
class ViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource,UITextFieldDelegate{
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var txtName: UITextField!
var originalArr = [[String:Any]]();
var searchArrRes = [[String:Any]]()
var searching:Bool = false
override func viewDidLoad() {
super.viewDidLoad()
//Assign delegate don't forget
txtName.delegate = self
tableView.delegate = self
tableView.dataSource = self
//my array
originalArr = [
["name": "abdul", "number": "+8800000001"],
["name": "abdin", "number": "+8800000002"],
["name": "Enamul", "number": "+8800000003"],
["name": "enam", "number": "+8800000004"],
["name": "Rafi", "number": "+8800000005"],
["name": "Ehaque", "number": "+8800000006"],
["name": "ab", "number": "+8800000007"],
["name": "Emon", "number": "+8800000008"],
["name": "enamu1", "number": "+8800000009"],
["name": "rafi", "number": "+88000000010"],
["name": "karim", "number": "+88000000011"],
["name": "radev", "number": "+88000000012"],
["name": "da", "number": "+88000000013"],
["name": "aa", "number": "+88000000014"],
["name": "rafi", "number": "+88000000010"],
["name": "karim", "number": "+88000000011"],
["name": "radev", "number": "+88000000012"],
["name": "da", "number": "+88000000013"],
["name": "aa", "number": "+88000000014"]
]
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool{
//input text
let searchText = textField.text! + string
//add matching text to arrya
searchArrRes = self.originalArr.filter({(($0["name"] as! String).localizedCaseInsensitiveContains(searchText))})
if(searchArrRes.count == 0){
searching = false
}else{
searching = true
}
self.tableView.reloadData();
return true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//check search text & original text
if( searching == true){
return searchArrRes.count
}else{
return originalArr.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//custom cell Custom_cell
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! Custom_cell
//check search text & original text
if( searching == true){
var dict = searchArrRes[indexPath.row]
cell.label_name.text = dict["name"] as? String
cell.label_number.text = dict["number"] as? String
}else{
var dict = originalArr[indexPath.row]
cell.label_name.text = dict["name"] as? String
cell.label_number.text = dict["number"] as? String
}
return cell
}
}
You can download full source from GitHub Link: https://github.com/enamul95/TableView_Search
You can check size of text before filter:
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool{
var searchText = textField.text! + string
if string == "" {
searchText = (searchText as String).substring(to: searchText.index(before: searchText.endIndex))
}
if searchText == "" {
isSearch = false
tableview.reloadData()
}
else{
if searchText.count > 2 {
getSearchArrayContains(searchText)
}
}
return true
}
Please use this code:-
func getSearchArrayContains(_ text : String) {
tableFilterData = tableData.filter({$0.lowercased().contains(text)})
isSearch = true
tableview.reloadData()
}
For should three character use this linkenter link description here:-
Thanks
All of the above answers try to reverse engineer the UITextField string by concatenating the change to the previous string. This needed to be done because the delegate method shouldChangeCharactersIn is called before the change is made on the UITextField string.
These implementations are wrong and do not work when the user scrolls the cursor to the left and continue typing or selects and replaces text (as the answers ignore the NSRange delegate variable).
A better implementation is to not use the delegate method at all and instead add a target to the UITextField. This works because UITextField inherits from UIControl.
override func viewDidLoad() {
super.viewDidLoad()
searchTextField.addTarget(self, action: #selector(searchTextChanged(_:)), for: .editingChanged)
}
#objc func searchTextChanged(_ sender: UITextField) {
let search = sender.text ?? ""
filterContentForSearchText(search)
}
func filterContentForSearchText(_ searchText: String) {
print("Filterin with:", searchText)
filtered.removeAll()
filtered = original.filter { thing in
return "\(thing.value.lowercased())".contains(searchText.lowercased())
}
tableView.reloadData()
}
Note: This action can also be created in the storyboard by creating an #IBAction and dragging the Editing Changed connection from the UITextField to the #IBAction
In this photo as you can see user can turn on or off the switch for each cell. At the end when the user presses the button I need to add the selected cell (the cells that have a switch on) to an array to send it to API.
When the user clicks the button (at the bottom of the screen) I should get the unitNo and personId and store them in my struct
My model:
struct Itemm : Codable {
var unitNo:Int?
var personId:Int?
}
struct welcome {
var items : [Itemm?]
}
and I have to send an array like below:
{
"items": [
{
"unitNo": 0,
"personId": 0
}
]
}
I'm some confused as to how I should access the cell data in UIButton with if statement and then storing them. I would be grateful for any help.
Tableview:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SmsCell") as? SmsTableViewCell
cell?.PhonNumberLbl.text = data![indexPath.section].contacts[indexPath.row]?.phoneNumber
cell?.NameLbl.text = data![indexPath.section].contacts[indexPath.row]?.name
cell?.selectedTF.isOn = (data![indexPath.section].contacts[indexPath.row]?.selected)!
return cell!
}
API response:
[
{
"contacts": [
{
"id": 9827,
"selected": true,
"phoneNumber": "09203137745",
"name": "owner"
},
{
"id": 10159,
"selected": true,
"phoneNumber": "08523698522",
"name": "hff"
},
{
"id": 10161,
"selected": true,
"phoneNumber": "09586731218",
"name": "hjdldue"
}
],
"unitNo": 1,
"unitPlaque": "jack",
"billText": "texttext"
},
{
"contacts": [
{
"id": 10145,
"selected": true,
"phoneNumber": "09123809556",
"name": "mmm"
},
{
"id": 10160,
"selected": true,
"phoneNumber": "85233366888",
"name": "ttt"
}
],
"unitNo": 2,
"unitPlaque": "mm",
"billText": "texttext"
}
]
I think I didn't clearly explain what I'm looking for.
The first method I call is a GET method which gives me the data that I'm presenting in the table. The user could only change the switch, the user can change it as many times as they want until he/she presses the button (at the bottom of the screen).
When the button is pressed, I need to get the hidden data behind each cell and store them in my struct and send it to API which has another method. When the user presses the button I need to extract the 2 properties from the cells that has a switch on.
Presenter represents this:
func sendSmsForAllTheMembers(AptId:String , data:[String:Int])
{
ApiGenerator.request(targetApi: ApartemanService.sendSms(aptId: AptId, data: data), responseModel: Nil.self, success: { (response) in
if response.response.statusCode == 200 {
self.view?.SendingSmsSuccess()
}else {
do{
var errorMessage = try response.response.mapString()
errorMessage = errorMessage.replacingOccurrences(of: "\"", with: "",
options: NSString.CompareOptions.literal, range:nil)
self.view?.SendingSmsFailed(errorMessage: errorMessage)
}catch let error{
print(error)
self.view?.SendingSmsFailed(errorMessage: "error")
}
}
}) { (error) in
self.view?.SendingSmsFailed(errorMessage: "error")
}
}
Your first problem is that you need to store the switch state in your view controller somehow; you can't store it in the cell directly since cells are re-used as your table view scrolls.
While you could store the switch state in your model struct, I would probably not do this, as it makes your struct mutable. I would use a Set<IndexPath> to track selections.
Your next problem is knowing when the switch is changed in a cell. You can provide a closure to your UITableviewCell subclass to handle this.
SmsCell
var switchHandler: ((Bool)->Void)?
#IBAction func switchChanged(_ sender: UISwitch) {
self.switchHandler?(sender.isOn)
}
View Controller
var selectedCells = Set<IndexPath>()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SmsCell") as! SmsTableViewCell
let cellData = self.data![indexPath.section]
let contact = cellData.contacts[indexPath.row]
cell.PhonNumberLbl.text = contact.phoneNumber
cell.NameLbl.text = contact.name
cell.selectedTF.isOn = self.selectedCells.contains(indexPath)
cell.switchHandler = { (switchState) in
if switchState {
self.selectedCells.insert(indexPath)
} else {
self.selectedCells.remove(indexPath)
}
}
return cell
}
Your final task is to create struct that you can encode into the required JSON:
struct Itemm: Codable {
let unitNo: Int
let personId: Int
}
struct Welcome: Codable {
var items:[Itemm]
}
View Controller
#IBAction func sendButtonTapped(_ sender: UIButton) {
var items = [Itemm]()
for indexPath in self.selectedCells {
let data = self.data![indexPath.section]
let contact = data.contacts[indexPath.row]
let newItem = Itemm(unitNo: data.unitNo, personId: contact.id)
items.append(newItem)
}
let welcome = Welcome(items: items)
// Now you can encode and send welcome
}
ok, as far as i got from what are you trying to do,
1- give the switch tag,
inside the
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch.tag = indexPath.row
switch.addTarget(self, action: #selector(onSwitchValueChanged), for: .touchUpInside)
}
And in the didChange Method of the Switch,
func onSwitchValueChanged(_ switch: UISwitch) {
if switch.on {
selectedArray.append(dataArray[switch.tag])
} esle {
selectedArray.remove(at: switch.tag)
}
SmsCell
var switchHandler: ((Bool)->Void)?
#IBAction func switchChanged(_ sender: UISwitch) {
self.switchHandler?(sender.isOn)
}
View Controller
var selectedCells = Set<IndexPath>()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SmsCell") as! SmsTableViewCell
let cellData = self.data![indexPath.section]
let contact = cellData.contacts[indexPath.row]
cell.PhonNumberLbl.text = contact.phoneNumber
cell.NameLbl.text = contact.name
cell.selectedTF.isOn = self.selectedCells.contains(indexPath)
cell.switchHandler = { (switchState) in
if switchState {
self.selectedCells.insert(indexPath)
} else {
self.selectedCells.remove(indexPath)
}
}
return cell
}
struct Itemm: Codable {
let unitNo: Int
let personId: Int
}
struct Welcome: Codable {
var items:[Itemm]
}
View Controller
#IBAction func sendButtonTapped(_ sender: UIButton) {
var items = [[String:Int]]()
for indexPath in self.selectedCells {
let data = self.data![indexPath.section]
let contact = data.contacts[indexPath.row]
items.append(["Number" : data.unitNo, "ID" : contact!.id])
}
let welcome = ["items" : items]
print(welcome)
Presenter.sendSmsForAllTheMembers(AptId: aptId, data: welcome)
}
the final answer is a combination of the answer that some people gave me here and some that I found it myself
You can update your Model objects when switch on/off function and also give switch tag as a cell.indexpath.
Update model objects like this Ex :-
if switch == on {
items[switch_tag].unitNo =0
} else {
items[switch_tag].unitNo =1
}
My JSON looks like this and data is not coming from the server:
[
{
"emp_id": "1",
"fname": "Shreya",
"lname": "Shah",
"email_id": "shreyashah#gmail.com",
"password": "shreya123",
"date_of_birth": "14/03/1995",
"gender": "Female",
"street1": "Arbudgiri Society",
"street2": "Nr.Rambaug Road",
"city": "Ahmedabad",
"zipcode": "380005",
"country": "India",
"country_code": "+91",
"phone_no": "456544545",
"emp_img": "employeeImages/shreyashah#gmail.com_services-pic2.jpg",
"emp_desig": "PHP Developer",
"emp_skills": "html,php,css,jquery,javascript",
"emp_edu": "BCA,MCA",
"emp_exp": "3years",
"emp_notice_period": "30days",
"emp_lang": "english,hindi,gujarati"
},
{
"emp_id": "2",
"fname": "Harish",
"lname": "Verma",
"email_id": "harishverma#gmail.com",
"password": "harish123",
"date_of_birth": "22/07/1994",
"gender": "Female",
"street1": "Satyam Skyline",
"street2": "Nr.Sola Cross Roads",
"city": "Ahmedabad",
"zipcode": "380005",
"country": "India",
"country_code": "+91",
"phone_no": "964783214",
"emp_img": "employeeImages/harishverma#gmail.com_services-pic2.jpg",
"emp_desig": "iOS Team Lead",
"emp_skills": "objective-c,swift",
"emp_edu": "BCA,MCA",
"emp_exp": "3years",
"emp_notice_period": "30days",
"emp_lang": "english,hindi,gujarati"
}
]
My code in VIEWCONTROLLER code where i have decoded the json and used it in my file:
import UIKit
struct EmployeeDisplayData: Decodable {
let emp_img: String
let fname: String
let emp_desig: String
let emp_exp: String
let country: String
let emp_notice_period: String
}
class UserViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var mainTableView: UITableView!
let URL_GET_DATA = "http://172.16.1.22/Get-Employee-API/get-employee/"
var employeeArray = [EmployeeDisplayData]()
override func viewDidLoad() {
super.viewDidLoad()
self.mainTableView.delegate = self
self.mainTableView.dataSource = self
getEmployee()
// Do any additional setup after loading the view, typically from a nib.
// Do any additional setup after loading the view.
}
func getEmployee(){
let empURL = URL(string: "http://172.16.1.22/Get-Employee-API/get-employee/")
URLSession.shared.dataTask(with: empURL!) { (data, response, error) in
do
{
if error == nil
{
self.employeeArray = try JSONDecoder().decode([EmployeeDisplayData].self, from: data!)
for mainArr in self.employeeArray
{
DispatchQueue.main.async {
self.mainTableView.reloadData()
}
}
print("****Employee Data****\(self.employeeArray)")
}
}
catch
{
print("Error in get JSON Data Employee\(error)")
}
}.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return employeeArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:EMpTableViewCell = tableView.dequeueReusableCell(withIdentifier: "EMpTableViewCell") as! EMpTableViewCell
let empData = employeeArray[indexPath.row]
cell.lblOne.text = empData.fname
cell.lblTwo.text = empData.emp_desig
cell.lblThree.text = empData.emp_exp
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}
Can someone help me with this error I have no idea what does this error mean I have seen the same type error most of the places and tried to solve it but still the same error from the server
my postman
]4
I got the same error and I searched a lot by changing contentType and many more things but none is fixed it. PostMan Raw and Preview Tab response gives me idea where the exact issue.
Raw
Preview
This error occur because of Connected to MySQL text beginning of response. so inform your developer about this, he will solve this and may be the error solve.
So here is the JSON
{
"city": {
"id": 4930956,
"name": "Boston",
"coord": {
"lon": -71.059769,
"lat": 42.358429
},
"country": "US",
"population": 0,
"sys": {
"population": 0
}
},
"cod": "200",
"message": 0.0424,
"cnt": 39,
"list": [
{
"dt": 1473476400,
"main": {
"temp": 76.33,
"temp_min": 73.11,
"temp_max": 76.33,
"pressure": 1026.47,
"sea_level": 1027.96,
"grnd_level": 1026.47,
"humidity": 73,
"temp_kf": 1.79
},
"weather": [
{
"id": 500,
"main": "Rain",
"description": "light rain",
"icon": "10n"
}
],
"clouds": {
"all": 8
},
"wind": {
"speed": 7.29,
"deg": 300.501
},
Here is my Controller where I go grab the data......
class ViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var amConnected: UILabel!
#IBOutlet weak var weatherTable: UITableView!
var arrRes = [[String:AnyObject]]()
var swiftyJsonVar: JSON?
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.doSomethingNotification(_:)), name: "ReachabilityChangedNotification", object: nil)
let openWMAPI = "http://api.openweathermap.org/data/2.5/forecast/city?q=Boston,Ma&APPID=XXXXXXXXXXXXXXX&units=imperial"
Alamofire.request(.GET,openWMAPI).responseJSON{
(responseData) -> Void in
print(responseData)
let swiftyJsonVar = JSON(responseData.result.value!)
self.weatherTable.reloadData()
}
.responseString{ response in
//print(response.data.value)
// print(response.result.value)
//print(response.result.error)
//eprint("inhere");
}
weatherTable.rowHeight = UITableViewAutomaticDimension
weatherTable.estimatedRowHeight = 140
// Do any additional setup after loading the view, typically from a nib.
}
In my table loop it is now saying that the jsonArray is nil and failing.
I'm unsure as to what I'm doing wrong at this point.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = weatherTable.dequeueReusableCellWithIdentifier("theCell", forIndexPath: indexPath)
let label1 = cell.viewWithTag(101) as! UILabel
print("inhere")
if((swiftyJsonVar) != nil){
if let jsonArray = self.swiftyJsonVar["list"].array {
var temp = jsonArray[indexPath.row]["main"]["temp"].float
var rdate = jsonArray[indexPath.row]["dt_txt"].string
print(temp)
}else{
print("test")
}
}
label1.text = "TEST"
return cell
}
OVerall I'm just not sure how to dig down into the next level of the JSON.
If you are not averse to it, try using AlamofireObjectMapper instead of SwiftyJson
1) If you are going to change the name of the json keys very often, and are going to do a lot of enum transformations try :
AlamofireObjectMapper
2) If the names are going to be the same, with minimal transformations, directly use :
AlamofireJsonToObjects
Both of these cases, create model classes for your json object. If you have an array - you can define a var as an array
if it is an object or an array of objects - you can then create another model class which is again Mappable and then define such an object var in the original model.
The above libraries will make your code extremely clean while extracting objects to json.
You can access to the elements inside an JSON array in SwiftyJSON using consecutive subscripts, if we have the following JSON for example:
var json: JSON = ["name": "Jack", "age": 25,
"list": ["a", "b", "c", ["what": "this"]]]
Then you can access to the four element of the subarray listcontained in the main array in the following way for example:
json["list"][3]["what"] // this
Or you can define a path like this let path = ["list",3,"what"] and then call it in this way:
json[path] // this
With the above explained let's introduce it with your JSON file to list the elements inside the array weather:
if let jsonArray = json["list"].array {
// get the weather array
if let weatherArray = jsonArray[0]["weather"].array {
// iterate over the elements of the weather array
for index in 0..<weatherArray.count {
// and then access to the elements inside the weather array using optional getters.
if let id = weatherArray[index]["id"].int, let main = weatherArray[index]["main"].string {
print("Id: \(id)")
print("Main: \(main)")
}
}
}
}
And you should see in the console:
Id: 800
Main: Clear
I hope this help you.
I have a Dictionary data structure like below and I am trying to group them in my TableViewController such that Group A displays MyData that starts with title = A and at the same time display sectionIndexTitlesForTableView with available letters gotten from Title.
[This is my what I want to achieve]
I have tried to scrap off all the first letters from the title Element in my Dictionary and save them in a set using the code below but when I run my app, I get results duplicated in my table.
I am quite new to swift and would be glad to be guided on how to achieve this.
Here's my Dictionary Data:
var data: [[String:AnyObject]] =
[
[
"id": "1",
"title": "A Title",
"alphabet": "A",
"Detail": "This is a String"
],
[
"id": "2",
"title": "A Title Again",
"alphabet": "A",
"Detail": "This is a String"
],
[
"id": "3",
"title": "B Title",
"alphabet": "B",
"Detail": "This is a String"
],
[
"id": "4",
"title": "B Title Again",
"alphabet": "B",
"Detail": "This is a String"
]
]
And Here's my attempt:
class Index: UITableViewController {
var MyData = data
var letters = Set<String>()
override func viewDidLoad() {
super.viewDidLoad()
for element in MyData {
var title = element["title"] as? String
let letter = title?.substringToIndex(advance(title!.startIndex, 1))
letters.insert(letter!)
}
MyData = MyData.sort { element1, element2 in
let title1 = element1["title"] as? String
let title2 = element2["title"] as? String
return title1 < title2
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return letters.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.MyData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell?
cell!.textLabel?.text = (MyData[indexPath.row]["title"] as! String)
return cell!
}
The problem is numberOfRowsInSection has to return the number of rows per section, in your example 2 for section 0 and 2 for section 1
You can collect your letter set with the key value coding method valueForKey which is often mistaken for objectForKey.
Unlike objectForKey which returns one value for the given key valueForKey returns the value of the key alphabetof all members in the array.
This code creates a Set of the letters to purge the duplicates, turns it back to an Array and sorts it.
let letters = (data as NSArray).valueForKey("alphabet") as! [String]
let filteredLetters = Set<String>(letters)
let sortedLetters = Array(filteredLetters).sorted {$0 < $1}
If all values for alphabet – as well as the other keys - are guaranteed to be String there is no need to cast them to optionals.
Then in numberOfRowsInSection you have to filter the number of items of each section
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.filter { ($0["alphabet"] as! String) == sortedLetters[section] }.count
}
Notice that there is no casting needed for the expression sortedLetters[section] because the compiler knows that's an array of String.
Of course you have also to retrieve the appropriate items for the sections in cellForRowAtIndexPath which is quite expensive because the main array is going to be filtered multiple times.
I'd recommend to transform data in viewDidLoad() into a new dictionary with the letters as keys and an array containing the items starting with this particular letter as values. This is the best solution regarding speed and performance.
Here a complete solution (without displaying the letters for quick search)
class TableViewController: UITableViewController {
let data: [[String:String]] =
[
[
"id": "1",
"title": "A Title",
"alphabet": "A",
"Detail": "This is a String"
],
[
"id": "2",
"title": "A Title Again",
"alphabet": "A",
"Detail": "This is a String"
],
[
"id": "3",
"title": "B Title",
"alphabet": "B",
"Detail": "This is a String"
],
[
"id": "4",
"title": "B Title Again",
"alphabet": "B",
"Detail": "This is a String"
]
]
var letters = [String]()
var dataSource = [String:AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
for value in data {
let letter = value["alphabet"]!
if dataSource[letter] == nil {
letters.append(letter)
dataSource[letter] = [[String:AnyObject]]()
}
var array = dataSource[letter] as! [[String:AnyObject]]
array.append(value)
dataSource.updateValue(array, forKey: letter)
}
letters.sorted {$0 < $1}
tableView.reloadData()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return letters.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let letter = letters[section]
return dataSource[letter]!.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
let letter = letters[indexPath.section]
let letterArray = dataSource[letter]! as! [[String:AnyObject]]
let item = letterArray [indexPath.row]
if let title = item["title"] as? String {
cell.textLabel?.text = title
}
return cell
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return letters[section]
}
}