xCode/Firebase Search bar cell fail - ios

I have now tried everything (as far as I know), I even made the whole thing from scratch, but it still doesn´t work.
I have made a search bar which can search for data in firebase and display them in a tableview. If the user clicks on a profile in the search bar, a new viewcontroller shows with information about that user.
The problem is that if you start searching, then clicks on a profile, it shows the profile which started on that position in the tableview before the search happened.
This is what I see without searching, it displays the 2 profiles in firebase which is correct:
Now, when I search for the profile "Lars Larsen" it filters like it should:
However, if I now choose the profile by clicking on "Lars Larsen" it shows the profile for "Jonas Larsen", which was at the top before the search?
This is the code for my searchViewController:
import UIKit
import FirebaseDatabase
class SearchTableViewController: UITableViewController,
UISearchResultsUpdating {
let searchController = UISearchController(searchResultsController: nil)
#IBOutlet var findKunder: UITableView!
var loggedInUser: user?
var usersArray = [NSDictionary?]()
var filteredUsers = [NSDictionary?]()
var databaseRef = Database.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
databaseRef.child("Buyers").queryOrdered(byChild: "Personnr").observe(.childAdded, with: { (snapshot) in
let key = snapshot.key
let snapshot = snapshot.value as? NSDictionary
snapshot?.setValue(key, forKey: "Personnr")
self.usersArray.append(snapshot)
//Insert rows
self.findKunder.insertRows(at: [IndexPath(row:self.usersArray.count-1,section:0)], with: UITableViewRowAnimation.automatic)
}) { (error) in
print(error)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in 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 searchController.isActive && searchController.searchBar.text != ""{
return filteredUsers.count
}
return self.usersArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let user : NSDictionary?
if searchController.isActive && searchController.searchBar.text != ""{
user = filteredUsers[indexPath.row]
}
else
{
user = self.usersArray[indexPath.row]
}
cell.textLabel?.text = user? ["Navn"] as? String
cell.detailTextLabel?.text = user?["Telefonnr"] as? String
// Configure the cell...
return cell
}
func updateSearchResults(for searchController: UISearchController) {
filterContent(searchText: self.searchController.searchBar.text!)
}
func filterContent(searchText:String)
{
self.filteredUsers = self.usersArray.filter{ user in
var fNavn = false
var personNr = false
var searchBil = false
var telefonNr = false
var korekortNr = false
if let Navn = user!["Navn"] as? String {
fNavn = Navn.lowercased().contains(searchText.lowercased())
}
if let Bil = user!["Bil"] as? String {
searchBil = Bil.lowercased().contains(searchText.lowercased())
}
if let Personnr = user!["Personnr"] as? String {
personNr = Personnr.lowercased().contains(searchText.lowercased())
}
if let Kørekortnr = user!["Kørekortnr"] as? String {
korekortNr = Kørekortnr.lowercased().contains(searchText.lowercased())
}
if let Telefonnr = user!["Telefonnr"] as? String {
telefonNr = Telefonnr.lowercased().contains(searchText.lowercased())
}
return fNavn || personNr || searchBil || korekortNr || telefonNr
}
tableView.reloadData()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
let showUserProfileViewController = segue.destination as! userProfileViewController
showUserProfileViewController.loggedInUser = self.loggedInUser
if let indexPath = tableView.indexPathForSelectedRow {
let user = usersArray[indexPath.row]
showUserProfileViewController.otherUser = user
}
}
}
This is the code I use to show the profiles:
import UIKit
import Firebase
class ProfileViewController: UIViewController {
//Outlets
var loggedInUser:User?
var otherUser:NSDictionary?
var databaseRef:DatabaseReference!
var loggedInUserData: NSDictionary?
#IBOutlet weak var Biler: UILabel!
#IBOutlet weak var Navn: UILabel!
#IBOutlet weak var kundeInfo: UILabel!
#IBOutlet weak var bilInfo: UILabel!
#IBOutlet weak var telefonNr: UILabel!
#IBOutlet weak var korekortNr: UILabel!
#IBOutlet weak var personNr: UILabel!
#IBOutlet weak var Interesse: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.databaseRef = Database.database().reference()
databaseRef.child("Buyers").child(self.otherUser?["Personnr"] as! String).observe(.value, with: { (snapshot) in
let uid = self.otherUser?["Personnr"] as! String
self.otherUser = snapshot.value as? NSDictionary
self.otherUser?.setValue(uid, forKey: "Personnr")
self.Navn.text = self.otherUser?["Navn"] as? String
self.bilInfo.text = self.otherUser?["Bil"] as? String
self.telefonNr.text = self.otherUser?["Telefonnr"] as? String
self.Interesse.text = self.otherUser?["Interesse"] as? String
self.personNr.text = self.otherUser?["Personnr"] as? String
self.korekortNr.text = self.otherUser?["Kørekortnr"] as? String
}
// Do any additional setup after loading the view.
)}
Please let me know if you need any other information. I hope you can help.

If the search bar is active and it does contain text, you should pass a user to ProfileViewController from the filteredUsers array and not usersArray.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
[...]
if let indexPath = tableView.indexPathForSelectedRow {
if searchController.isActive && searchController.searchBar.text != "" {
showUserProfileViewController.otherUser = filteredUsers[indexPath.row]
} else {
showUserProfileViewController.otherUser = usersArray[indexPath.row]
}
}
}
On a side note, you shouldn't us NSDictionary in Swift. Use Swift's Dictionary instead (for your case, if would be [String: Any]).

If I'm understanding your code correctly, your issue is here:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
let showUserProfileViewController = segue.destination as! userProfileViewController
showUserProfileViewController.loggedInUser = self.loggedInUser
if let indexPath = tableView.indexPathForSelectedRow {
let user = usersArray[indexPath.row]
showUserProfileViewController.otherUser = user
}
}
let user = usersArray[indexPath.row] // this is wrong
Since you filtered the results if the search bar is active you have to use your updated datasource
let user = filteredUsers[indexPath.row]
Basically, you're passing the wrong information before you segue.

Related

Passing data from Firebase Database from UITableviewCell to next ViewController not showing

I've already searched through some of the answers that have been asked about this but none of them seem to apply/work in my situation which is why I've decided to ask the community. I am simply trying to pass the data from the table view cell to the next view controller. I have gotten as far as being able to display the information in my cells accurately however whenever i select the row it just shows the view controller with no information
I have tried to set the labels and pictures to whatever the UITableViewCell may show but it is not working. I created an NSObject class that defines the variables which is why it is confusing me as to how to pass the data through to the next view Controller.
This is my AddFriendViewController where I fetch the users from Firebase and it displays my information on the tableview
class AddFriendViewController: UIViewController {
var users = [Users]()
var databaseRef = Database.database().reference()
#IBOutlet weak var friendsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
friendsTableView.delegate = self
friendsTableView.dataSource = self
fetchUser()
}
func fetchUser() {
databaseRef.child("users").observe(.childAdded) { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = Users()
user.nameOfUser = dictionary["nameOfUser"] as? String ?? ""
user.email = dictionary["email"] as? String ?? ""
user.profileImageURL = dictionary["profileImageURL"] as? String ?? ""
self.users.append(user)
DispatchQueue.main.async {
self.friendsTableView.reloadData()
}
}
}
}
}
extension AddFriendViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.users.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let friendCell = UITableViewCell(style: .subtitle, reuseIdentifier: "friendCell")
let user = users[indexPath.row]
friendCell.textLabel?.text = user.nameOfUser
friendCell.detailTextLabel?.text = user.email
if let profileImageURL = user.profileImageURL {
let url = URL(string: profileImageURL)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error)
return
}
DispatchQueue.main.async {
friendCell.imageView?.image = UIImage(data: data!)
}
}.resume()
}
return friendCell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showFriendProfile", sender: self.users[indexPath.row])
self.friendsTableView.deselectRow(at: indexPath as IndexPath, animated: true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showFriendProfile" {
if let indexPath = friendsTableView.indexPathForSelectedRow {
let dvc = segue.destination as! DetailViewController
***This is where I am confused as to what I should be doing***
//EDIT1:
print("The nameOfUser is \(user.nameOfUser!)")
print("The email is \(user.email!)")
}
}
}
}
This is my Users Class:
class Users: NSDictionary {
var nameOfUser: String?
var email: String?
var profileImageURL: String?
}
This is my DetailViewController:
class DetailViewController: UIViewController {
var nameOfUser = String()
var email = String()
var profileImageURL = UIImage()
var ref: DatabaseReference?
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var nameOfUserLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
nameOfUser = nameOfUserLabel.text!
email = emailLabel.text!
profileImageURL = profileImageView.image!
}
}
The obvious goal is to simply click on the cell to show the data on the next view controller. I understand similar questions have been asked in the past but I truly don't know how to use those as the solution to my problem. Any help will be greatly appreciated and please let me know if there is anything I need to clarify.
EDIT1:
I added the print statement on the prepare for segue function and noticed it is at least pulling the information but for some reason not passing it to the next view controller.
Thank you
You just need to get your sender and set the properties of the detail view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showFriendProfile" {
guard let dvc = segue.destination as? DetailViewController else {
return
}
if let user = sender as? Users {
DispatchQueue.main.async {
dvc.nameOfUserLabel.text = user.nameOfUser
dvc.emailLabel.text = user.email
let url = URL(string: user.profileImageURL!)
let data = try? Data(contentsOf: url!)
dvc.profileImageView.image = UIImage(data: data!)
}
}
}
}
1- Send the object ( Make sure to connect the segue source to the vc itself not to the cell )
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showFriendProfile" {
if let indexPath = friendsTableView.indexPathForSelectedRow {
let dvc = segue.destination as! DetailViewController
dvc.user = sender as! Users
}
}
}
class DetailViewController: UIViewController {
var user:Users! // add this then inside viewDidLoad set the labels
}
2- Don't ise URLSession.shared.dataTask(with: url!) { (data, response, error) inside cellForRowAt consider using SDWebImage
import SDWebImage // install pods then add this line top of the vc
friendCell.imageView?.sd_setImage(with: URL(string:urlStr), placeholderImage: UIImage(named: "placeholder.png"))
3- No need for DispatchQueue.main.async { inside
DispatchQueue.main.async {
self.friendsTableView.reloadData()
}
As firebase callbacks run in main thread by default

iOS / Swift - Appending a filter to retrieving data from Firebase

What I got so far is a tableView and custom Cells about hookah tobacco. Those include an image, name, brand and ID. Now what I try to reach is basically a tableview that contains only the cells with attributes based on a "filter". For example the tableView that appears at the beginning has only the following two settings to make it simple: PriceRange and BrandName. At the first time loading the tableView those are PriceRange: 0 - 100 and Brands: all brands. Then imagine a user restricting those like 0 - 15 Euros and only brand called "7 Days". How exactly would I do that with reloading the tableView?
import UIKit
import Firebase
class ShopViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var button_filter: UIBarButtonItem!
#IBOutlet weak var searchBar_shop: UISearchBar!
#IBOutlet weak var view_navigator: UIView!
#IBOutlet weak var tableView_shop: UITableView!
var ShopCells: [ShopCell] = []
var databaseRef: DatabaseReference!
var storageRef: StorageReference!
override func viewDidLoad() {
super.viewDidLoad()
self.databaseRef = Database.database().reference()
self.storageRef = Storage.storage().reference()
createArray() { shopCells in
for item in shopCells {
self.ShopCells.append(item)
}
DispatchQueue.main.async {
self.tableView_shop.reloadData()
}
}
self.navigationItem.title = "Shop"
self.tableView_shop.delegate = self
self.tableView_shop.dataSource = self
self.searchBar_shop.delegate = self
self.searchBar_shop.barTintColor = UIColor(hexString: "#1ABC9C")
self.view_navigator.backgroundColor = UIColor(hexString: "#1ABC9C")
self.tableView_shop.separatorColor = UIColor.clear
self.searchBar_shop.isTranslucent = false
self.searchBar_shop.backgroundImage = UIImage()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ShopViewController.viewTapped(gestureRecognizer:)))
view.addGestureRecognizer(tapGesture)
}
#objc func viewTapped(gestureRecognizer: UITapGestureRecognizer) {
view.endEditing(true)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.searchBar_shop.resignFirstResponder()
}
func createArray(completion: #escaping ([ShopCell]) -> () ) {
var tempShopCells: [ShopCell] = []
let rootRef = Database.database().reference()
let query = rootRef.child("tobaccos").queryOrdered(byChild: "name")
query.observeSingleEvent(of: .value) { (snapshot) in
let dispatchGroup = DispatchGroup()
for child in snapshot.children.allObjects as! [DataSnapshot] {
let value = child.value as? [String: Any];
let name = value?["name"] as? String ?? "";
let brand = value?["brand"] as? String ?? "";
let iD = value?["iD"] as? String ?? "";
dispatchGroup.enter()
let imageReference = Storage.storage().reference().child("tobaccoPictures").child("\(iD).jpg")
imageReference.getData(maxSize: (1 * 1024 * 1024)) { (data, error) in
if let _error = error{
print(_error)
} else {
if let _data = data {
let image: UIImage! = UIImage(data: _data)
tempShopCells.append(ShopCell(productName: name, brandName: brand, productImage: image, iD: iD))
}
}
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
completion(tempShopCells)
}
}
}
}
extension ShopViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.ShopCells.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let shopCell = ShopCells[indexPath.row]
let cell = tableView_shop.dequeueReusableCell(withIdentifier: "ShopCell") as! ShopTableViewCell
cell.setShopCell(shopCell: shopCell)
return cell
}
}

How to pull users from database and list them in a table view using firebase?

I'm using firebase to make an iOS app. I want to retrieve all the users on my database and display their name and profile picture in a table view. Here is my code for my TableViewCell:
import UIKit
import FirebaseDatabase
import FirebaseAuth
import SDWebImage
class HomeTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var likeImageView: UIImageView!
#IBOutlet weak var messageImageView: UIImageView!
#IBOutlet weak var likeCountButton: UIButton!
var homeVC: HomeViewController?
var postReference: DatabaseReference!
var post: UserFile?{
didSet {
updateView()
}
}
var user: UserFile? {
didSet {
updateUserInfo()
}
}
override func awakeFromNib() {
super.awakeFromNib()
nameLabel.text = ""
let berryTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleLikeTap))
likeImageView.addGestureRecognizer(berryTapGesture)
likeImageView.isUserInteractionEnabled = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func updateView() {
if let photoURL = post?.picURL {
profileImageView.sd_setImage(with: URL(string: photoURL))
}
API.Post.REF_POSTS.child(post!.id!).observeSingleEvent(of: .value, with: { postSnapshot in
if let postDictionary = postSnapshot.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: postSnapshot.key)
self.updateLike(post: post)
}
})
API.Post.REF_POSTS.child(post!.id!).observe(.childChanged, with: { snapshot in
if let value = snapshot.value as? Int {
self.likeCountButton.setTitle("\(value) berries", for: .normal)
}
})
}
func updateLike(post: UserFile) {
let imageName = post.berries == nil || !post.isBerried! ? "berry" : "berrySelected"
likeImageView.image = UIImage(named: imageName)
// display a message for berries
guard let count = post.berryCount else {
return
}
if count != 0 {
likeCountButton.setTitle("\(count) berries", for: .normal)
} else if post.berryCount == 0 {
likeCountButton.setTitle("Be the first to Like this", for: .normal)
}
}
func incrementberries(forReference ref: DatabaseReference) {
ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = Auth.auth().currentUser?.uid {
var berries: Dictionary<String, Bool>
berries = post["berries"] as? [String : Bool] ?? [:]
var likeCount = post["berryCount"] as? Int ?? 0
if let _ = berries[uid] {
// Unlike the post and remove self from stars
likeCount -= 1
berries.removeValue(forKey: uid)
} else {
// Like the post and add self to stars
likeCount += 1
berries[uid] = true
}
post["berryCount"] = likeCount as AnyObject?
post["berries"] = berries as AnyObject?
currentData.value = post
return TransactionResult.success(withValue: currentData)
}
return TransactionResult.success(withValue: currentData)
}) { (error, committed, snapshot) in
if let error = error {
print(error.localizedDescription)
}
if let postDictionary = snapshot?.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: snapshot!.key)
self.updateLike(post: post)
}
}
}
func handleLikeTap() {
postReference = API.Post.REF_POSTS.child(post!.id!)
incrementberries(forReference: postReference)
}
override func prepareForReuse() {
super.prepareForReuse()
profileImageView.image = UIImage(named: "industribune-default-no-profile-pic")
}
func updateUserInfo() {
nameLabel.text = user?.username
if let photoURL = user?.profileImageURL {
profileImageView.sd_setImage(with: URL(string: photoURL), placeholderImage: UIImage(named: "industribune-default-no-profile-pic"))
}
}
}
I am displaying this cell on my HomeViewController:
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import Firebase
class HomeViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
var posts = [UserFile]()
var users = [UserFile]()
override func viewDidLoad() {
super.viewDidLoad()
// for performance set an estimated row height
tableView.estimatedRowHeight = 1
// but also request to dynamically adjust to content using AutoLayout
tableView.rowHeight = UITableViewAutomaticDimension
//tableView.delegate = self
tableView.dataSource = self
loadPosts()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadPosts() {
activityIndicatorView.startAnimating()
API.User.observePosts { (newPost) in
guard let userID = newPost.uid else { return }
self.fetchUser(uid: userID, completed: {
// append the new Post and Reload after the user
// has been cached
self.posts.append(newPost)
self.activityIndicatorView.stopAnimating()
self.tableView.reloadData()
})
}
}
func fetchUser(uid: String, completed: #escaping () -> Void) {
API.User.observeUser(withID: uid) { user in
self.users.append(user)
completed()
}
}
}
extension HomeViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
cell.post = posts[indexPath.row]
cell.user = users[indexPath.row]
cell.homeVC = self
return cell
}
}
I have a lot of craziness going on in my project so let me know if you have any questions and what I'm doing wrong. If it's too complicated to understand I'm ready to erase everything and start over too.
And I do honestly think that I followed all the guidelines to ask a question so don't like shut this question down or something.
That's a lot of code. Try this super reduced example. For this, the users node only stores the name as a child node but it could also have an image, email, address, etc.
Example users node
users
uid_0:
name: "Bert"
uid_1:
name: "Ernie"
and some code
var usersArray = [ [String: Any] ]() //an array of dictionaries.
class ViewController: UIViewController {
//set up firebase references here
override func viewDidLoad() {
super.viewDidLoad()
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let userDict = snap.value as! [String: Any]
self.usersArray.append(userDict)
}
self.tableView.reloadData()
})
and the tableView delegate methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.usersArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
let userDict = self.usersArray[indexPath.row]
cell.text = userDict["name"] as! String
//cell.imge = userDict["image"] etc etc
return cell
}
Now... that all being said. This is the perfect use for an array of UserClass objects instead of the dictionaries.
Here's a starting point....
class UserClass {
var name = ""
var image = ""
func init(snap: DataSnapshot) {
//populate the vars from the snapshot
}
}
var userClassArray = [UserClass]()
Don't copy and paste this as there are probably typos but it should point you in the right direction.

Send array by segue to new view controller swift iOS 9

I am attempting to send an array of data to a new view controller and I'm currently getting the error fatal error: unexpectedly found nil while unwrapping an Optional value
Im using the API data from www.thecocktaildb.com
Example:
http://www.thecocktaildb.com/api/json/v1/1/search.php?s=margarita
Not sure what I'm doing wrong. Tried debugging and checking values before the segue in my search view controller and they're accurate.
Heres my code:
Main Storyboard
SearchViewController
class SearchViewController: UIViewController, UISearchBarDelegate, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var TableView: UITableView!
#IBOutlet weak var SearchBar: UISearchBar!
var valueToPass:Drinks!
var isSearching : Bool = false
class Drinks {
var idDrink: Int = 0
var strDrink: String = ""
var strCategory: String = ""
var strAlcoholic: String = ""
var strGlass: String = ""
var strInstructions: String = ""
var strDrinkThumb: String = ""
var strIngredient1: String = ""
var strIngredient2: String = ""
var strIngredient3: String = ""
var strIngredient4: String = ""
var strIngredient5: String = ""
var strIngredient6: String = ""
var strIngredient7: String = ""
var strIngredient8: String = ""
var strIngredient9: String = ""
var strIngredient10: String = ""
var strIngredient11: String = ""
var strIngredient12: String = ""
var strIngredient13: String = ""
var strIngredient14: String = ""
var strIngredient15: String = ""
var strMeasure1: String = ""
var strMeasure2: String = ""
var strMeasure3: String = ""
var strMeasure4: String = ""
var strMeasure5: String = ""
var strMeasure6: String = ""
var strMeasure7: String = ""
var strMeasure8: String = ""
var strMeasure9: String = ""
var strMeasure10: String = ""
var strMeasure11: String = ""
var strMeasure12: String = ""
var strMeasure13: String = ""
var strMeasure14: String = ""
var strMeasure15: String = ""
}
var TableData:Array< Drinks > = Array < Drinks >()
override func viewDidLoad() {
super.viewDidLoad()
for subView in self.SearchBar.subviews
{
for subsubView in subView.subviews
{
if let textField = subsubView as? UITextField
{
textField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Search", comment: ""))
}
}
}
self.SearchBar.delegate = self
self.TableView.delegate = self
self.TableView.dataSource = self
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if self.SearchBar.text!.isEmpty {
self.isSearching = false
}else{
self.isSearching = true
let userSearchInput = self.SearchBar.text!.lowercaseString
let newString = userSearchInput.stringByReplacingOccurrencesOfString(" ", withString: "%20", options: NSStringCompareOptions.LiteralSearch, range: nil)
let postEndpoint: String = "http://www.thecocktaildb.com/api/json/v1/1/search.php?s=" + newString
guard let url = NSURL(string: postEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = NSURLRequest(URL: url)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler: { (data, response, error) in
guard let responseData = data else {
print("Error: did not receive data")
return
}
guard error == nil else {
print("error calling GET on www.thecocktaildb.com")
print(error)
return
}
let post: NSDictionary
do {
post = try NSJSONSerialization.JSONObjectWithData(responseData,
options: []) as! NSDictionary
} catch {
print("error trying to convert data to JSON")
return
}
var count = 1
if let drinks = post["drinks"] as? [NSDictionary] {
self.TableData.removeAll()
for drink in drinks {
let adrink = Drinks()
if let strDrink = drink["strDrink"] as? String {
print(String(count) + ". " + strDrink)
adrink.strDrink = strDrink
count++
}
if let strCategory = drink["strCategory"] as? String {
print(" Category: " + strCategory)
adrink.strCategory = strCategory
}
if let strDrinkThumb = drink["strDrinkThumb"] as? String {
print(" Thumbnail Image: " + strDrinkThumb)
adrink.strDrinkThumb = strDrinkThumb
}
self.TableData.append(adrink)
self.TableView.reloadData()
}
}
})
task.resume()
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
//title = TableData[indexPath.row].strDrink
cell.textLabel?.text = TableData[indexPath.row].strDrink;
let imageString = TableData[indexPath.row].strDrinkThumb
if (imageString == ""){
let noDrinkImage : UIImage = UIImage(named: "noimage.jpg")!
cell.imageView!.image = noDrinkImage
}else{
let drinkImage = UIImage(data: NSData(contentsOfURL: NSURL(string:TableData[indexPath.row].strDrinkThumb)!)!)
cell.imageView!.image = drinkImage
}
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
print(TableData[indexPath.row].strDrink)
valueToPass = TableData[indexPath.row]
//self.performSegueWithIdentifier("drinkSegue", sender: TableData[indexPath.row])
}
// hide kwyboard when search button clicked
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
self.SearchBar.resignFirstResponder()
}
// hide keyboard when cancel button clicked
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
self.SearchBar.text = ""
self.SearchBar.resignFirstResponder()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "DrinkSegue") {
// initialize new view controller and cast it as your view controller
let drinkViewController = segue.destinationViewController as! DrinkViewController
// your new view controller should have property that will store passed value
drinkViewController.passedValue = valueToPass
}
}
}
DrinkViewController.swift
class DrinkViewController: UIViewController {
#IBOutlet weak var DrinkNameLabel: UILabel!
var passedValue : SearchViewController.Drinks!
override func viewDidLoad() {
super.viewDidLoad()
DrinkNameLabel.text = passedValue!.strDrink
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Do it like this instead
In your didSelectRowAtIndexPath pass the array
self.performSegueWithIdentifier("drinkSegue", sender: TableData[indexPath.row])
Here you need to pass the array to your DrinkViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "DrinkSegue") {
// initialize new view controller and cast it as your view controller
let drinkViewController = segue.destinationViewController as! DrinkViewController
// your new view controller should have property that will store passed value
drinkViewController.passedValue = valueToPass
// declare myArray in your drinkViewController and then assign it here
// now your array that you passed will be available through myArray
drinkViewController.myArray = sender
}
}
Update
After I got your project I noticed that the issue you had was that you did drag a segue from the tableView to the drinksController directly - what happened is that didSelectRowAtIndexPath will not be called and your sender will always be nil drinkViewController.myArray = sender as! Drinks.
I changed that by dragging the segue from the viewController to the drinksController instead.

How to pass parse data from tableview to detail view controller?

New to parse backend and coding all together...
Looking to pass data from my HomeTimelineViewController(VC #1), to my ProductDetailViewController(VC #2) through the UIStoryboard segue.
Here is my code for VC #1 :
import UIKit
import Parse
class HomeTimelineViewController: UIViewController, UITableViewDelegate {
#IBOutlet var homeTimelineTableView: UITableView!
var imagePNG = [PFFile]()
var shortDescription = [String]()
var productTitle = [String]()
var productPrice = [String]()
override func viewDidLoad() {
super.viewDidLoad()
var query = PFQuery(className: "Product")
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock {
(products: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// success fetching objects
for product in products! {
self.imagePNG.append(product["imagePNG"] as! PFFile)
self.shortDescription.append(product["shortDescription"] as! String)
self.productTitle.append(product["title"] as! String)
self.productPrice.append(product["price"] as! String)
}
// reload the timeline table
self.homeTimelineTableView.reloadData()
}else {
println(error)
}
}
}
// table view population beginning
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return imagePNG.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let singleCell: ProductTableViewCell = tableView.dequeueReusableCellWithIdentifier("Product Cell") as! ProductTableViewCell
// short description
singleCell.productCellShortDescriptionLabel.text = shortDescription[indexPath.row]
// price
singleCell.productCellPriceLabel.text = productPrice[indexPath.row]
// title
singleCell.productCellTitleLabel.text = productTitle[indexPath.row]
// image
imagePNG[indexPath.row].getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if imageData != nil {
let image = UIImage(data: imageData!)
singleCell.productCellImageview.image = image
}
}
return singleCell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var productDetailVC: ProductDetailViewController = segue.destinationViewController as! ProductDetailViewController
productDetailVC.productDetailTitleLabel = shortDescription
}
}
Here is my code for VC #2 (DetailView):
import UIKit
import Parse
class ProductDetailViewController: UIViewController {
#IBOutlet var tagProduct: UIButton!
#IBOutlet var productDetailTitle: UITextView!
#IBOutlet var productDetailImage: UIImageView!
#IBOutlet var productDetailShortDescription: UITextView!
#IBOutlet var productDetailLongDescription: UITextView!
var productDetailTitleLabel = [String]()
var productDetailImageView = [PFFile]()
var productDetailShortDescriptionLabel = [String]()
var productDetailLongDescriptionLabel = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// tag product button
tagProduct.layer.borderColor = UIColor.blackColor().CGColor
tagProduct.layer.borderWidth = 0.5
tagProduct.layer.cornerRadius = 5
productDetailTitle.text = productDetailTitleLabel
productDetailShortDescription.text = productDetailShortDescriptionLabel
productDetailLongDescription.text = productDetailLongDescriptionLabel
}
}
I can't continue with my code because it keeps giving me an error:
"Cannot assign a value of type '[(String)]' to a value of type 'String!'.
Any tips? Thank you!
Try this:
In TableViewController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var productDetailVC: ProductDetailViewController = segue.destinationViewController as! ProductDetailViewController
if let selectedArrayIndex = tableView.indexPathForSelectedRow()?.row {
productDetailVC.productDetailTitleLabel = shortDescription[selectedArrayIndex]
}
}
In DetailsView change this:
var productDetailTitleLabel = [String]()
to
var productDetailTitleLabel = String()

Resources