Swift refresh table view - ios

i have a problem,when i called the viewWillAppear ,table view get refreshed,but the data are strange(i insert a new record so i need to refresh the table view every time it appears), for example
original data:a,b,c
insert one record should be:a,b,c,d
result:a,b,c,a(why a?)
Hope someone could help me ,please.
import UIKit
import SQLite
class DetailViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
let inputDetailTable = Table("input_detail")
var labelOneArray = [String]()
var labelTwoArray = [NSAttributedString]()
var labelThreeArray = [String]()
var labelFourArray = [String]()
let id = Expression<Int>("id")
let deleteFlag = Expression<Int>("delete_flag")
// var selectedDatas :AnySequence<Row>!
let viewService = ViewService()
override func viewDidLoad() {
super.viewDidLoad()
getTableViewData()
}
override func viewWillAppear(_ animated: Bool) {
getTableViewData()
self.tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
let selectedDatas = viewService.selectTableData()
var counter = 0
for _ in selectedDatas{
counter += 1
}
return counter
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
for: indexPath) as! DetailTableViewCell
cell.labelOne.attributedText = labelTwoArray[indexPath.row]
cell.labelTwo.text = labelFourArray[indexPath.row]
return cell
}
func getTableViewData() {
let selectedDatas = viewService.selectTableData()
for selectedData in selectedDatas{
var tempAmount = String(selectedData[Expression<Int>
("amount")])
var tempTwoAmout :NSAttributedString?
if selectedData[Expression<Int>("type_flag")] == 0 {
tempAmount = "-"+tempAmount
let text = tempAmount
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let myMutableString = NSMutableAttributedString(
string: tempAmount,
attributes: [:])
myMutableString.addAttribute(
NSAttributedStringKey.foregroundColor,
value: UIColor.red,
range: textRange)
tempTwoAmout = myMutableString
} else {
tempAmount = "+"+tempAmount
let text = tempAmount
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let myMutableString = NSMutableAttributedString(
string: tempAmount,
attributes: [:])
myMutableString.addAttribute(
NSAttributedStringKey.foregroundColor,
value: UIColor.green,
range: textRange)
tempTwoAmout = myMutableString
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
let createTimeFormat = formatter.string(from:
selectedData[Expression<Date>("create_time")])
self.labelOneArray.append(String(selectedData[Expression<Int>
("id")]))
self.labelTwoArray.append(tempTwoAmout!)
self.labelThreeArray.append(String(selectedData[Expression<String>
("location")]))
self.labelFourArray.append(createTimeFormat)
}
}
}

You are calling twice getTableViewData(), and try to remove all data from your arrays before getting new data.

Related

Filter multiple arrays that are populated using Firebase

I have a TextField which works as a SearchView and then my TableView shows my results. The prototype cell of the TableView consists of two Labels. The first label is populated by schoolNameArray. The second label is populated by schoolTownArray + ", " + schoolCountryArray. All 3 arrays are populated via Firebase. Image of the search functionality shown below.
Without the second label, I can filter one array just fine. My issue is I can only make my SearchView filter on one array, not three. E.g. I type 'Academy' and 'Azhar Academy' shows, but if I type 'Bolton' I get "Fatal error: Index out of range" because I haven't been able to populate the filteredSchoolLocationArray correctly.
My Android version of the project retrieves the text from the search box and then handles the filtering within the for loop of the firebase query. How can I do this (or achieve the same result) here using the following code:
class SearchViewController: UIViewController {
#IBOutlet weak var editTextSearch: UITextField!
#IBOutlet weak var tableViewSearch: UITableView!
var schoolNameArray = [String]()
var schoolTownArray = [String]()
var schoolCountryArray = [String]()
var filteredSchoolNameArray = [String]()
var filteredSchoolLocationArray = [String]()
var searching = false
override func viewDidLoad() {
super.viewDidLoad()
let schoolDatabase = Database.database().reference().child("Timetable")
schoolDatabase.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let schoolID = child as! DataSnapshot
let stringApproved = schoolID.childSnapshot(forPath: "Approved").value
if stringApproved as? String == "Yes" {
let stringSchoolName = schoolID.childSnapshot(forPath: "Name").value as! String
let stringSchoolTown = schoolID.childSnapshot(forPath: "Town or City").value as! String
let stringSchoolCountry = schoolID.childSnapshot(forPath: "Country").value as! String
self.schoolNameArray.append(stringSchoolName)
self.schoolTownArray.append(stringSchoolTown)
self.schoolCountryArray.append(stringSchoolCountry)
}
}
self.tableViewSearch.reloadData()
})
}
}
extension SearchViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return filteredSchoolNameArray.count
} else {
return schoolNameArray.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let searchItem = tableView.dequeueReusableCell(withIdentifier: "SearchItem") as! SearchItemCell
searchItem.searchItemSchoolName.text = schoolNameArray[indexPath.row]
searchItem.searchItemSchoolLocation.text = schoolTownArray[indexPath.row] + ", " + schoolCountryArray[indexPath.row]
if searching {
searchItem.searchItemSchoolName.text = filteredSchoolNameArray[indexPath.row]
searchItem.searchItemSchoolLocation.text = filteredSchoolLocationArray[indexPath.row]
} else {
searchItem.searchItemSchoolName.text = schoolNameArray[indexPath.row]
searchItem.searchItemSchoolLocation.text = schoolTownArray[indexPath.row] + ", " + schoolCountryArray[indexPath.row]
}
return searchItem
}
}
extension SearchViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
filteredSchoolNameArray = schoolNameArray.filter({$0.lowercased().contains((textField.text?.lowercased())!)})
searching = true
tableViewSearch.reloadData()
return true
}
}
You should be using a struct to represent your data. You'll have a much easier time. You have a few more kinks to work out but this should get you going:
class SearchViewController: UIViewController {
struct School {
let name: String
let town: String
let country: String
}
#IBOutlet weak var editTextSearch: UITextField!
#IBOutlet weak var tableViewSearch: UITableView!
var schools: [School] = []
var filteredSchools: [School] = []
var searching = false
override func viewDidLoad() {
super.viewDidLoad()
// ...
if stringApproved as? String == "Yes" {
let name = schoolID.childSnapshot(forPath: "Name").value as! String
let town = schoolID.childSnapshot(forPath: "Town or City").value as! String
let country = schoolID.childSnapshot(forPath: "Country").value as! String
self.schools.append(.init(name: name, town: town, country: country))
}
// ...
}
// ...
}
extension SearchViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
filteredSchools = schools.filter({ $0.name.lowercased().contains(text.lowercased()) })
searching = true
tableViewSearch.reloadData()
return true
}
}
Thanks to Rob for his help, below is the final code.
class SearchViewController: UIViewController {
#IBOutlet weak var editTextSearch: UITextField!
#IBOutlet weak var tableViewSearch: UITableView!
struct School {
let schoolName: String
let schoolTown: String
let schoolCountry: String
}
var schoolArray: [School] = []
var filteredSchoolArray: [School] = []
var searching = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let schoolDatabase = Database.database().reference().child("Timetable")
schoolDatabase.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let schoolID = child as! DataSnapshot
let stringApproved = schoolID.childSnapshot(forPath: "Approved").value
if stringApproved as? String == "Yes" {
// stringSchoolID = schoolID.key
let stringSchoolName = schoolID.childSnapshot(forPath: "Name").value as! String
let stringSchoolTown = schoolID.childSnapshot(forPath: "Town or City").value as! String
let stringSchoolCountry = schoolID.childSnapshot(forPath: "Country").value as! String
self.schoolArray.append(.init(schoolName: stringSchoolName, schoolTown: stringSchoolTown, schoolCountry: stringSchoolCountry))
}
}
self.tableViewSearch.reloadData()
})
//filteredSchoolArray.removeAll()
}
}
extension SearchViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return filteredSchoolArray.count
} else {
return schoolArray.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let searchItem = tableView.dequeueReusableCell(withIdentifier: "SearchItem") as! SearchItemCell
if searching {
let filteredSchoolItem = filteredSchoolArray[indexPath.row]
searchItem.searchItemSchoolName.text = filteredSchoolItem.schoolName
searchItem.searchItemSchoolLocation.text = filteredSchoolItem.schoolTown + ", " + filteredSchoolItem.schoolCountry
} else {
let schoolItem = schoolArray[indexPath.row]
searchItem.searchItemSchoolName.text = schoolItem.schoolName
searchItem.searchItemSchoolLocation.text = schoolItem.schoolTown + ", " + schoolItem.schoolCountry
}
return searchItem
}
}
extension SearchViewController: UITextFieldDelegate {
func textFieldShouldClear(_ textField: UITextField) -> Bool {
editTextSearch.resignFirstResponder()
editTextSearch.text = ""
filteredSchoolArray.removeAll()
tableViewSearch.reloadData()
return true
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
filteredSchoolArray = schoolArray.filter({$0.schoolName.lowercased().contains(text.lowercased()) ||
$0.schoolTown.lowercased().contains(text.lowercased()) ||
$0.schoolCountry.lowercased().contains(text.lowercased())})
searching = true
tableViewSearch.reloadData()
return true
}
}

My data from firebase database is not loading into my tableview

I have a tableview loading data from firebase database. When I open my app the data does not populate. when I create a new post I can see the tableview cells modifying like changed were made but the post doesn't populate. I can't figure it out.
import UIKit
import FirebaseAuth
import FirebaseDatabase
class EventsViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var eventsRef: DatabaseReference?
var eventsDatabaseHandle:DatabaseHandle?
var eventsTitles = [String]()
var eventTimestamps = [String]()
var eventsLocations = [String]()
var eventsImages = [UIImage]()
#IBOutlet weak var addEventsButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
adminAuth()
eventsRef = Database.database().reference()
tableView.reloadData()
tableView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi)
tableView.delegate = self
tableView.dataSource = self
eventsDatabaseHandle = eventsRef?.child("Church Events").observe(.childAdded, with: { (snaphot) in
let eventPost = snaphot.value as! [String: Any]
self.eventTimestamps.append(eventPost["eventdate"] as! String)
self.eventsTitles.append(eventPost["eventtitle"] as! String)
self.eventsLocations.append(eventPost["eventlocation"] as! String)
let task = URLSession.shared.dataTask(with: URL(string: eventPost["ImageUrl"] as! String)!) {(data, response, error) in
if let image: UIImage = UIImage(data: data!) {
self.eventsImages.append(image)
}
}
self.tableView.reloadData()
task.resume()
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return eventsImages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "events") as! EventsTableViewCell
let image = eventsImages[indexPath.row]
cell.flyerImages.image! = image
cell.eventTitle.text! = eventsTitles[indexPath.row]
cell.eventDate.text! = eventTimestamps[indexPath.row]
cell.eventLocation.text! = eventsLocations[indexPath.row]
cell.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
tableView.reloadData()
return cell
}
func adminAuth() {
if (Auth.auth().currentUser!.displayName != "Neil Leon") {
self.addEventsButton.tintColor = UIColor.clear
self.addEventsButton.isEnabled = false
}
else{
self.addEventsButton.isEnabled = true
}
}
}
image of empty tableview
]
So the code below is not tested as I don't have firebase setup currently.
However, observing childAdded... the documentation says it will pass all of the current records in the database at first and will then just post new additions. So all you need to do is loop through them, setup your tableView data source and reload the table.
Rather than use multiple arrays for values I've created an array of ChurchEvent objects instead.
struct ChurchEvents {
let title: String
let location: String?
let date: Date?
let imageUrlString: String?
init(dict: [String: Any]) {
self.title = dict["title"] as String
self.location = dict["location"] as? String
// etc
}
}
var events = [ChurchEvents]()
eventsDatabaseHandle = eventsRef?.child("Church Events").observe(.childAdded, with: { snapshot in
let list = snapshot.value as? [[String : AnyObject]]
let newEvents = list.map { ChurchEvent(dict: $0) }
events.append(newEvents)
tableView.reloadData()
}
Other improvements you could make:
class EventsTableViewCell: UICollectionViewCell {
func configure(with event: ChurchEvent {
eventDate.text = event.date
eventTitle.text = event.title
eventLocation.text = event.location
// etc
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "events") as! EventsTableViewCell
let event = events[indexPath.row]
cell.configure(with: event)
return cell
}

Tableview array is not loaded properly in iOS Swift?

I'm working in a chat application. Saving sender and receiver messages to firebase. My issue when tried to load array message to TableView, messages are not completely loaded. it shows gap for remaining messages.
MainChatViewController.swift
var messages = [Message]()
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("message count::\(messages.count)")
let message = messages[indexPath.row]
if let cell = chatTableView.dequeueReusableCell(withIdentifier: "Message") as? mainChatScreenTableViewCell {
cell.tag = indexPath.row
cell.configCell(message: message)
return cell
} else {
return mainChatScreenTableViewCell()
}
}
func loadData() {
Database.database().reference().child("messages").child(messageId)
.observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [DataSnapshot] {
self.messages.removeAll()
for data in snapshot {
if let postDict = data.value as? Dictionary<String, AnyObject>
{
let key = data.key
let post = Message(messageKey: key, postData: postDict)
self.messages.append(post)
}
}
}
self.chatTableView.reloadData()
})
}//loadData
func moveToBottom() {
DispatchQueue.main.async {
if self.messages.count > 0 {
let indexPath = IndexPath(row: self.messages.count - 1, section: 0)
self.chatTableView.scrollToRow(at: indexPath, at: .bottom, animated: false)
}
}
}
mainChatScreenTableViewCell
import UIKit
import SwiftKeychainWrapper
import Firebase
import FirebaseStorage
import FirebaseDatabase
class mainChatScreenTableViewCell: UITableViewCell {
#IBOutlet weak var recievedMessageLbl: UILabel!
#IBOutlet weak var recievedMessageView: UIView!
#IBOutlet weak var sentMessageLbl: UILabel!
#IBOutlet weak var sentMessageView: UIView!
#IBOutlet var receivedTimeLabel: UILabel!
#IBOutlet var sentTimeLabel: UILabel!
#IBOutlet var likeOrUnlikeImageView: UIImageView!
#IBOutlet var errorImageView: UIImageView!
#IBOutlet var checkImage: UIImageView!
var message: Message!
var currentUser = KeychainWrapper.standard.string(forKey: "uid")
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
sentMessageView.layer.masksToBounds = true
recievedMessageView.layer.masksToBounds = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configCell(message: Message) {
self.message = message
print("message label::\(message.message.count)")
if message.sender == currentUser {
let time = message.receivedTimeStamp
let timeinterval : TimeInterval = time
let dateFromServer = NSDate(timeIntervalSince1970:timeinterval)
let formatter = DateFormatter()
formatter.calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.ISO8601) as Calendar?
formatter.locale = NSLocale(localeIdentifier: "en_IN") as Locale
formatter.timeZone = NSTimeZone(name: "GMT+5:30") as TimeZone?
formatter.dateFormat = "h:mm a"
formatter.amSymbol = "AM"
formatter.pmSymbol = "PM"
let dateString: String = formatter.string(from: dateFromServer as Date)
print("dateString:::\(dateString)")
sentMessageView.isHidden = false
sentMessageView.layer.backgroundColor = UIColor.clear.cgColor
sentMessageLbl.text = " " + message.message
sentTimeLabel.text = " " + dateString
recievedMessageLbl.text = ""
recievedMessageLbl.isHidden = true
recievedMessageView.isHidden = true
} else {
let time = message.receivedTimeStamp
let timeinterval : TimeInterval = time
let dateFromServer = NSDate(timeIntervalSince1970:timeinterval)
let formatter = DateFormatter()
formatter.calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.ISO8601) as Calendar?
formatter.locale = NSLocale(localeIdentifier: "en_IN") as Locale
formatter.timeZone = NSTimeZone(name: "GMT+5:30") as TimeZone?
formatter.dateFormat = "h:mm a"
formatter.amSymbol = "AM"
formatter.pmSymbol = "PM"
let dateString: String = formatter.string(from: dateFromServer as Date)
print("dateString:::\(dateString)")
sentMessageView.isHidden = true
sentMessageLbl.isHidden = true
sentMessageLbl.text = ""
recievedMessageLbl.text = " " + message.message
receivedTimeLabel.text = " " + dateString
recievedMessageLbl.isHidden = false
recievedMessageView.layer.backgroundColor = UIColor.clear.cgColor
}
}
}
Here is my screenshot:
I'm getting correct message count but messages are fully loaded.
Any help much appreciated pls...
use it. heightForRowAt for height of cell but you must check your constraint.
and also use UITableViewDelegate to use it
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}

How to use search bar to fetch data from json result using swift?

Requirement : i need to filter the JSON data in UITableView with UISearchBar so i placed UISearchBar (not UISearchBarController) to top of my view controller and i placed UITableView below to the UISearchBarand I also have api key which contains data in json format .
code in my view controller:
class FourthViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate,UITabBarControllerDelegate,UISearchDisplayDelegate{
var arrDict = NSMutableArray()
var FilteredData = NSMutableArray()
var userid:String!
#IBOutlet var SearchButton: UISearchBar!
#IBOutlet var SlideShow: ImageSlideshow!
#IBOutlet var MyTableView: UITableView!
#IBOutlet var PostButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.hidden = true
SearchButton.delegate = self
jsonParsingFromURL()
SlideShow.backgroundColor = UIColor.whiteColor()
SlideShow.slideshowInterval = 5.0
SlideShow.pageControlPosition = PageControlPosition.UnderScrollView
SlideShow.pageControl.currentPageIndicatorTintColor = UIColor.lightGrayColor()
SlideShow.pageControl.pageIndicatorTintColor = UIColor.blackColor()
SlideShow.contentScaleMode = UIViewContentMode.ScaleAspectFill
SlideShow.setImageInputs(alamofireSource)
}
func jsonParsingFromURL () {
if Reachability.isConnectedToNetwork() == true
{
Alamofire.request(.GET, "http://something.com", parameters: nil, encoding: .URL, headers: nil).response { (req, res, data, error) -> Void in
let dataString = NSString(data: data!, encoding:NSUTF8StringEncoding)
print(dataString)
self.startParsing(data!)
}
}
else{
let alert = UIAlertController(title: "No Internet Connection", message: "make sure your device is connected to the internet", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
func startParsing(data :NSData)
{
let dict: NSDictionary!=(try! NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
for i in 0 ..< (dict.valueForKey("ads") as! NSArray).count
{
arrDict.addObject((dict.valueForKey("ads") as! NSArray) .objectAtIndex(i))
}
MyTableView.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrDict.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("FirstCell") as! FirstTableViewCell
let strTitle : NSString=arrDict[indexPath.row] .valueForKey("categoryname") as! NSString
let photoImage : NSString=arrDict[indexPath.row] .valueForKey("image1") as! NSString
let SecondImage : NSString=arrDict[indexPath.row] .valueForKey("image2") as! NSString
let ThirdImage : NSString=arrDict[indexPath.row] .valueForKey("image3") as! NSString
let FourthImage : NSString=arrDict[indexPath.row] .valueForKey("image4") as! NSString
let URL_API_HOST2:String = "https://www.imagestring.com/"
// let FourthData = NSData(contentsOfURL: NSURL(string: URL_API_HOST2 + (FourthImage as String))!)
cell.image1.sd_setImageWithURL(NSURL(string: URL_API_HOST2 + (photoImage as String)))
cell.image2.sd_setImageWithURL(NSURL(string: URL_API_HOST2 + (SecondImage as String)))
// cell.image2.image = UIImage(data: SecData!)
cell.image3.sd_setImageWithURL(NSURL(string: URL_API_HOST2 + (ThirdImage as String)))
cell.image4.sd_setImageWithURL(NSURL(string: URL_API_HOST2 + (FourthImage as String)))
cell.CategoryName.text = strTitle as String
return cell
}
Issue : I have already loaded one api key which is known as category..now i need fetch subcategory data using search bar..subcategory has another api....
Apple statement : UISearchController object manages the display of search results based on interactions with a search bar. description here
If you'r using UISearchBar
import UIKit
class TDSearchVC: UIViewController ,UITableViewDataSource,UITableViewDelegate , UISearchResultsUpdating , UISearchBarDelegate{
//MARK:- Outlets
//MARK:-
#IBOutlet var tblSearch: UITableView!
//MARK:- Properties
//MARK:-
var dataArray = [String]()
var filteredArray = [String]()
var shouldShowSearchResults = false
var searchController: UISearchController!
//MARK:- VDL
//MARK:-
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
loadListOfCountries() // get the data from file
configureSearchController() // Config Controller in VC
}
//MARK:- VC Methods
//MARK:-
func loadListOfCountries() {
// Specify the path to the countries list file.
let pathToFile = Bundle.main.path(forResource: "Country", ofType: "txt")
if let path = pathToFile {
// Load the file contents as a string.
do{
let countriesString = try String(contentsOfFile: path, encoding: String.Encoding.utf8)
self.dataArray = countriesString.components(separatedBy: "\n")
}
catch{
print("try-catch error is catched!!")
}
tblSearch.reloadData()
}
}
func configureSearchController() {
searchController = UISearchController(searchResultsController: nil)
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search here..."
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.searchBar.sizeToFit()
self.tblSearch.tableHeaderView = searchController.searchBar
}
//MARK:- table datasource
//MARK:-
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if shouldShowSearchResults {
return filteredArray.count
}
else {
return dataArray.count
}
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as UITableViewCell
if shouldShowSearchResults {
cell.textLabel?.text = filteredArray[indexPath.row]
}
else {
cell.textLabel?.text = dataArray[indexPath.row]
}
return cell
}
//MARK:- search update delegate
//MARK:-
public func updateSearchResults(for searchController: UISearchController){
let searchString = searchController.searchBar.text
// Filter the data array and get only those countries that match the search text.
filteredArray = dataArray.filter({ (country) -> Bool in
let countryText: NSString = country as NSString
return (countryText.range(of: searchString!, options: .caseInsensitive).location) != NSNotFound
})
tblSearch.reloadData()
}
//MARK:- search bar delegate
//MARK:-
public func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
shouldShowSearchResults = true
tblSearch.reloadData()
}
public func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
shouldShowSearchResults = false
tblSearch.reloadData()
}
}
If you'r using UITextField
import UIKit
class TDSearchVC: UIViewController , UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate{
#IBOutlet var textSearch: UITextField!
#IBOutlet var tblSearchResult: UITableView!
var arrData : [String] = []
var arrFilterData : [String] = []
var isSearch : Bool!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
isSearch = false
/*
* If date Data is in Json then use JSON Serialization
*/
arrData = ["Apple", "Banana", "Chikoo", "Brew", "Cherry", "Mango", "Lotus", "Peacock", "Temple", "Pine Apple","Glass", "Rose", "Church", "Computer", "Carrot"]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK:- textfield
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
tblSearchResult.reloadData()
}
else{
getSearchArrayContains(searchText)
}
return true
}
// Predicate to filter data
func getSearchArrayContains(_ text : String) {
var predicate : NSPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", text)
arrFilterData = (arrData as NSArray).filtered(using: predicate) as! [String]
isSearch = true
tblSearchResult.reloadData()
}
// MARK:- TableView Delegates
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
if isSearch! {
return arrFilterData.count
}
else{
return arrData.count
}
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
var cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
if isSearch! {
cell.textLabel?.text = arrFilterData[indexPath.row]
}
else{
cell.textLabel?.text = arrData[indexPath.row]
}
return cell
}
}

NSUser Defaults - Saving 3 variables together and then fetching

I'm using NSUserDefaults for persistent storage in my app. It is a game where after the game ends the score, together with the date and name (entered by user) must be stored and then shown in an table view. My code so far is:
import UIKit
class LeaderboardVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
var finishedGame = 0
var gameScore:Int! = 0
var name:String!
var date:String!
var score:Int!
var scoreData = [NSArray]()
var defaults = UserDefaults.standard
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
if finishedGame == 1{
saveNew()
}
self.tableView.delegate = self
self.tableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = false
self.navigationItem.hidesBackButton = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func saveNew() {
let enterNameAlert = UIAlertController(title: "Please Enter Your Name", message: "This will be used to place you in the leaderboards", preferredStyle: .alert)
enterNameAlert.addTextField { (textField:UITextField) -> Void in
textField.placeholder = "Name"
textField.autocapitalizationType = UITextAutocapitalizationType.words
textField.autocorrectionType = UITextAutocorrectionType.no
textField.clearsOnBeginEditing = true
textField.clearsOnInsertion = true
textField.clearButtonMode = UITextFieldViewMode.always
textField.keyboardType = UIKeyboardType.default
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (action:UIAlertAction) in
let currentTime = Date()
let timeFormatter = DateFormatter()
timeFormatter.locale = Locale.current
timeFormatter.dateFormat = "HH:mm dd/MM/yy"
let convertedTime = timeFormatter.string(from: currentTime) //date
let enteredName = enterNameAlert.textFields?.first?.text //name
// set object of date,name and score here
}
enterNameAlert.addAction(cancelAction)
enterNameAlert.addAction(confirmAction)
self.present(enterNameAlert, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! LeaderBoardCell
cell.dateLabel?.text =
cell.dateLabel.adjustsFontSizeToFitWidth = true
cell.scoreLabel?.text =
cell.scoreLabel.adjustsFontSizeToFitWidth = true
cell.nameLabel?.text =
cell.nameLabel.adjustsFontSizeToFitWidth = true
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return scoreData.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
There are 3 labels in the cell one for each of the 3 values which must be displayed. How do I set these values using NSUserDefaults (They must be together FOR example score:500 goes with name:John and so on)?
EDIT - NEW CODE
import UIKit
class LeaderboardVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
var finishedGame = 0
var gameScore:Int! = 0
var defaults = UserDefaults.standard
var newUserArray = NSMutableArray()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
if finishedGame == 1{
saveNew()
}
self.tableView.delegate = self
self.tableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = false
self.navigationItem.hidesBackButton = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func saveNew() {
let enterNameAlert = UIAlertController(title: "Please Enter Your Name", message: "This will be used to place you in the leaderboards", preferredStyle: .alert)
enterNameAlert.addTextField { (textField:UITextField) -> Void in
textField.placeholder = "Name"
textField.autocapitalizationType = UITextAutocapitalizationType.words
textField.autocorrectionType = UITextAutocorrectionType.no
textField.clearsOnBeginEditing = true
textField.clearsOnInsertion = true
textField.clearButtonMode = UITextFieldViewMode.always
textField.keyboardType = UIKeyboardType.default
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (action:UIAlertAction) in
let currentTime = Date()
let timeFormatter = DateFormatter()
timeFormatter.locale = Locale.current
timeFormatter.dateFormat = "HH:mm dd/MM/yy"
let convertedTime = timeFormatter.string(from: currentTime)
let enteredName = enterNameAlert.textFields?.first?.text
let newUserData = self.defaults.object(forKey: "UserData") as! NSArray
let newUserArray = NSMutableArray(array: newUserData)
let newUserRecord = [
"Name" : enteredName!,
"Score" : String(self.gameScore),
"Date" : convertedTime
] as [String : String]
newUserArray.add(newUserRecord)
self.defaults.set(newUserArray, forKey: "UserData")
self.defaults.synchronize()
print(newUserArray)
print("hello")
print(self.defaults)
}
enterNameAlert.addAction(cancelAction)
enterNameAlert.addAction(confirmAction)
self.present(enterNameAlert, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! LeaderBoardCell
let userData = defaults.object(forKey: "UserData") as! NSArray
cell.dateLabel?.text = "Date: \(((userData.object(at: indexPath.row) as! [String:Any])["Date"] as! String))"
cell.dateLabel.adjustsFontSizeToFitWidth = true
cell.scoreLabel?.text = "Score: \(((userData.object(at: indexPath.row) as! [String:Any])["Score"] as! String))"
cell.scoreLabel.adjustsFontSizeToFitWidth = true
cell.nameLabel?.text = "Name: \(((userData.object(at: indexPath.row) as! [String:Any])["Name"] as! String))"
cell.nameLabel.adjustsFontSizeToFitWidth = true
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return newUserArray.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
You can set dictionary to UserDefaults (that's for 3 values at one time). To do a leaderboard you'll need to hold this dictionaries in array.
let scoreDict: [String : Any] = ["name": "John", "score": 500, "date": NSDate()]
let defaults = UserDefaults.standard
let scoresKey = "scores"
var currentScores: [Any] = defaults.array(forKey: scoresKey) ?? []
currentScores.append(scoreDict)
defaults.set(currentScores, forKey: scoresKey)
defaults.synchronize()
I couldn't get as much but I just found that you need to add some elements to your scoreData for the tableview datasource
Here you can add the complete object in UserDefaults
//Make your scoreData as MutableArray
var scoreData = NSMutableArray()
// Record of a user
let user = [
"name" : "John",
"score" : 10,
"id" : 20
] as [String : Any]
scoreData.add(user)
defaults.set(scoreData, forKey: "UserData")
How to fetch them
let userData = defaults.object(forKey: "UserData") as! NSArray
print((userData.object(at: 0) as! [String:Any])["name"] as! String)
print((userData.object(at: 0) as! [String:Any])["score"] as! Int)
print((userData.object(at: 0) as! [String:Any])["id"] as! Int)
I have printed them on console and only the 0th index. You can print them on your Label with indexPath.row elements.
EDIT
In order to add a new record, first Fetch the old data from userDefaults
let newUserData = defaults.object(forKey: "UserData") as! NSArray
Then convert it into mutable array because we want to add a new user record. I could have done it while fetching but on fetching from UserDefaults it gives a immutable object no matter we type cast to NSMutableArray. So convert the newUserData to mutable object
let newUserMutableArray = NSMutableArray(array: newUserData)
Add a new record
// New record
let newUserRecord = [
"name" : "Rajan",
"score" : 20,
"id" : 30
] as [String : Any]
newUserMutableArray.add(newUserRecord)
Save it again
defaults.set(newUserMutableArray, forKey: "UserData")
And again fetch the way it is mentioned above. Now the fetched NSArray will contain two elements.
I still prefer to use core data in this case as the handling will become lot easy.
EDIT
After fetching
scoreDataArray = defaults.object(forKey: "UserData") as! NSArray
Reload your table
self.yourTableView.reloadData()
Your table view datasource methods will be like
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return scoreDataArray.count
}
In your cellForRowAt datasource method
cell.scoreLabel?.text = scoreDataArray.object(at: indexPath.row) as! [String:Any])["score"] as! String

Resources