When tapped on search bar, the app crashes - swift

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"?

Related

How to Increase count of Page in Url for loading more data and show indicator at bottom?

I Creating a demo of webservices, In this I want to increase page count and load more data from api, and add in table view after activity indicator refreshing. I find many tutorials but Not found useful... They are all Advance and I'm beginner so i didn't get properly. Can Any one please tell how to do this.
Here's My Demo details...
This Is Page Count of URL
"info": {
"count": 826,
"pages": 42,
"next": "https://rickandmortyapi.com/api/character/?page=3",
"prev": "https://rickandmortyapi.com/api/character/?page=1"
},
My json Model
import UIKit
import Foundation
// MARK: - JsonModel
struct JSONModel:Decodable {
let info: Info
let results: [Result]
}
// MARK: - Info
struct Info : Decodable {
let count, pages: Int
let next: String
let prev: NSNull
}
// MARK: - Result
struct Result : Decodable {
let id: Int
let name: String
let status: Status
let species: Species
let type: String
let gender: Gender
let origin, location: Location
let image: String
let episode: [String]
let url: String
let created: String
}
enum Gender {
case female
case male
case unknown
}
// MARK: - Location
struct Location {
let name: String
let url: String
}
enum Species {
case alien
case human
}
enum Status {
case alive
case dead
case unknown
}
This is my View controller Class
import UIKit
import Kingfisher
class ViewController: UIViewController,UISearchBarDelegate{
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var results = [Results]()
var filteredData = [Results]()
var batchSize = 42
var fromIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
tableView.delegate = self
tableView.dataSource = self
apiCalling()
filteredData = results
}
override func viewWillAppear(_ animated: Bool) {
filteredData = results
self.tableView.reloadData()
}
func apiCalling(){
guard let url = URL(string: "https://rickandmortyapi.com/api/character/") else { return }
URLSession.shared.dataTask(with: url) {[weak self]data, response, error in
if error != nil{
print("error While Fetching Data")
}
guard let data = data else {
return
}
do {
let resultData = try JSONDecoder().decode(JsonModel.self, from: data)
self?.results = resultData.results!
self?.filteredData = self!.results
DispatchQueue.main.async {
self?.tableView.reloadData()
}
} catch {
print(error.localizedDescription)
}
}.resume()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
let searchText = searchBar.text!
guard !searchText.isEmpty else {
filteredData = results
tableView.reloadData()
return
}
filteredData = results.filter({ $0.name!.lowercased().contains(searchText.lowercased() ) })
tableView.reloadData()
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
self.searchBar.showsCancelButton = true
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.showsCancelButton = false
searchBar.text = ""
searchBar.resignFirstResponder()
filteredData.removeAll()
self.tableView.reloadData()
}
}
This My Tableview Extension
extension ViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UserTableViewCell
let row = filteredData[indexPath.row]
let imageUrl = URL(string: row.image!)
cell.userImage.kf.setImage(with: imageUrl)
cell.lblGender.text = "Gender:- \(row.gender ?? "no value")"
cell.lblID.text = "ID:- \(row.id ?? 0)"
cell.lblName.text = "Name: \(row.name!)"
cell.lblSpecies.text = "Species:- \(row.species ?? "No Speies")"
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 250
}
}
u need save page info.
self?.info = resultData.info!
call "loadpage" when u loading more data
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
tableView.delegate = self
tableView.dataSource = self
filteredData = []
result = []
apiCalling(apiurl:"https://rickandmortyapi.com/api/character/")
}
func apiCalling(apiurl:String){
guard let url = URL(string: apiurl) else { return }
URLSession.shared.dataTask(with: url) {[weak self]data, response, error in
if error != nil{
print("error While Fetching Data")
}
guard let data = data else {
return
}
do {
let resultData = try JSONDecoder().decode(JsonModel.self, from: data)
self?.results.append(resultData.results!)
self?.info = resultData.info!
filterWord()
} catch {
print(error.localizedDescription)
}
}.resume()
}
func filterWord(){
let searchText = searchBar.text!
guard !searchText.isEmpty else {
filteredData = results
tableView.reloadData()
return
}
filteredData = results.filter({ $0.name!.lowercased().contains(searchText.lowercased() ) })
tableView.reloadData()
}
func loadPage(){
guard let page = self?.info.next,!page.isEmpty else{
return
}
apiCalling(apiurl:page)
}
under indicator simple example like this
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
guard let page = self?.info.next,!page.isEmpty else{
return nil
}
//press to call loadPage
let loading = UIButton.init()
let view = UIView.init()
view.addSubview(loading)
return view
}
I'm Giving My own Questions answer Here...
I Have Create 3 more variables
var curentIndex : Int = 0
// I'm Putting Default Limit Here...
var numberArray = Array(1...42)
var fetchingMore = false
Api Call
func apiCalling(){
guard !fetchingMore else {
print("Didn't call Get Data")
return
}
fetchingMore = true
guard let url = URL( string: "\(baseUrl)?page=\(numberArray[curentIndex])") ?? URL(string: "" ) else {
fetchingMore = false
return
}
curentIndex += 1
URLSession.shared.dataTask(with: url) {[weak self]data, response, error in
if error != nil{
print("error While Fetching Data")
}
guard let data = data else {
return
}
do {
let resultData = try JSONDecoder().decode(JsonModel.self, from: data)
self?.results += resultData.results!
self?.filteredData = self!.results
DispatchQueue.main.async {
self?.tableView.reloadData()
}
} catch {
print(error.localizedDescription)
}
self?.fetchingMore = false
}.resume()
}
**Here's My CellForRowMethod **
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UserTableViewCell
let row = filteredData[indexPath.row]
if indexPath.row == filteredData.count - 1 && curentIndex <= row.id ?? 0 {
apiCalling()
}
let imageUrl = URL(string: row.image!)
cell.userImage.kf.setImage(with: imageUrl)
cell.lblGender.text = "Gender:- \(row.gender ?? "no value")"
cell.lblID.text = "ID:- \(row.id ?? 0)"
cell.lblName.text = "Name: \(row.name!)"
cell.lblSpecies.text = "Species:- \(row.species ?? "No Speies")"
return cell
}

CoreData gets added to Table View AGAIN Every Time I Show View Controller (duplicates data)

I followed this tutorial
https://www.youtube.com/watch?v=35mKM4IkHS8&lc=UgztyK4XjUuAOrKk0XJ4AaABAg.9LtwRc_M0Gv9Nt8GIlAzDo
Basically I made a NotePad App that has a core data save function.
I made this app on another view controller
So There is MainViewController > NoteViewViewController
The first time I click the notepad section it loads core data perfectly well, but if I close out the NoteView and reopen it -- it duplicates all the saved Notes in Core Data
Here is the. Note ViewController
import UIKit
import CoreData
var noteList = [Note]()
class NoteTableView: UITableViewController
{
func nonDeletedNotes() -> [Note]
{
var noDeleteNoteList = [Note]()
for note in noteList
{
if(note.deletedDate == nil)
{
noDeleteNoteList.append(note)
}
}
return noDeleteNoteList
}
var firstLoad = true
override func viewDidLoad() {
if(firstLoad == true)
{
firstLoad = false
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Note")
do {
let results:NSArray = try context.fetch(request) as NSArray
for result in results
{
let note = result as! Note
noteList.append(note)
}
}
catch
{
print("Fetch Failed")
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell
{
let noteCell = tableView.dequeueReusableCell(withIdentifier: "noteCellID", for: indexPath) as! NoteCell
let thisNote: Note!
thisNote = nonDeletedNotes()[indexPath.row]
noteCell.titleLabel.text = thisNote.title
noteCell.descLabel.text = thisNote.desc1
return noteCell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return nonDeletedNotes().count
}
override func viewDidAppear(_ animated: Bool) {
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
self.performSegue(withIdentifier: "editNote", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if(segue.identifier == "editNote")
{
let indexPath = tableView.indexPathForSelectedRow!
let noteDetail = segue.destination as? FocusWheelViewController
let selectedNote : Note!
selectedNote = nonDeletedNotes()[indexPath.row]
noteDetail!.selectedNote = selectedNote
tableView.deselectRow(at: indexPath, animated: true)
}
}
}
I'm sure there is a common solution but I'm not sure what it is and wasn't able to follow the posts asking similar questions as my code was different and I truthfully don't understand the mechanics well enough to apply other answers to this
I found the easiest solution was to just add these two lines so the table view refreshed every-time, then loaded the data
noteList.removeAll()
tableView.reloadData()
So the code looks something like this:
var firstLoad = true
override func viewDidLoad() {
if(firstLoad == true)
{
noteList.removeAll() //NEWCODE
tableView.reloadData() //NEWCODE
firstLoad = false
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Note")
do {
let results:NSArray = try context.fetch(request) as NSArray
for result in results
{
let note = result as! Note
noteList.append(note)
}
}
catch
{
print("Fetch Failed")
}
}
}
The problem is var firstLoad = true. Because every time the controller start, firtLoad always true and the app will get data from Coredata and append to noteList.
The solution is UserDefaults. The first time when you run app, firstLoad always true. So you need to save the value bool of firstLoad to UserDefaults
// Set
UserDefaults.standard.setValue(true, forKey: "firstLoad")
// Get
UserDefaults.standard.bool(forKey: "firstLoad")
import UIKit
import CoreData
class NoteTableView: UITableViewController{
var noteList = [Note]()
func nonDeletedNotes() -> [Note]{
var noDeleteNoteList = [Note]()
for note in noteList {
if(note.deletedDate == nil) {
noDeleteNoteList.append(note)
}
}
return noDeleteNoteList
}
override func viewDidLoad() {
if noteList.count == 0 {
if(UserDefaults.standard.bool(forKey: "firstLoad") == true){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Note")
do {
let results:NSArray = try context.fetch(request) as NSArray
for result in results
{
let note = result as! Note
noteList.append(note)
UserDefaults.standard.setValue(false, forKey: "firstLoad")
}
}
catch {
print("Fetch Failed")
}
}
} {
else {
UserDefaults.standard.setValue(false, forKey: "firstLoad")
}
}
}
}
And maybe you need to check duplicate value when get data from CoreData.

How to update table view

I used search bar. It isn't updating the table view.
struct ApiResults:Decodable {
let resultCount: Int
let results: [Music]
}
struct Music:Decodable {
let trackName: String?
let artistName: String?
let artworkUrl60: String?
}
class ItunesDataViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var musicArray:[Music] = []
var mArray:[Music] = []
var filteredData:[Music] = []
var isSearching = false
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self
self.tableView.delegate = self
self.searchBar.delegate = self
searchBar.placeholder = "search"
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
{
print("\n\nsearchText : \(searchText)\n\n")
Search(searchTerm: "\(searchText)")
if searchBar.text == nil || searchBar.text == ""
{
isSearching = false
view.endEditing(true)
self.tableView.reloadData()
}
else
{
isSearching = true
filteredData = mArray.filter{$0.artistName == searchText}
self.tableView.reloadData()
}
}
func Search(searchTerm: String)
{
guard let url = URL(string: "https://itunes.apple.com/search?term=\(searchTerm)&attribute=actorTerm&attribute=languageTerm&attribute=allArtistTerm&attribute=tvEpisodeTerm&attribute=shortFilmTerm&attribute=directorTerm&attribute=releaseYearTerm&attribute=titleTerm&attribute=featureFilmTerm&attribute=ratingIndex&attribute=keywordsTerm&attribute=descriptionTerm&attribute=authorTerm&attribute=genreIndex&attribute=mixTerm&attribute=allTrackTerm&attribute=artistTerm&attribute=composerTerm&attribute=tvSeasonTerm&attribute=producerTerm&attribute=ratingTerm&attribute=songTerm&attribute=movieArtistTerm&attribute=showTerm&attribute=movieTerm&attribute=albumTerm") else {return}
URLSession.shared.dataTask(with: url){(data, response, error) in
guard let data = data else {return}
do
{
let apiressults = try JSONDecoder().decode(ApiResults.self, from: data)
for item in apiressults.results
{
if let track_Name = item.trackName, let artist_Name = item.artistName, let artwork_Url60 = item.artworkUrl60
{
let musics = Music(trackName: track_Name, artistName: artist_Name, artworkUrl60: artwork_Url60)
self.musicArray.append(musics)
print(musics.artistName!,"-", musics.trackName!)
}
}
DispatchQueue.main.async
{
self.tableView.reloadData()
}
}
catch let jsonError
{
print("Error:", jsonError)
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearching
{
return filteredData.count
}
else
{
return mArray.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "musicCell", for: indexPath) as! ItunesDataTableViewCell
if isSearching
{
cell.lblDesc?.text = filteredData[indexPath.row].artistName
cell.lblSongDesc?.text = filteredData[indexPath.row].trackName
let imgString = filteredData[indexPath.row].artworkUrl60!
let imgUrl:URL = URL(string: imgString)!
DispatchQueue.global(qos: .userInitiated).async {
let imageData:NSData = NSData(contentsOf: imgUrl)!
DispatchQueue.main.async {
let image = UIImage(data: imageData as Data)
cell.imgArt?.image = image
}
}
}
else
{
cell.lblDesc?.text = mArray[indexPath.row].artistName
cell.lblSongDesc?.text = mArray[indexPath.row].trackName
let imgString = mArray[indexPath.row].artworkUrl60!
let imgUrl:URL = URL(string: imgString)!
DispatchQueue.global(qos: .userInitiated).async {
let imageData:NSData = NSData(contentsOf: imgUrl)!
DispatchQueue.main.async {
let image = UIImage(data: imageData as Data)
cell.imgArt?.image = image
}
}
}
return cell
}
}
Please clean up your code 😉: You have two different arrays mArray and musicArray.
You are populating musicArray in Search but mArray is used as data source.
Why do you create new Music items from Music items? You can reduce the code to
let apiressults = try JSONDecoder().decode(ApiResults.self, from: data)
self.mArray = apiressults.results
DispatchQueue.main.async {
self.tableView.reloadData()
}
Please change your code in the cellForRowAt delegate method to:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "musicCell", for: indexPath) as! ItunesDataTableViewCell
let tempArray: [Music] = isSearching ? filteredData : musicArray
cell.lblDesc?.text = tempArray[indexPath.row].artistName
cell.lblSongDesc?.text = tempArray[indexPath.row].trackName
guard let imgString = tempArray[indexPath.row].artworkUrl60,
let imgUrl = URL(string: imgString) else {
// Handle properly the fact that there's no image to display
return cell
}
// Review this code as I'm not sure about this double dispatch
// However, please, no force unwrap optionals (!)
DispatchQueue.global(qos: .userInitiated).async {
do {
let imageData = try Data(contentsOf: imgUrl)
DispatchQueue.main.async {
let image = UIImage(data: imageData)
cell.imgArt?.image = image
}
} catch let error {
print("Error with the image URL: ", error)
}
}
return cell
}
See how you don't repeat your code that way?
Furthermore you were not using the right music array, or we don't have all the information to assess what is wrong with this mix of mArray and musicArray.

This class is not key value coding-compliant for the key ProductsCollection.'

I am getting this exception:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key ProductsCollection.'
I read previous posts about this, and I checked twice - and my UIVewCollection (ProductsCollection) is linked to the outlet.
My Class :
class SellProductsView: ProductsCollectionViewController
{
override func viewDidLoad()
{
// Do any additional setup after loading the view.
self.ProductsCollection.delegate = self
self.ProductsCollection.dataSource = self;
LoadProducts(productsToSellOrBuy: "ToSell")
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "view_product_information"
{
let prodPageView = segue.destination as! SellProductPageView
PrepareProductForSegue(prodPageView: prodPageView)
}
}
}
My base class :
class ProductsCollectionViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource
{
#IBOutlet weak var ProductsCollection: UICollectionView!
var ref: DatabaseReference?
var databaseHandle: DatabaseHandle?
#IBAction func unwindFromProductPageView(segue: UIStoryboardSegue)
{
}
var products = [Product]()
override func viewDidLoad()
{
// Do any additional setup after loading the view.
self.ProductsCollection.delegate = self
self.ProductsCollection.dataSource = self;
super.viewDidLoad()
}
internal func LoadProducts(productsToSellOrBuy: String)
{
if productsToSellOrBuy != "ToBuy" && productsToSellOrBuy != "ToSell"
{
// Throw error
}
// Set firebase reference
ref = Database.database().reference()
let loggedOnUserID = Auth.auth().currentUser?.uid
// Retrieve the products and listen for changes
databaseHandle = ref?.child("Products").observe(.childAdded, with:
{ (snapshot) in
// Code to execute when new product is added
let prodValue = snapshot.value as? NSDictionary
let prodToLoad = prodValue?[productsToSellOrBuy] as? Bool // Checks if this is to sell or buy
if loggedOnUserID != prodValue?["Owner"] as? String, prodToLoad == true
{
let prodName = prodValue?["Name"] as? String ?? ""
let prodPrice = prodValue?["Price"] as? Double ?? -1
let prodDesc = prodValue?["Description"] as? String ?? ""
let prodURLS = prodValue?["MainImage"] as? String
let prodAmount = prodValue?["Amount"] as? Int ?? 0
let prodID = snapshot.key
let prodToAddToView = Product(name: prodName, price: prodPrice, currency: "NIS", description: prodDesc, location: "IL",
toSell: false, toBuy: false, owner: "No one", uniqueID: prodID, amount: prodAmount, mainImageURL: prodURLS)
self.products.append(prodToAddToView)
DispatchQueue.main.async {
self.ProductsCollection.reloadData()
}
}
})
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return products.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "product_collection_cell", for: indexPath) as! ProductsCollectionViewCell
//cell.ProductName.text
let requestListenRef = ref?.child("Products").child(products[indexPath.row].UniqueID()).child("MainImage")
requestListenRef?.observe(DataEventType.value, with:
{
(snapshot) in
let mainImage = snapshot.value as? String
if mainImage == nil
{
// No main image exists
cell.ProductImageView.image = UIImage(named: "DefaultProductImage")
}
else
{
// Main Image exists
let url = URL(string: mainImage!)
if let data = try? Data(contentsOf: url!)
{
cell.ProductImageView.image = UIImage(data: data)
}
}
})
// Set fields
let prodInCell = products[indexPath.row]
cell.ProductName.text = prodInCell.Name()
cell.ProductPrice.text = String(prodInCell.Price())
cell.productUniqueID = prodInCell.UniqueID()
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
// Display selected Item
prodToLoad = products[indexPath.row]
performSegue(withIdentifier: "view_product_information", sender:self )
}
var prodToLoad: Product?
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "view_product_information"
{
let prodPageView = segue.destination as! ProductPageView
PrepareProductForSegue(prodPageView: prodPageView)
}
}
internal func PrepareProductForSegue(prodPageView: ProductPageView)
{
prodPageView.productToDisplay = prodToLoad
prodPageView.pictures = [UIImageView] ()
if (prodToLoad?.Images().count != 0)
{
let mainImage = prodToLoad?.GetMainImageURLString()
// Main Image exists
let url = URL(string: mainImage!)
if let data = try? Data(contentsOf: url!)
{
let imageView = UIImageView()
imageView.image = UIImage(data: data)
prodPageView.pictures?.append(imageView)
}
}
else
{
let imageView = UIImageView()
imageView.image = #imageLiteral(resourceName: "DefaultProductImage")
prodPageView.pictures?.append(imageView)
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
Also :
When I delete the connection, app doesn't crash. Once I reconnect, it crashes again. The ProductsCollection is now shared between two INHERITING views.

Wrong indexPathForSelectedRow when using prepareForSegue

I'm trying to fill a tableView with some products from my MySQL database (using a PHP POST file) and at the moment everything is fine, but when I select a "Cell", the prepareForSegue is triggered, but the indexPathForSelectedRow is wrong, so it's showing a different product.
Here is my full code, I hope you can tell me something because I don't know why is this happening, I have tried a lot of things and I'm out of options...!
TableViewController.swift
import UIKit
class ListadoBuscarResultadosTableViewController: UITableViewController {
var option: String = ""
var productos = [Producto]()
var refreshControl2:UIRefreshControl!
var imageCache = [String:UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
requestPost()
title = self.option
tableView.allowsMultipleSelection = true
tableView.scrollsToTop = true
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.hidesBarsOnSwipe = false
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
func refresh(sender:AnyObject) {
requestPost()
self.refreshControl2.endRefreshing()
}
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 the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return productos.count
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
// Define the initial state (Before the animation)
cell.alpha = 0.25
// Define the final state (After the animation)
UIView.animateWithDuration(1.0, animations: { cell.alpha = 1 })
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// try to reuse cell
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! BuscarCellTableViewCell
cell.selectionStyle = .None
//cell.nombre.text = productos[indexPath.row].nombre
//cell.marca.text = productos[indexPath.row].marca
//println(cell.nombre.text)
// get the deal image
let currentImage = productos[indexPath.row].imagen
let unwrappedImage = currentImage
var image = self.imageCache[unwrappedImage]
let imageUrl = NSURL(string: productos[indexPath.row].imagen)
// reset reused cell image to placeholder
cell.imagen.image = UIImage(named: "")
// async image
if image == nil {
let request: NSURLRequest = NSURLRequest(URL: imageUrl!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in
if error == nil {
image = UIImage(data: data)
self.imageCache[unwrappedImage] = image
dispatch_async(dispatch_get_main_queue(), {
cell.imagen.image = image
cell.nombre.text = self.productos[indexPath.row].nombre
cell.marca.text = self.productos[indexPath.row].marca
})
}
else {
cell.nombre.text = self.productos[indexPath.row].nombre
cell.marca.text = self.productos[indexPath.row].marca
}
})
}
else {
cell.imagen.image = image
cell.nombre.text = self.productos[indexPath.row].nombre
cell.marca.text = self.productos[indexPath.row].marca
}
return cell
}
func requestPost () {
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.web.es/productos_by_category.php")!)
request.HTTPMethod = "POST"
let postString = "category="+option
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
//println("error=\(error)")
return
}
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)!
// JSON RESULTADO ENTERO
//println("responseString = \(responseString)")
self.productos = self.parseJsonData(data)
// Reload table view
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
task.resume()
}
func parseJsonData(data: NSData) -> [Producto] {
var productos = [Producto]()
var error:NSError?
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as? NSDictionary
// Return nil if there is any error
if error != nil {
println(error?.localizedDescription)
}
// Parse JSON data
let jsonProductos = jsonResult?["lista_productos"] as! [AnyObject]
for jsonProducto in jsonProductos {
let producto = Producto()
producto.nombre = jsonProducto["nombre"] as! String
producto.imagen = jsonProducto["imagen"] as! String
producto.marca = jsonProducto["marca"] as! String
producto.distribuidor = jsonProducto["distribuidor"] as! String
producto.linea = jsonProducto["linea"] as! String
producto.precio = jsonProducto["precio"] as! String
productos.append(producto)
}
return productos
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "verProducto" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let destinationController = segue.destinationViewController as! MarcaProductoViewController
//println(productos[indexPath.row].nombre)
println(indexPath)
destinationController.nombre = productos[indexPath.row].nombre
}
}
}
Thanks in advance,
Regards.
try this way...
if segue.identifier == "verProducto"{
if let indexPath = tableView.indexPathForCell(sender as! BuscarCellTableViewCell){
var detailsVC = segue.destinationViewController as! MarcaProductoViewController
}
}