return data in DetailTableView (swift) - ios

In my app I have two table views. The first table view has a set number of cells. These cells will always be the same and will never change The above table view will always have the 4 cells and never more. On my server I have my API which has routes for each of these cells.
For example:
GET - myAPI/Air
GET - myAPI/history
GET - myAPI/train
GET - myAPI/taxi
And each routes send backs different data
mainTablewView:
import UIKit
enum NeededAPI {
case Air
case History
case Train
case Taxi
}
class mainTableViewController : UITableViewController {
struct WeatherSummary {
var id: String
}
var testArray = NSArray()
var manuArray = NSArray()
// Array of sector within our company
var selectSector: [String] = ["Air", "History","Train","Taxi"]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 80.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.selectSector.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("sectorList", forIndexPath: indexPath)
// Configure the cell...
if selectSector.count > 0 {
cell.textLabel?.text = selectSector[indexPath.row]
}
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "AirSegue"){
if let destination = segue.destinationViewController as? AirTableViewController {
let indexPath:NSIndexPath = self.tableView.indexPathForSelectedRow!
if let row:Int = indexPath.row {
destination.apiThatNeedsToBeCalled = .Air
}
}
}
if (segue.identifier == "HistorySegue"){
if let destination = segue.destinationViewController as? HistoryTableViewController {
let indexPath:NSIndexPath = self.tableView.indexPathForSelectedRow!
if let row:Int = indexPath.row {
destination.apiThatNeedsToBeCalled = .History
}
}
}
if (segue.identifier == "TrainSgue"){
if let destination = segue.destinationViewController as? TrainTableViewController {
let indexPath:NSIndexPath = self.tableView.indexPathForSelectedRow!
if let row:Int = indexPath.row {
destination.apiThatNeedsToBeCalled = .Train
}
}
}
if (segue.identifier == "TaxiSegue"){
if let destination = segue.destinationViewController as? TaxiTableViewController {
let indexPath:NSIndexPath = self.tableView.indexPathForSelectedRow!
if let row:Int = indexPath.row {
destination.apiThatNeedsToBeCalled = .Taxi
}
}
}
}
}
and Post
import Foundation
class Post : CustomStringConvertible {
var userId:Int
var title: String
init(userid:Int , title:String){
self.userId = userid
self.title = title
}
var description : String { return String(userId) }
}
When user selects cell you set the correct value for the apiThatNeedsToBeCalled. Once you do this, code inside the didSet will get executed and it should call the function which calls the appropriate API.
to other tableView :
import UIKit
class AirTableViewController: UITableViewController {
var postCollection = [Post]()
var apiThatNeedsToBeCalled:NeededAPI = .Air {
didSet {
//check which API is set and call the function which will call the needed API
AirLine()
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var apiThatNeedsToBeCalled:NeededAPI = .Air {
didSet {
//check which API is set and call the function which will call the needed API
AirLine()
}
}
func AirLine(){
let url = NSURL(string: "http://jsonplaceholder.typicode.com/posts")
NSURLSession.sharedSession().dataTaskWithURL(url!){[unowned self] (data , respnse , error) in
if error != nil{
print(error!)
}else{
do{
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! [[String:AnyObject]]
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
var newPost = Iduser(id: 0)
for posts in json {
let postObj = Post(userid:posts["userId"] as! Int,title: posts["title"] as! String)
self.postCollection.append(postObj)
}
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
}
}catch let error as NSError{
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
print(error.localizedDescription)
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON:\(jsonStr)")
dispatch_async(dispatch_get_main_queue()) {
let alert = UIAlertController(title: "Alert", message: "Oops! Wrong Details, Try Again", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return self.postCollection.count ?? 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("AirCell", forIndexPath: indexPath)
// Configure the cell...
// cell.textLabel?.text = "test"
let weatherSummary = postCollection[indexPath.row]
cell.textLabel?.text = String(weatherSummary.userId)
cell.detailTextLabel?.text = weatherSummary.title
return cell
}
}
mainTableView and Air cell is Ok but when that selected other return The same information Air cell?

Perhaps I'm just missing it, but I can see your creation of the NSURLSession looks fine, but I don't see where you're calling .resume() on that once you've created it. If you don't call .resume() it'll never even perform that URLSession at all. Check the discussion here.

Related

Swift - Update/add/delete item on a table view with sections and source

How can I update/add/delete an item on a table view with sections and its source?
Im having issues when trying to update a cell. Sometimes it happens on delete and add too.
It seems that a problem on sections is triggering it.
1- Data is loaded from a JSON response from a server.
2 - This data is sorted alphabetically and sections based on the first letter from the name is created adding each client to its indexed letter.
Im adding to print-screens:
Before:
After:
I renamed 'Bowl' to 'Bowl 2' and it 'created' a new entry, keeping the old and new value. If I refresh (pull), it gets fixed.
Also, sometimes, it removes 'Abc MacDon' and after refreshing, it gets fixed.
class ClientsViewController: UITableViewController {
var sortedFirstLetters: [String] = []
var sections: [[Client]] = [[]]
var tableArray = [Client]()
var client: Client?
var wasDeleted: Bool?
var refresher: UIRefreshControl!
#IBOutlet weak var noClientsLabel: UILabel!
#IBOutlet var noClientsView: UIView!
#IBAction func unwindToClients(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? ClientViewController,
let client = sourceViewController.client,
let wasDeleted = sourceViewController.wasDeleted {
if(wasDeleted) {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
print("Delteted")
tableArray.remove(at: selectedIndexPath.row)
// DispatchQueue.main.async {
// // Deleting the row in the tableView
// if self.tableView.numberOfRows(inSection: selectedIndexPath.section) > 1 {
// self.tableView.deleteRows(at: [selectedIndexPath], with: UITableViewRowAnimation.bottom)
// } else {
// let indexSet = NSMutableIndexSet()
// indexSet.add(selectedIndexPath.section)
// self.tableView.deleteSections(indexSet as IndexSet, with: UITableViewRowAnimation.bottom)
// }
//
// }
}
}
else {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing client.
tableArray[selectedIndexPath.row] = client
//tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
print("update")
print(tableArray)
}
else {
// Add a client.
tableArray.append(client)
print("add")
}
}
self.prepareData()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondScene = segue.destination as! ClientViewController
if segue.identifier == "ShowDetail", let indexPath = self.tableView.indexPathForSelectedRow {
let currentPhoto = sections[indexPath.section][indexPath.row]
secondScene.client = currentPhoto
}
else if segue.identifier == "AddItem" {
print("add")
}
else {
fatalError("The selected cell is not being displayed by the table")
}
}
#objc func handleRefresh(_ refreshControl: UIRefreshControl) {
getClients()
}
}
extension ClientsViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.refreshControl?.addTarget(self, action: #selector(ClientsViewController.handleRefresh(_:)), for: UIControlEvents.valueChanged)
tableView.backgroundView = nil
noClientsLabel.text = ""
getClients() //for only the 1st time ==> when view is created ==> ok ish
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if(self.tableArray.count > 0) {
return sortedFirstLetters[section]
}
else {
return ""
}
}
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return sortedFirstLetters
}
override func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = sections[indexPath.section][indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "ClientCell", for: indexPath)
cell.textLabel?.text = item.name
cell.detailTextLabel?.text = item.city + " - " + item.province
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].count
}
func getClients() {
print("called server")
self.refreshControl?.beginRefreshing()
self.tableView.setContentOffset(CGPoint(x:0, y:-100), animated: true)
makeRequest(endpoint: "api/clients/all",
parameters: [:],
completionHandler: { (container : ApiContainer<Client>?, error : Error?) in
if let error = error {
print("error calling POST on /getClients")
print(error)
return
}
self.tableArray = (container?.result)!
self.prepareData()
DispatchQueue.main.async {
if(self.tableArray.isEmpty)
{
self.noClientsLabel.text = "bNo Clients"
self.tableView.backgroundView?.isHidden = false
self.noClientsLabel.text = ""
print("all")
}
else{
print("nothing")
}
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
} )
}
//sorts and makes the index
func prepareData() {
let firstLetters = self.tableArray.map { $0.nameFirstLetter }
let uniqueFirstLetters = Array(Set(firstLetters))
self.sortedFirstLetters = uniqueFirstLetters.sorted()
self.sections = self.sortedFirstLetters.map { firstLetter in
return self.tableArray
.filter { $0.nameFirstLetter == firstLetter }
.sorted { $0.name < $1.name }
}
}
}
Struct
struct Client: Codable {
var client_id: Int!
let name: String!
let postal_code: String!
let province: String!
let city: String!
let address: String!
init(name: String, client_id: Int! = nil, postal_code: String, province: String, city: String, address: String) {
self.client_id = client_id
self.name = name
self.postal_code = postal_code
self.province = province
self.city = city
self.address = address
}
var nameFirstLetter: String {
return String(self.name[self.name.startIndex]).uppercased()
}
}
code after some interaction with Hardik
import UIKit
import Foundation
class ClientsViewController: UITableViewController {
var sortedFirstLetters: [String] = []
var sections: [[Client]] = [[]]
// var tableArray = [Client]()
var tableArray : [Client] = [Client]()
var client: Client?
var wasDeleted: Bool?
var refresher: UIRefreshControl!
#IBOutlet weak var noClientsLabel: UILabel!
#IBOutlet var noClientsView: UIView!
#IBAction func unwindToClients(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? ClientViewController,
let client = sourceViewController.client,
let wasDeleted = sourceViewController.wasDeleted {
if(wasDeleted) {
if let index = self.tableArray.index(where: { (item) -> Bool in
item.client_id == client.client_id
}) {
self.tableArray.remove(at: index)
print("Delteted")
// Find the client in the tableArray by the client id and remove it from the tableArray
// I am writing an example code here, this is not tested so just get the logic from here.
//self.tableArray.remove(at: selectedIndexPath.row)
}
}
else {
if self.tableArray.contains(where: { (item) -> Bool in
item.client_id == client.client_id
}) {
//Find the item in the tableArray by the client id and update it there too
// I am writing an example code here, this is not tested so just get the logic from here.
//if let index = self.tableArray.index(where: { (item) -> Bool in
// item.id == client.id
//}) {
self.tableArray[index] = client
//self.tableArray.replace(client, at: index)
//}
print("update")
print(tableArray)
}
else {
// Add a client.
tableArray.append(client)
print("add")
}
}
// Now update the sections Array and it will have all the correct values
self.prepareData()
DispatchQueue.main.async {
self.tableView.reloadData()
}
// if(wasDeleted) {
// if let selectedIndexPath = tableView.indexPathForSelectedRow {
// print("Delteted")
// sections[selectedIndexPath.section].remove(at: selectedIndexPath.row)
// }
//
// }
// else {
// if let selectedIndexPath = tableView.indexPathForSelectedRow {
// // Update an existing client.
// sections[selectedIndexPath.section][selectedIndexPath.row] = client
// //tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
// print("update")
// print(tableArray)
// }
// else {
// // Add a client.
// tableArray.append(client)
// print("add")
// self.prepareData()
// }
// }
//
// DispatchQueue.main.async {
//
// self.tableView.reloadData()
// }
// if(wasDeleted) {
// if let selectedIndexPath = tableView.indexPathForSelectedRow {
// print("Delteted")
// tableArray.remove(at: selectedIndexPath.row)
//// DispatchQueue.main.async {
//// // Deleting the row in the tableView
//// if self.tableView.numberOfRows(inSection: selectedIndexPath.section) > 1 {
//// self.tableView.deleteRows(at: [selectedIndexPath], with: UITableViewRowAnimation.bottom)
//// } else {
//// let indexSet = NSMutableIndexSet()
//// indexSet.add(selectedIndexPath.section)
//// self.tableView.deleteSections(indexSet as IndexSet, with: UITableViewRowAnimation.bottom)
//// }
////
//// }
// }
//
// }
// else {
// if let selectedIndexPath = tableView.indexPathForSelectedRow {
// // Update an existing client.
// tableArray[selectedIndexPath.row] = client
// //tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
// print("update")
// print(tableArray)
// }
// else {
// // Add a client.
// tableArray.append(client)
// print("add")
// }
// }
// self.prepareData()
// DispatchQueue.main.async {
//
// self.tableView.reloadData()
// }
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondScene = segue.destination as! ClientViewController
if segue.identifier == "ShowDetail", let indexPath = self.tableView.indexPathForSelectedRow {
let currentPhoto = sections[indexPath.section][indexPath.row]
secondScene.client = currentPhoto
}
else if segue.identifier == "AddItem" {
print("add")
}
else {
fatalError("The selected cell is not being displayed by the table")
}
}
#objc func handleRefresh(_ refreshControl: UIRefreshControl) {
getClients()
}
}
extension ClientsViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.refreshControl?.addTarget(self, action: #selector(ClientsViewController.handleRefresh(_:)), for: UIControlEvents.valueChanged)
tableView.backgroundView = nil
noClientsLabel.text = ""
getClients() //for only the 1st time ==> when view is created ==> ok ish
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if(self.tableArray.count > 0) {
return sortedFirstLetters[section]
}
else {
return ""
}
}
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return sortedFirstLetters
}
override func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = sections[indexPath.section][indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "ClientCell", for: indexPath)
cell.textLabel?.text = item.name
cell.detailTextLabel?.text = item.city + " - " + item.province
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].count
}
func getClients() {
print("called server")
self.refreshControl?.beginRefreshing()
self.tableView.setContentOffset(CGPoint(x:0, y:-100), animated: true)
makeRequest(endpoint: "api/clients/all",
parameters: [:],
completionHandler: { (container : ApiContainer<Client>?, error : Error?) in
if let error = error {
print("error calling POST on /getClients")
print(error)
return
}
self.tableArray = (container?.result)!
self.prepareData()
DispatchQueue.main.async {
if(self.tableArray.isEmpty)
{
self.noClientsLabel.text = "bNo Clients"
self.tableView.backgroundView?.isHidden = false
self.noClientsLabel.text = ""
print("all")
}
else{
print("nothing")
}
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
} )
}
//sorts and makes the index
func prepareData() {
let firstLetters = self.tableArray.map { $0.nameFirstLetter }
let uniqueFirstLetters = Array(Set(firstLetters))
self.sortedFirstLetters = uniqueFirstLetters.sorted()
self.sections = self.sortedFirstLetters.map { firstLetter in
return self.tableArray
.filter { $0.nameFirstLetter == firstLetter }
.sorted { $0.name < $1.name }
}
}
}
You are editing the wrong datasource. You should edit or update the sections array not tableArray.
Change your code in unwindToClients here :
if(wasDeleted) {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
print("Delteted")
tableArray.remove(at: selectedIndexPath.row)
}
}
else {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing client.
tableArray[selectedIndexPath.row] = client
//tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
print("update")
print(tableArray)
}
else {
// Add a client.
tableArray.append(client)
print("add")
}
}
self.prepareData()
DispatchQueue.main.async {
self.tableView.reloadData()
}
with this :
if(wasDeleted) {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
print("Delteted")
sections[selectedIndexPath.section].remove(at: selectedIndexPath.row)
}
}
else {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing client.
sections[selectedIndexPath.section][selectedIndexPath.row] = client
//tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
print("update")
print(tableArray)
}
else {
// Add a client.
tableArray.append(client)
print("add")
self.prepareData()
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
To fix the incorrect section headers, try doing something like this:
if(wasDeleted) {
if let index = self.tableArray.index(where: { (item) -> Bool in
item.id == client.id
}) {
print("Delteted")
// Find the client in the tableArray by the client id and remove it from the tableArray
// I am writing an example code here, this is not tested so just get the logic from here.
//self.tableArray.remove(at: index)
}
}
else {
if self.tableArray.contains(client) {
//Find the item in the tableArray by the client id and update it there too
// I am writing an example code here, this is not tested so just get the logic from here.
//if let index = self.tableArray.index(where: { (item) -> Bool in
// item.id == client.id
//}) {
// self.tableArray.replace(client, at: index)
//}
print("update")
print(tableArray)
}
else {
// Add a client.
tableArray.append(client)
print("add")
}
}
// Now update the sections Array and it will have all the correct values
self.prepareData()
DispatchQueue.main.async {
self.tableView.reloadData()
}

SearchBar does not stay in place (table headerview) - swift

I have made a searchbar in my table header view. I actually would not mind having it in my navigation bar, by setting it to: navigationItem.titleView = searchController.searchBar.. The problem is though that when the searchBar is active, the navigation bar is dismissed - including the searchBar, So you cannot see what you are searching for?
How can I change that? Here is my complete searchBar code:
import UIKit
import FirebaseDatabase
import FirebaseAuth
import FBSDKCoreKit
import FirebaseStorage
class SearchTableViewController: UITableViewController, UISearchResultsUpdating {
#IBOutlet var searchUsersTableView: UITableView!
let searchController = UISearchController(searchResultsController: nil)
var usersArray = [NSDictionary?]()
var filteredUsers = [NSDictionary?]()
let databaseRef = FIRDatabase.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
//tableView.tableHeaderView = searchController.searchBar
navigationItem.titleView = searchController.searchBar
searchController.searchBar.placeholder = "Søg"
searchController.searchBar.setValue("Annuller", forKey:"_cancelButtonText")
let cancelButtonAttributes: NSDictionary = [NSForegroundColorAttributeName: UIColor.whiteColor()]
UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes as? [String : AnyObject], forState: UIControlState.Normal)
databaseRef.child("UserInformation").queryOrderedByChild("userName").observeEventType(.ChildAdded, withBlock: { (snapshot) in
self.usersArray.append(snapshot.value as? NSDictionary)
self.searchUsersTableView.insertRowsAtIndexPaths([NSIndexPath(forRow: self.usersArray.count-1, inSection: 0)], withRowAnimation: .Automatic)
}) { (error) in
print(error.localizedDescription)
}
}
override func viewWillAppear(animated: Bool) {
if let user = FIRAuth.auth()?.currentUser {
let userId = user.uid
FIRDatabase.database().reference().child("Users").child(userId).child("NotificationBadge").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let badgesNumber = value?["numberOfBadges"] as! Int?
if badgesNumber != nil {
self.tabBarController?.tabBar.items?[3].badgeValue = String(badgesNumber!)
} else {
self.tabBarController?.tabBar.items?[3].badgeValue = nil
}
// ...
}) { (error) in
print(error.localizedDescription)
}
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showProfile" {
if let nextVC = segue.destinationViewController as? theProfileTableViewController {
nextVC.viaSegue = sender! as! String
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if searchController.active && searchController.searchBar.text != "" {
return filteredUsers.count
}
return usersArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:SearchTableViewCell = tableView.dequeueReusableCellWithIdentifier("searchCell", forIndexPath: indexPath) as! SearchTableViewCell
let user: NSDictionary?
if searchController.active && searchController.searchBar.text != "" {
user = filteredUsers[indexPath.row]
} else {
user = self.usersArray[indexPath.row]
}
cell.nameLabel.text = user?["userName"] as? String
let profileUserIDPic = user?["usersUID"] as? String
let storage = FIRStorage.storage()
// Refer to your own Firebase storage
let storageRef = storage.referenceForURL("gs://bigr-1d864.appspot.com")
let profilePicRef = storageRef.child(profileUserIDPic!+"/profile_pic.jpg")
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
profilePicRef.dataWithMaxSize(1 * 300 * 300) { (data, error) -> Void in
if (error != nil) {
// Uh-oh, an error occurred!
print("Unable to download image")
cell.profilePic.image = UIImage(named: "profile.png")
cell.profilePic.layer.cornerRadius = cell.profilePic.frame.size.width/2
cell.profilePic.clipsToBounds = true
} else {
// Data for "images/island.jpg" is returned
// ... let islandImage: UIImage! = UIImage(data: data!)
if (data != nil){
cell.profilePic.image = UIImage(data: data!)
cell.profilePic.layer.cornerRadius = cell.profilePic.frame.size.width/2
cell.profilePic.clipsToBounds = true
}
}
}
return cell
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
// update something
filterContent(self.searchController.searchBar.text!)
}
func filterContent(searchText: String) {
self.filteredUsers = self.usersArray.filter{ user in
let username = user!["userName"] as? String
return (username?.lowercaseString.containsString(searchText.lowercaseString))!
}
self.tableView.reloadData()
}
}
When going to the search page it looks like this (because I added the insets
When searching for a user it looks like this
When having click a user and returned to search it looks like this

tableview error while searching

Hi I have two arrays and only one array is updating with search bar.. I keep the TitleArray to show in tableView title and detailsArray to show in tableView subtitle.. once I start searching only title following my typing but subtitle nothing change.
#IBOutlet weak var AirportsTableView: UITableView!
var TitleArray = [String]()
var DetailsArray = [String]()
var NumberOfRows = 0
var filteredNamesArray = [String]()
var filteredDetailsArray = [String]()
var resultSearchController = UISearchController!()
**override func viewDidLoad() {
super.viewDidLoad()**
// Do any additional setup after loading the view.
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController.searchResultsUpdater = self
self.resultSearchController.dimsBackgroundDuringPresentation = false
self.resultSearchController.searchBar.sizeToFit()
self.resultSearchController.loadViewIfNeeded()
self.AirportsTableView.tableHeaderView = self.resultSearchController.searchBar
self.AirportsTableView.reloadData()
parseJSON()
}
func parseJSON() {
if let path = NSBundle.mainBundle().pathForResource("airports", ofType: "json") {
do {
let data = try NSData(contentsOfURL: NSURL(fileURLWithPath: path), options: NSDataReadingOptions.DataReadingMappedIfSafe)
let jsonObj = JSON(data: data)
if jsonObj != JSON.null {
// print("jsonData:\(jsonObj)")
NumberOfRows = jsonObj.count
for i in 0...NumberOfRows {
let City = jsonObj[i]["city"].string as String!
let Country = jsonObj[i]["country"].string as String!
let Iata = jsonObj[i]["iata"].string as String!
let Name = jsonObj[i]["name"].string as String!
self.TitleArray.append("\(City) - \(Country) - \(Iata)")
self.DetailsArray.append("\(Name)")
}
} else {
print("could not get json from file, make sure that file contains valid json.")
}
} catch let error as NSError {
print(error.localizedDescription)
}
} else {
print("Invalid filename/path.")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if self.resultSearchController.active
{
return self.filteredNamesArray.count
} else
{
return self.TitleArray.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell?
if self.resultSearchController.active
{
cell!.textLabel?.text = self.filteredNamesArray[indexPath.row]
} else
{
cell!.textLabel?.text = self.TitleArray[indexPath.row]
cell!.detailTextLabel?.text = self.DetailsArray[indexPath.row]
}
return cell!
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredNamesArray.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.TitleArray as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredNamesArray = array as! [String]
self.AirportsTableView.reloadData()
}
// MARK: - Segues
/*
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "AirportDetails" {
if let indexPath = self.AirportsTableView.indexPathForSelectedRow {
let airportDetail : Airports = TitleArray[indexPath.row]
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! AllWaysFlightsViewController
controller.airportDetail = airportDetail
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
*/
Instead of using two separate arrays use only one array and populate it with object containing both variables you are using to populate the tableView.
class Address {
var city: String
var detail: String
init(city: String, detail:String) {
self.city = city
self.detail = detail
}
}
Parse your json like this:
for i in 0...NumberOfRows {
let City = jsonObj[i]["city"].string as String!
let Country = jsonObj[i]["country"].string as String!
let Iata = jsonObj[i]["iata"].string as String!
let Name = jsonObj[i]["name"].string as String!
let city = "\(City) - \(Country) - \(Iata)"
let address = Address(city: city, detail: Name)
self.TitleArray.append(address)
self.filteredNamesArray.append(address)
}
Filter your title array containing addresses. Your titlearray and filtered array both contains same data for the first time you can refer to the json parsing for this. Here you can use one for filtering and when search bar is empty it user cancel his search you can re-populate your array from the other one.
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredNamesArray.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF.city CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.TitleArray as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredNamesArray = array as! [Address]
self.AirportsTableView.reloadData()
}
your tableView logic will be changed accordingly
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return self.filteredNamesArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell?
let address = self.filteredNamesArray[indexPath.row]
cell!.textLabel?.text = address?.city
cell!.detailTextLabel?.text = address?.detail
return cell!
}
You need to change the way you approach filtering the data so that rather than just apply a predicate you explicitly iterate and check the predicate, if you find a match then you take that item and the corresponding description into your filtered arrays.
Something like:
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredNamesArray.removeAll(keepCapacity: false)
self.filteredDetailsArray.removeAll(keepCapacity: false)
let searchString = searchController.searchBar.text!
var index = 0
for title in self.TitleArray
if title.rangeOfString(searchString).location != NSNotFound {
self.filteredNamesArray.append(title)
self.filteredDetailsArray.append(self.DetailsArray[index])
}
index += 1
}
self.AirportsTableView.reloadData()
}

trying to implement the uisearchcontroller but its not giving any search result

import UIKit
class MasterTableViewController: UITableViewController, PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating {
//approches for uisearchbar
var searchNotes: [PFObject] = [PFObject]()
var notesSearchController = UISearchController()
var searchActive: Bool = false
// creating array for holding ojects
var noteObjects: NSMutableArray! = NSMutableArray()
var v = 0
override func viewDidLoad() {
super.viewDidLoad()
self.notesSearchController = UISearchController(searchResultsController: nil)
self.notesSearchController.dimsBackgroundDuringPresentation = true
self.notesSearchController.searchResultsUpdater = self
// Configure the search controller's search bar
self.notesSearchController.searchBar.placeholder = "Search for a user"
self.notesSearchController.searchBar.sizeToFit()
self.notesSearchController.searchBar.delegate = self
self.definesPresentationContext = true
// Set the search controller to the header of the table
self.tableView.tableHeaderView = self.notesSearchController.searchBar
print("check")
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if v == 0 {
self.fetchAllObjectsFromLocalDataStore()
//self.fetchAllObjects()
}
}
// fetching data from local datastore and from parse
func fetchAllObjectsFromLocalDataStore(){
let query: PFQuery = PFQuery(className: "Sinhgad")
query.orderByDescending("createdAt")
query.fromLocalDatastore()
query.findObjectsInBackgroundWithBlock { ( objects, error) -> Void in
if (error == nil) {
let temp: NSArray = objects as NSArray!
self.noteObjects = temp.mutableCopy() as! NSMutableArray
self.tableView.reloadData()
}else {
print(error!.userInfo)
}
}
}
func fetchAllObjects(){
let query: PFQuery = PFQuery(className: "Sinhgad")
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if (error == nil) {
PFObject.pinAllInBackground(objects, block: nil )
self.fetchAllObjectsFromLocalDataStore()
// self.tableView.reloadData()
} else {
print(error?.userInfo)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if (self.notesSearchController.active) {
return self.searchNotes.count
} else {
return self.noteObjects.count
}}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! MasterTableViewCell
if (self.notesSearchController.active && self.searchNotes.count > indexPath.row) {
// bind data to the search results cell
let object : PFObject = self.noteObjects.objectAtIndex(indexPath.row) as! PFObject
cell.MasterTitleLabel?.text = object["Title"] as? String
cell.MasterTextLabel.text = object["Fstory"] as? String
cell.MasterTimeLabel.text = object["Time"] as? String
cell.MasterLocationLabel.text = object["Location"] as? String
return cell
} else {
let object : PFObject = self.noteObjects.objectAtIndex(indexPath.row) as! PFObject
cell.MasterTitleLabel?.text = object["Title"] as? String
cell.MasterTextLabel.text = object["Fstory"] as? String
cell.MasterTimeLabel.text = object["Time"] as? String
cell.MasterLocationLabel.text = object["Location"] as? String
return cell
}}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if (self.notesSearchController.active && self.searchNotes.count > 0) {
// Segue or whatever you want
self.performSegueWithIdentifier("openStory", sender: self)
} else {
self.performSegueWithIdentifier("openStory", sender: self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let upcoming: AddNoteTableViewController = segue.destinationViewController as! AddNoteTableViewController
if (segue.identifier == "openStory"){
let indexPath = self.tableView.indexPathForSelectedRow!
let object: PFObject = self.noteObjects.objectAtIndex(indexPath.row) as! PFObject
upcoming.object = object
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
#IBAction func btnReload(sender: AnyObject) {
fetchAllObjects()
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == UITableViewCellEditingStyle.Delete ){
let object : PFObject = self.noteObjects.objectAtIndex(indexPath.row) as! PFObject
// the below for deleting the selected cell's object from server's database
// object.deleteInBackground()
//the below for deleting the selected cell's object from localstorage
object.unpinInBackground()
self.noteObjects.removeObjectAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
// MARK: - Parse Backend methods
func loadSearchUsers(searchString: String) {
let query: PFQuery = PFQuery(className: "Sinhgad")
query.orderByDescending("createdAt")
// Filter by search string
query.whereKey("Notes", containsString: searchString)
self.searchActive = true
query.findObjectsInBackgroundWithBlock {(objects, error) -> Void in
if (error == nil) {
self.searchNotes.removeAll(keepCapacity: false)
self.searchNotes += objects as [PFObject]!
self.tableView.reloadData()
} else {
// Log details of the failure
print("search query error: \(error) \(error!.userInfo)")
}
self.searchActive = false
}
}
// MARK: - Search Bar Delegate Methods
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
// Force search if user pushes button
let searchString: String = searchBar.text!.lowercaseString
if (searchString != "") {
loadSearchUsers(searchString)
}
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
// Clear any search criteria
searchBar.text = ""
// Force reload of table data from normal data source
}
// MARK: - UISearchResultsUpdating Methods
// This function is used along with UISearchResultsUpdating for dynamic search results processing
// Called anytime the search bar text is changed
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchString: String = searchController.searchBar.text!.lowercaseString
if (searchString != "" && !self.searchActive) {
loadSearchUsers(searchString)
}
}
}
The above code is for retrieving stored objects from parse's server and from local storage and show them in table view.
Everything is working fine but I am trying to implement searchbar for adding searching function into my app. The problem is that when am running the app its showing the searchbar but when interacting with search bar its moving to upside and disappearing and when am typing anything.
I am not getting any search result and in NSLog am getting this :
2015-12-03 16:43:48.769 Notes[1015:56944] Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UISearchController: 0x7ff2d48165a0>)
I know am missing something and its not the right way to achieve that function.
If somebody knows how to do it correctly or what am missing than please let me know , thanks and sorry if the way am asking question is not proper !
i just figured out that my 'searchNotes' var of Pfobject have nothing i mean its empty ! for that i tried
cell.MasterTitleLabel?.text = searchNotes["Title"] as! String
but its giving error
cannot subscript a value of type '[PFObject]' with an index of type 'string'
i know its because i declared searchNotes as
searchNotes [PFObject] = [PFObject]()
i should do it something like
searchNotes PFObject = PFObject()
but when am doing this its giving so many errors please help if somebody's know how to fix this
Maybe you shouldn't user PFObject directly.
Can you use another class instead of PFObject
private class object {
var mTitle : String!
var mStory : String!
var mTime : String!
var mLocation : String!
}
And use your code here
let obj : object = object()
obj.mTitle = PFObject["title"];
...etc

Retrieving data from parse in NSMutable array and then get the data from the cells where the button has been tapped

So this is how I'm retrieving all the data and then added a custom button this way :
import UIKit
class userListTableViewController: UITableViewController {
var data:NSMutableArray = NSMutableArray()
func loadData() {
data.removeAllObjects()
var userQuery = PFUser.query()
userQuery?.orderByAscending("createdAt")
userQuery?.findObjectsInBackgroundWithBlock({ (objects, erroe) -> Void in
if let objects = objects {
for object in objects {
if let user = object as? PFUser {
if user.objectId != PFUser.currentUser()?.objectId {
self.data.addObject(object)
}
}
}
}
self.tableView.reloadData()
})
}
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("users", forIndexPath: indexPath) as! userListTableViewCell
let userData:PFObject = self.data.objectAtIndex(indexPath.row) as! PFObject
// Usernames and gender..
myCell.fullName.text = userData.objectForKey("fullName") as! String!
myCell.genderLabel.text = userData.objectForKey("gender") as! String!
// Profile pictures..
let profilePics = userData.objectForKey("profilePicture") as! PFFile
profilePics.getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
myCell.dp.image = downloadedImage
}
}
myCell.followButtton.removeTarget(nil, action: nil, forControlEvents: UIControlEvents.AllEvents)
myCell.followButtton.addTarget(self, action: "followButtonTapped:", forControlEvents: UIControlEvents.TouchUpInside)
return myCell
}
// IBActions..
func followButtonTapped(sender:AnyObject) {
let buttonPosition = sender.convertPoint(CGPointZero, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)
if indexPath != nil {
if let cell = self.tableView.cellForRowAtIndexPath(indexPath!) as? userListTableViewCell {
cell.followButtton.setTitle("unfollow", forState: UIControlState.Normal)
var followers:PFObject = PFObject(className: "Followers")
followers["follower"] = PFUser.currentUser()?.objectId
followers["user"] = //*********************** here i want to save the objectId of the user being tapped on. want to get the NSMutableArray index from the indexPath
}
println(indexPath!)
}
}
my problem is there where you see ************************. now like in cellForRowAtIndexPath I've used this to show the data :
let userData:PFObject = self.data.objectAtIndex(indexPath.row) as! PFObject
same like this i want to do it in func followButton(sender:AnyObject).
You should not call self.tableView.reloadData() in the for loop, you should do that only once at the end of the for loop when all the data is loaded.
self.data.objectAtIndex(indexPath.row) this worked.

Resources