Search bar will only show first Cell in detail view - swift

I have created a TableView with a Search Bar in Swift.
I have created 3 searchable objects. (131, 132, 137)
When i search for 137 and press 137(which is the only option to press), i still get to 131 detailView. Anyone know why? Thanks
Here is my code:
import UIKit
class TableViewController: UITableViewController, UISearchResultsUpdating {
let TitleList = ["131","132","137"]
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController.searchResultsUpdater = self
self.resultSearchController.dimsBackgroundDuringPresentation = false
self.resultSearchController.searchBar.sizeToFit()
self.tableView.tableHeaderView = self.resultSearchController.searchBar
self.tableView.reloadData()
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: TableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! TableViewCell
cell.LabelTitle.text = TitleList[indexPath.row]
cell.CellDescription.text = DescriptionList[indexPath.row]
let imagename = UIImage(named: ImageList[indexPath.row])
cell.CellImage.image = imagename
if self.resultSearchController.active {
cell.LabelTitle?.text = self.filteredfarger[indexPath.row]
}
else {
cell.LabelTitle?.text = self.TitleList[indexPath.row]
}
return cell
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredfarger.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.TitleList as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredfarger = array as! [String]
self.tableView.reloadData()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "DetailView") {
let VC = segue.destinationViewController as! DetailedViewController
if let indexpath = self.tableView.indexPathForSelectedRow {
let Title = TitleList[indexpath.row] as String
VC.SentData1 = Title
}

Because when you do the search the row for 137 is 0, and then in prepareForSegue you use the tableView.indexPathForSelectedRow (which means indexPath.row == 0) to get the title data. Try doing something like in your tableView.cellForRowAtIndexPath function where you check if resultSearchController is active or not, and if it is use filteredfarger instead of TitleList. Something like this maybe:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "DetailView") {
let VC = segue.destinationViewController as! DetailedViewController
if let indexpath = self.tableView.indexPathForSelectedRow {
if self.resultSearchController.active {
let Title = self.filteredfarger[indexpath.row]
VC.SentData1 = Title
}
else {
let Title = TitleList[indexpath.row] as String
VC.SentData1 = Title
}
}
}
}
Better yet, you already give the cell some info, why not just use that, kinda like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "DetailView") {
let VC = segue.destinationViewController as! DetailedViewController
if let cell = sender as? TableViewCell {
VC.SentData1 = cell.LabelTitle.text
}
}
}

Related

When tapped on search bar, the app crashes

The app crashes with the following error when the search bar is tapped:
Not able to understand why?
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier ContactCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
This is my code:
import UIKit
class ContactViewController: UITableViewController, UISearchResultsUpdating {
var dataSource: [Contact] = []
var filteredResult = [Contact]()
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
downloadJSONUrl()
}
func downloadJSONUrl() {
let urlString = "https://us-central1-practo-contacts-sample.cloudfunctions.net/get"
let url = NSURL(string: urlString)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response , error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
if let NameArray = jsonObj?.value(forKey: "contacts") as? [[String: Any]] {
for names in NameArray {
var cont = Contact()
if let name = names["name"] as? String {
cont.name = name
}
if let ph = names["number"] as? String {
cont.phoneNumber = ph
}
self.dataSource.append(cont)
}
self.dataSource.sort {$0.name.lowercased() < $1.name.lowercased()}
}
OperationQueue.main.addOperation {
self.tableView.reloadData()
}
}
}).resume()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.dataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell", for: indexPath) as UITableViewCell
let contact = self.dataSource[indexPath.row]
cell.textLabel?.text = contact.name
cell.detailTextLabel?.text = contact.phoneNumber
return cell
}
func updateSearchResults(for searchController: UISearchController) {
if searchController.searchBar.text! == "" {
filteredResult = dataSource
} else {
filteredResult = dataSource.filter { $0.name.lowercased().contains(searchController.searchBar.text!.lowercased()) }
}
self.tableView.reloadData()
}
#IBAction func unwindToContactList(segue: UIStoryboardSegue) {
guard let viewController = segue.source as? AddOrEditViewController else { return }
if let name = viewController.nameTextField.text, let phoneNumber = viewController.phoneNumberTextField.text {
let contact = Contact(name: name, phoneNumber: phoneNumber)
self.dataSource.append(contact)
tableView.reloadData()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "contactDetailsSegue" {
guard let viewController = segue.destination as? ContactDetialsViewController else {
return
}
guard let indexPath = tableView.indexPathForSelectedRow else { return }
let contact = self.dataSource[indexPath.row]
viewController.contact = contact
}
}
}
Are you sure that you have set the identifier for the cell on the storyboard and the identifier name is the same used on the code "ContactCell"?

how to deal with alamofire's async?

I am trying to save the data I got from the JSON fire however, because of Alamofire's async nature I dont get the data I need instantly but only when I tap on the tableviewcell again (and the data is wrong too)
I am wondering what I should do here so that when I tap the tableviewcell it will get the data I needed (instead of empty arrays)
Here's my code:
class CurrencyExchangeViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
let exchange = currencyExchangeModel()
var past30DaysDateValueToPass = [String]()
var past30DaysPriceValueToPass = [Double]()
var valueToPass = ""
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath) as? CurrencyExchangeTableViewCell
valueToPass = Array(self.exchange.currencyToGetExchangesDictionary.keys)[indexPath.row]
self.getPastData(currency: valueToPass)
performSegue(withIdentifier: "currencyHistorySegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "currencyHistorySegue") {
var viewController = segue.destination as? CurrencyHistoricalDataViewController
viewController?.historicalCurrency = valueToPass
viewController?.past30DaysPrice = self.exchange.currencyPast30DaysPriceArray
viewController?.past30DaysDate = self.exchange.currencyPast30DaysDatesArray
}
}
func getPastData(currency: String){
Alamofire.request("https://api.coindesk.com/v1/bpi/historical/close.json?currency=\(currency)").responseJSON{ (responseData) in
if responseData.result.value != nil {
let responseJSON = JSON(responseData.result.value)["bpi"].dictionaryObject
self.exchange.currencyPast30DaysDatesArray = Array(responseJSON!.keys)
self.exchange.currencyPast30DaysPriceArray = Array(responseJSON!.values) as! [Double]
}
}
}
}
If you want performSegue execute after getting data from Alamofire then remove
performSegue(withIdentifier: "currencyHistorySegue", sender: self)
this line to from didselect and place at getPastData.
func getPastData(currency: String){
Alamofire.request("https://api.coindesk.com/v1/bpi/historical/close.json?currency=\(currency)").responseJSON{ (responseData) in
if responseData.result.value != nil {
let responseJSON = JSON(responseData.result.value)["bpi"].dictionaryObject
self.exchange.currencyPast30DaysDatesArray = Array(responseJSON!.keys)
self.exchange.currencyPast30DaysPriceArray = Array(responseJSON!.values) as! [Double]
performSegue(withIdentifier: "currencyHistorySegue", sender: self)
}
}
This will help.

Segue to detailed view controller using a button in a cell

I have a collection view cell that passes data to a detailed view controller. When the cell is clicked, it segues into a view controller with more details. In the cells, I have a button, when the button is clicked, it also segues into a detailed view controller but a different view controller from when the cell is clicked.
This is what my didselect function looks like.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "details" {
self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
if let indexPaths = self.CollectionView!.indexPathsForSelectedItems{
let vc = segue.destination as! BookDetailsViewController
let cell = sender as! UICollectionViewCell
let indexPath = self.CollectionView!.indexPath(for: cell)
let post = self.posts[(indexPath?.row)!] as! [String: AnyObject]
let Booked = post["title"] as? String
let Authors = post["Author"] as? String
let ISBNS = post["ISBN"] as? String
let Prices = post["Price"] as? String
let imageNames = post["image"] as? String
let imagesTwo = post["imageTwo"] as? String
let imagesThree = post["imageThree"] as? String
let imagesFour = post["imageFour"] as? String
let imagesFive = post["imageFive"] as? String
vc.Booked = Booked
vc.Authors = Authors
vc.ISBNS = ISBNS
vc.Prices = Prices
vc.imageNames = imageNames
vc.imagesTwo = imagesTwo
vc.imagesThree = imagesThree
vc.imagesFour = imagesFour
vc.imagesFive = imagesFive
print(indexPath?.row)
} }
if segue.identifier == "UsersProfile" {
if let indexPaths = self.CollectionView!.indexPathsForSelectedItems{
let vc = segue.destination as! UsersProfileViewController
let cell = sender as! UICollectionViewCell
let indexPath = self.CollectionView!.indexPath(for: cell)
let post = self.posts[(indexPath?.row)!] as! [String: AnyObject]
let username = post["username"] as? String
let userpicuid = post["uid"] as? String
vc.username = username
vc.userpicuid = userpicuid
print(indexPath?.row)
}}}
For if the segue == User's Profile I get an error in the let cell = line. My button in the cell was created in the cellForItemAt collection view function
let editButton = UIButton(frame: CGRect(x: 106, y: 171, width: 36, height: 36))
editButton.addTarget(self, action: #selector(editButtonTapped), for: UIControlEvents.touchUpInside)
editButton.tag = indexPath.row
print(indexPath.row)
editButton.isUserInteractionEnabled = true
cell.addSubview(editButton)
When I click the cell, it works perfectly and segues me into a detailed view controller but when I click the button within the cell, I get an error.
Here is my editTappedButton function
#IBAction func editButtonTapped() -> Void {
print("Hello Edit Button")
performSegue(withIdentifier: "UsersProfile", sender: self)
}
It is obvious that you are getting that crash because with your button action you are calling performSegue(withIdentifier: "UsersProfile", sender: self) now with sender you are passing self means reference of current controller not the UICollectionViewCell what you need is get the indexPath of that cell and pass that and now in prepareForSegue cast the sender to IndexPath instead of UICollectionViewCell.
First replace your editButtonTapped with below one
#IBAction func editButtonTapped(_ sender: UIButton) -> Void {
print("Hello Edit Button")
let point = sender.superview?.convert(sender.center, to: self.tableView)
if let indexPath = self.tableView.indexPathForRow(at: point!) {
performSegue(withIdentifier: "UsersProfile", sender: indexPath)
}
}
Now in prepareForSegue for identifier UsersProfile cast the sender to IndexPath or simply replace your condition with my one.
if segue.identifier == "UsersProfile" {
if let indexPath = sender as? IndexPath{
let vc = segue.destination as! UsersProfileViewController
let post = self.posts[indexPath.row] as! [String: AnyObject]
let username = post["username"] as? String
let userpicuid = post["uid"] as? String
vc.username = username
vc.userpicuid = userpicuid
print(indexPath.row)
}
}

how to handle two collection view with push segues

I'm having two UICollectionView
#IBOutlet weak var collectionView1: UICollectionView!
#IBOutlet weak var collectionview2: UICollectionView!
i'm getting indexPath for each collection view separately with functions.
func getIndexPathForSelectedCell() -> NSIndexPath?
{
var indexPath:NSIndexPath?
if collectionview1.indexPathsForSelectedItems()!.count > 0 {
indexPath = collectionview1.indexPathsForSelectedItems()![0]
}
return indexPath
}
func getIndexPathForSelectedCell2() -> NSIndexPath?
{
var indexPath2:NSIndexPath?
if collectionView2.indexPathsForSelectedItems()!.count > 0 {
indexPath2 = collectionView2.indexPathsForSelectedItems()![0]
}
return indexPath2
}
I'm Performing segue for cell touch as follows.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if let indexPath = getIndexPathForSelectedCell()
{
let DealsdetailViewController = segue.destinationViewController as! DealsDetailViewController
DealsdetailViewController.Dealsdata = Dealsdata[indexPath.row]
}
else if let indexPath2 = getIndexPathForSelectedCell2()
{
let ContainerviewController = segue.destinationViewController as! ContainerViewController
ContainerviewController.BTdata = BTdata[indexPath2.row]
}
}
if i click on a cell in first collection view segue performs correctly, when i click on a cell in second collection view
i got error
in
let DealsdetailViewController = segue.destinationViewController as! DealsDetailViewController
which is first if statement condition value, i'm stuck here
please help me, how to handle performing both segue on cell click on each collection view.
Use the method from UICollectionView protocol
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = cellForItemAtIndexPath(indexPath)!
if collectionView == self.collectionView1 {
self.performSegueWithIdentifier("segue1", sender: cell)
} else if collectionView == self.collectionView2 {
self.performSegueWithIdentifier("segue2", sender: cell)
}
}
func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if segue.identifer == "segue1" {
let detailVC:DetailViewController = segue.destinationViewController as DetailViewController
// Your sender is cell. You have indexPath of them and can get his identity in dataSource.
//detailVC.name = ...
//detailVC.surname = ...
} else if segue.identifier == "segue2" {
//...
}
}

can't retrieve indexPath on prepareforsegue?

I'm having a problem with retrieving the indexPath on prepareForSegue with the "comments" block. The comments button is inside the cell, and I created a segue from the view controller to the desired vc but every time I click the button, I get an "unexpectedly found nil while unwrapping an optional value" on the part where I declare the indexPath.
override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool {
if (identifier == "webPage1") {
let indexPath: NSIndexPath = tableView.indexPathForSelectedRow()!
if (arrayByVotes[indexPath.row].objectForKey("videoURL") as NSString == "") {
performSegueWithIdentifier("comments", sender: nil)
return false
}
}
return true
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "webPage1") {
var urlToOpen:String = ""
let indexPath: NSIndexPath = tableView.indexPathForSelectedRow()!
if((arrayByVotes[indexPath.row].objectForKey("videoURL")) != nil) {
urlToOpen = arrayByVotes[indexPath.row].objectForKey("videoURL") as String
urlToOpen = urlToOpen.stringByReplacingOccurrencesOfString("http:", withString: "https:", options: .LiteralSearch, range: nil)
}
let sW: webpageViewController = segue.destinationViewController as webpageViewController
sW.urlPath = urlToOpen
} else if (segue.identifier == "comments") {
let indexPath: NSIndexPath = tableView.indexPathForSelectedRow()!
let sweet:PFObject = arrayByVotes[indexPath.row] as PFObject
var postId = sweet.objectId
let selectedFContent: String = arrayByVotes[indexPath.row].objectForKey("content") as String
let fpvc: FeedPageViewController = segue.destinationViewController as FeedPageViewController
fpvc.selectedFeedFeedContent = selectedFContent
fpvc.ourpostId = postId
}
}
I then tried wrapping it in an
if let indexPath = tableView.indexPathForSelectedRow()!
but that didn't work either as it wouldn't retrieve the correct indexPath every time.
Essentially the program is supposed to check to see if a cell contains a URL. If it does, it calls the vc with a webview, and if not, then it directly calls the comments page.
Try this
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "webPage1") {
let selectedRow = tableView.indexPathForSelectedRow()?.row
var urlToOpen = arrayByVotes(selectedRow!) as? String
if((urlToOpen != nil) {
urlToOpen = urlToOpen.stringByReplacingOccurrencesOfString("http:", withString: "https:", options: .LiteralSearch, range: nil)
}
let sW: webpageViewController = segue.destinationViewController as webpageViewController
sW.urlPath = urlToOpen
} else if (segue.identifier == "comments") {
let selectedRow = tableView.indexPathForSelectedRow()?.row
let sweet:PFObject = arrayByVotes(selectedRow!) as PFObject
var postId = sweet.objectId
let selectedFContent: String = arrayByVotes(selectedRow!).objectForKey("content") as String
let fpvc: FeedPageViewController = segue.destinationViewController as FeedPageViewController
fpvc.selectedFeedFeedContent = selectedFContent
fpvc.ourpostId = postId
}
}