I'm trying to implement an UISearchBarController in UICollectionViewController, here are the codes:
class FirstViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchResultsUpdating {
var titles: [String] = []
var card: [RecommendArticles]?
var searchArr = [String](){
didSet {
self.collectionView?.reloadData()
}
}
func updateSearchResults(for searchController: UISearchController) {
guard let searchText = searchController.searchBar.text else {
return
}
searchArr = titles.filter { (title) -> Bool in
return title.contains(searchText)
}
}
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
navigationItem.hidesSearchBarWhenScrolling = true
self.navigationItem.searchController = searchController
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if (searchController.isActive) {
return searchArr.count
} else {
return self.counters
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: verticalCellId, for: indexPath) as! VerticalCellId
if (searchController.isActive) {
cell.titleLabel.text = searchArr[indexPath.row]
return cell
} else {
if let link = self.card?[indexPath.item]._imageURL {
let url = URL(string: link)
cell.photoImageView.kf.setImage(with: url)
}
if let title = self.card?[indexPath.item]._title {
cell.titleLabel.text = title
titles.append(title)
print("\(titles)")
}
if let source = self.card?[indexPath.item]._source {
cell.sourceLabel.text = source
}
return cell
}
}
Here are the errors locate:
titles.append(title)
print("\(titles)")
When I search a keyword, the filtered results are incorrect, because the collection view cell will change dynamically (I'm not sure if my expression is accurate).
But, if I set up the variable titles like this, it works perfectly:
var titles = ["Tom","Jack","Lily"]
But I have to retrieve the values of titles from internet. Any suggestions would be appreciated.
Here are the update question for creating an array after self.card:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.view.addSubview(activityView)
activityView.hidesWhenStopped = true
activityView.center = self.view.center
activityView.startAnimating()
let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
dynamoDbObjectMapper.load(TheUserIds2018.self, hashKey: "TheUserIds2018", rangeKey: nil, completionHandler: { (objectModel: AWSDynamoDBObjectModel?, error: Error?) -> Void in
if let error = error {
print("Amazon DynamoDB Read Error: \(error)")
return
}
DispatchQueue.main.async {
if let count = objectModel?.dictionaryValue["_articleCounters"] {
self.counters = count as! Int
self.collectionView?.reloadData()
self.updateItems()
self.activityView.stopAnimating()
}
}
})
}
func updateItems() {
let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
var tasksList = Array<AWSTask<AnyObject>>()
for i in 1...self.counters {
tasksList.append(dynamoDbObjectMapper.load(RecommendArticles.self, hashKey: "userId" + String(self.counters + 1 - i), rangeKey: nil))
}
AWSTask<AnyObject>.init(forCompletionOfAllTasksWithResults: tasksList).continueWith { (task) -> Any? in
if let cards = task.result as? [RecommendArticles] {
self.card = cards
DispatchQueue.main.async {
if let totalCounts = self.card?.count {
for item in 0...totalCounts - 1 {
if let title = self.card?[item]._title {
self.newTitle = title
}
self.titles.append(self.newTitle)
print("titles: \(self.titles)")
}
}
}
} else if let error = task.error {
print(error.localizedDescription)
}
return nil
}
}
This is not an issue with UISearchController . it is with your data. Collection view data source method -
cellForItemAtIndexPath works only when cell is presented in the view. So you can't keep the code for creating array of titles in the cellForItemAtIndexPath
Instead of separately keeping title strings in array for search you can directly filter self.card array with search text in title with below code
self.searchArr = self.card?.filter(NSPredicate(format:"_title CONTAINS[cd] %#",searchText)).sorted(byKeyPath: "_title", ascending: true)
or
you can create array of titles before loading collection view
Some where after you create self.card
Related
Hello Respected Comrades,
I made a gallery app. initially I get a data of media from phone.
but when i add some new photos to device. i cant see them until :
re install app
fetching in view will appear (this is just a temperory fix)
I want to update my data dynamically with respect to phone data
code:
import UIKit
import Foundation
import Photos
import CoreData
enum MediaType{
case video, photo
}
View model
class GalleryPageViewModel : BaseViewModel{
var mediaArray = Spectator<MediaArray?>(value:
[])
var title = "Gallery"
var appDelegate:AppDelegate?
var context : NSManagedObjectContext?
let responder : AppResponder
var tempMedia = [MediaModel]()
init(with responder:AppResponder){
self.responder = responder
super.init()
}
}
extension GalleryPageViewModel{
func populatePhotos(){
mediaArray.value = []
tempMedia = []
PHPhotoLibrary.requestAuthorization{ [weak self] status in
if status == .authorized{
let assets = PHAsset.fetchAssets(with: nil)
assets.enumerateObjects{ (object,_,_) in
var image = UIImage()
image = self!.conversionPhotoToImage(asset: object)
var mediaType : MediaType = .photo
if object.mediaType == .video{
mediaType = .video
}
let media = MediaModel(img: image, asset: object, mediaType: mediaType)
self?.tempMedia.append(media)
}
}
else{
print("auth failed")
}
self?.setUpData()
}
}
func conversionPhotoToImage(asset : PHAsset) -> UIImage{
let manager = PHImageManager.default()
var media = UIImage()
let requestOptions=PHImageRequestOptions()
requestOptions.isSynchronous=true
requestOptions.deliveryMode = .highQualityFormat
manager.requestImage(for: asset, targetSize: CGSize(width: 1000, height: 1000), contentMode:.aspectFill, options: requestOptions) { image,_ in
media = image!
}
return media
}
func setUpData(){
self.mediaArray.value = tempMedia
}
}
extension GalleryPageViewModel{
func numberOfItemsInSection() -> Int{
return mediaArray.value?.count ?? 0
}
func didSelectAtRow(index:IndexPath){
self.responder.pushToDetailVc(data: mediaArray.value![index.row])
}
}
extension GalleryPageViewModel{
func setUpCoreData(){
appDelegate = UIApplication.shared.delegate as! AppDelegate
context = appDelegate?.persistentContainer.viewContext
}
func storeInCoreData(){
let entity = NSEntityDescription.entity(forEntityName: "MediaData", in: context!)
let newUser = NSManagedObject(entity: entity!, insertInto: context)
var i = 1
for data in mediaArray.value!{
newUser.setValue(data.img, forKey: "image")
newUser.setValue(data.duration, forKey: "duration")
newUser.setValue("data", forKey: "mediaType")
newUser.setValue(data.location, forKey: "location")
newUser.setValue(data.creationDate, forKey: "creationDate")
var id = String()
if data.mediaType == .photo{
id = "imgEn\(i)"
}
else{
id = "vidEn\(i)"
}
i+=1
newUser.setValue(id, forKey: "id")
}
}
}
View controller
import UIKit
import Photos
class GalleryPageViewController: BaseViewController {
var galleryCollectionView : UICollectionView?
var galleryCollectionViewFlowLayout : UICollectionViewFlowLayout? = nil
var viewModel: GalleryPageViewModel? {
didSet {
self.bindViewModel()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = viewModel?.title
view.backgroundColor = .systemBackground
setUpFlowLayout()
galleryCollectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: galleryCollectionViewFlowLayout!)
galleryCollectionView?.register(MediaCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
setUpCollectionView()
viewConstraintsForGalleryPage()
viewModel?.populatePhotos()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
}
// MARK: - UI WORKS
extension GalleryPageViewController{
func setUpFlowLayout(){
galleryCollectionViewFlowLayout = UICollectionViewFlowLayout()
galleryCollectionViewFlowLayout?.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
galleryCollectionViewFlowLayout?.itemSize = CGSize(width: (view.frame.width/2) - 20, height: (self.view.frame.width/2)-10)
}
func setUpCollectionView(){
galleryCollectionView?.dataSource = self
galleryCollectionView?.delegate = self
}
func viewConstraintsForGalleryPage(){
// adding sub view to maintain heirarchy.
view.addSubview(galleryCollectionView!)
// constraints for views in this page
// collection view
galleryCollectionView?.translatesAutoresizingMaskIntoConstraints = false
// galleryCollectionView?.backgroundColor = .blue
galleryCollectionView?.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,constant: 10).isActive = true
galleryCollectionView?.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
galleryCollectionView?.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,constant: 10).isActive = true
galleryCollectionView?.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
}
}
//MARK: - COLLECTION VIEW DELEGATE AND DATA SOURCE
extension GalleryPageViewController : UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// vc.data = viewModel?.mediaArray.value?[indexPath.row]
viewModel?.didSelectAtRow(index: indexPath)
}
}
extension GalleryPageViewController : UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return viewModel?.numberOfItemsInSection() ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MediaCollectionViewCell
let cellData = viewModel?.mediaArray.value?[indexPath.row]
cell.imageView.image = cellData?.img
if cellData?.mediaType == .video{
cell.playBtn.isHidden = false
cell.durationLabel.isHidden = false
let manager = PHImageManager.default()
manager.requestAVAsset(forVideo: (cellData?.asset)!, options: nil){asset,_,_ in
let duration = asset?.duration
let durationTime = CMTimeGetSeconds(duration!)
let finalTime = durationTime/60
DispatchQueue.main.async {
cell.durationLabel.text = "\(Int(finalTime)) : \(Int(durationTime) % 60)"
}
}
}
else{
cell.playBtn.isHidden = true
cell.durationLabel.isHidden = true
}
return cell
}
}
//MARK: - BINDING VARIABLES
extension GalleryPageViewController{
private func bindViewModel(){
viewModel?.mediaArray.bind { [weak self] _ in
print("i am gonna reload")
print(self?.viewModel?.mediaArray.value?.isEmpty)
DispatchQueue.main.async {
self?.galleryCollectionView?.reloadData()
}
}
}
}
I parsed a data from iTunes API with model:
struct AlbumData: Codable {
let results: [Result]
}
struct Result: Codable {
let artistName, collectionName: String
let trackCount: Int
let releaseDate: String
let artworkUrl100: String
}
struct AlbumModel {
let albumsResult: [Result]
}
and this is NetworkService code:
import Foundation
protocol NetworkServiceDelegate {
func updateInfo (_ manager: NetworkService, album: AlbumModel)
func errorInfo (error: Error)
}
struct NetworkService {
var delegate: NetworkServiceDelegate?
func fetchAlbums () {
let urlString = "https://itunes.apple.com/search?term=eminem&limit=8&entity=album"
performRequest(with: urlString)
}
func performRequest(with urlString: String) {
if let url = URL(string: urlString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
delegate?.errorInfo(error: error!)
print("Debuggg error \(LocalizedError.self)")
}
if let safeData = data {
if let albums = self.parseJSON(safeData) {
delegate?.updateInfo(self, album: albums)
print("succses - \(albums)")
}
}
}
task.resume()
}
}
func parseJSON(_ data: Data) -> AlbumModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(AlbumData.self, from: data)
let result = decodedData.results
let album = AlbumModel(albumsResult: result)
return album
} catch {
print(error)
return nil
}
}
}
after that I appended this data into array:
var albums: [AlbumModel] = []
now I have array like this:
[Itunes_Albums.AlbumModel(albumsResult: [Itunes_Albums_.Result(artistName: "Eminem", collectionName: "The Eminem Show", trackCount: 20, releaseDate: "2002-01-01T08:00:00Z", artworkUrl100: "https://is5-ssl.mzstatic.com/image/thumb/Music115/v4/61/d5/0a/61d50a3d-4a27-187a-d16f-6b8ce4b62560/source/100x100bb.jpg"), Itunes_Albums_.Result(artistName: "Eminem", collectionName: "Recovery (Deluxe Edition)", trackCount: 19, releaseDate: "2010-06-21T07:00:00Z", artworkUrl100: "https://is5-ssl.mzstatic.com/image/thumb/Music125/v4/f1/40/ce/f140ce18-f176-7cf9-c220-3958d7747ae6/source/100x100bb.jpg")
After that I display this data into collection view
my SearchViewController
after that I want to write some characters to searchBar (name of album) and display that albums, but it doesn't works.
I can show all code:
import UIKit
class SearchViewController: UIViewController {
#IBOutlet weak var albumCollectionView: UICollectionView!
var albums: [AlbumModel] = []
var albumsForSearch: [AlbumModel] = []
var networkService = NetworkService()
override func viewDidLoad() {
super.viewDidLoad()
title = "Search"
registerCells()
networkService.delegate = self
networkService.fetchAlbums()
}
private func registerCells() {
albumCollectionView.register(UINib(nibName: AlbumCollectionViewCell.identifier, bundle: nil), forCellWithReuseIdentifier: AlbumCollectionViewCell.identifier)
}
}
//MARK: - AlbumManagerDelegate
extension SearchViewController: NetworkServiceDelegate {
func updateInfo(_ manager: NetworkService, album: AlbumModel) {
DispatchQueue.main.async {
self.albums.append(album)
self.albumsForSearch = self.albums
print("CHEEEEEK - \(self.albums)")
self.albumCollectionView.reloadData()
}
}
func errorInfo(error: Error) {
print(error)
}
}
//MARK: - UICollectionViewDelegate, UICollectionViewDataSource
extension SearchViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return albums.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return albums[section].albumsResult.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: AlbumCollectionViewCell.identifier, for: indexPath)
as! AlbumCollectionViewCell
let jData = albums[indexPath.section].albumsResult[indexPath.item]
cell.albumNameLabel.text = jData.collectionName
cell.artistNameLabel.text = jData.artistName
cell.numberOfTracksLabel.text = "\(jData.trackCount) song(s)"
cell.albumImage.load(urlString: "\(jData.artworkUrl100)")
cell.dateOfRelease.text = jData.releaseDate.substring(toIndex: 10)
return cell
}
}
//MARK: - UISearchBarDelegate
extension SearchViewController: UISearchBarDelegate, UISearchResultsUpdating {
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let searchView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "CollectionReusableView", for: indexPath)
return searchView
}
func updateSearchResults(for searchController: UISearchController) {
if let searchText = searchController.searchBar.text?.lowercased(), !searchText.isEmpty {
albumsForSearch = albums.filter { album -> Bool in
return album.albumsResult.contains { item -> Bool in item.collectionName.lowercased().contains(searchText)
}
}
albumCollectionView.reloadData()
} else {
albumsForSearch = albums
}
}
}
When I write some characters into searchBar nothing changes
Not sure what exactly it is you're struggling with so ill just point out a few things and hope this will solve your issue.
First, I don't know what your AlbumModel looks like. Perhaps you can show this code. but it looks like each instance contains more than one albumsResult. Are you expecting exactly one object in albumsResult in this array?
If not, I find this line really strange item.albumsResult[0]. That 0 there is a potential for crashes. So my answer doesn't assume there will always be one (And only one) item in that array.
Second, I also find this strange:
} else {
searching = false
albumsForSearch.removeAll()
albumsForSearch = albums
}
You're resetting your array every time you don't find a result. Not sure I can think of a valid reason why you would ever want to do that.
So perhaps this code is what you need:
if let searchText = let searchText = searchController.searchBar.text?.lowercased(), !searchText.isEmpty {
// Search text means filter results
albumsForSearch = albums.filter { album -> Bool in
return album.albumResult.contains { item -> Bool in
item.collectionName.lowercased().contains(searchText)
}
}
} else {
// No search text means display everything
albumsForSearch = albums
}
Then you just base your tableview sections and rows on albumsForSearch
My tableview gets slow when scrolling, I have a custom cell and a tableview:
I have this controller, where the api call is made and the array of trips is filled, then in cellForRowAt im creating the cell
class HistorialViewController: UIViewController , UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var historialTableView: UITableView!
var trips = [RootClass]()
override func viewDidLoad() {
super.viewDidLoad()
self.historialTableView.delegate = self
self.historialTableView.dataSource = self
self.historialTableView.register(CustomCellHistorial.self, forCellReuseIdentifier: cellId)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("coming back")
self.fetchTrips()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.historialTableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CustomCellHistorial
let trip = self.trips[indexPath.row]
cell.trip = trip
return cell
}
private func fetchTrips(){
AFWrapper.getTripHistory( success: { (jsonResponse) in
self.trips = []
for item in jsonResponse["trips"].arrayValue {
self.trips.append(RootClass(fromJson:item))
}
self.reloadTrips()
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { (nil) in
self.indicator.stopAnimating()
}
}, failure: { (error) -> Void in
print(error)
})
}
func reloadTrips(){
DispatchQueue.main.async{
self.historialTableView.reloadData()
}
}
This is my CustomCell
class CustomCellHistorial : UITableViewCell{
var trip: RootClass? {
didSet{
dateTimeLabel.text = returnCleanDate(fecha: trip!.createdAt)
distanceAndTimeLabel.text = returnDistanceAndTime(distance: (trip?.trip!.real!.dist!)!, time: (trip?.trip!.real!.time)!)
priceLabel.text = returnCleanPrice(price: (trip?.trip!.real!.price!)!)
ratedLabel.text = "Not Rated"
self.productImage.image = self.returnDriverImage(photoUrl: (self.trip?.driver!.photo!)!)
if (trip!.score) != nil {
let score = trip!.score
if (score?.driver) != nil{
if(trip!.score!.driver!.stars! != 0.0 ){
ratedLabel.isHidden = true
}else{
ratedLabel.isHidden = false
}
}else{
print("yei")
}
}
}
}
private func returnDriverImage(photoUrl: String) -> UIImage{
let url = URL(string: photoUrl)
do {
let data = try Data(contentsOf: url!)
if let roundedimg = UIImage(data: data){
let croppedImageDriver = roundedimg.resize(toTargetSize: self.productImage.frame.size)
return croppedImageDriver
}
} catch let error {
debugPrint("ERRor :: \(error)")
let image = UIImage(named: "perfilIcono")
return image!
}
let image = UIImage(named: "perfilIcono")
return image!
}
Answers that I have found are for older versions of Swift, and the way they make the tableview its in storyboard or they are not handling custom cells.
I think the problem is in the returnDriverImage function.
This line
let data = try Data(contentsOf: url!)
You call from
self.productImage.image = self.returnDriverImage(photoUrl: (self.trip?.driver!.photo!)!)
blocks the main thread and re downloads the same image multiple times when scroll , please consider using SDWebImage
In my app, I have a UICollectionView with some of cells with images from web and some with Youtube videos via UIWebView. When I press Back via NavigationController memory seems to add up more and more every time. I tried disabling video player, it helped a bit, but still..
CollectionViewController
var listOfData = [SingleData]()
var Data:[[String:AnyObject]]?{
didSet{
for asset in Data! {
let info = SingleData.init(Data: asset)
listOfData.append(info)
}
}
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
listOfData.removeAll()
Data?.removeAll()
imageCache.removeAllObjects()
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return listOfData.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Data Single Cell", forIndexPath: indexPath) as! CollectionViewControllerCell
cell.oneData = listOfData[indexPath.item]
return cell
}
CollectionViewControllerCell
#IBOutlet var videoWebView: UIWebView!
#IBOutlet var singleDataImageView: UIImageView!
var oneData:SingleData! {
didSet{
updateUI()
}
}
func updateUI(){
singleImageView.image = nil
if (oneData.isVideo == true) {
videoWebView.hidden = false
singleDataImageView.hidden = true
showVideo()
} else {
videoWebView.hidden = true
singleDataImageView.hidden = false
setImage(oneData.imageURL, singleDataImageView)
}
}
func showVideo() {
if let videoUrl = oneData.videoUrl {
let replacedString = videoUrl.replace("watch?v=", withString: "embed/")
videoWebView.scrollView.scrollEnabled = false
videoWebView.scrollView.bounces = false
videoWebView.allowsInlineMediaPlayback = true
let embededHTML = "<html><head><title>.</title><style>body,html,iframe{margin:0;padding:0;}</style></head><body><iframe width=\"\(videoWebView.frame.width)\" height=\"\(videoWebView.frame.height)\" src=\"\(replacedString)?&playsinline=1\" frameborder=\"0\" allowfullscreen></iframe></body></html>"
videoWebView.loadHTMLString(embededHTML, baseURL:nil)
}
}
func setImage(url : String?, _ imageView : UIImageView) {
imageView.image = nil
imageUrlString = url
if let cUrl = url {
if let imageFromCache = imageCache.objectForKey(cUrl) as? UIImage {
imageView.image = imageFromCache
return
}
if let urlImage = NSURL(string: cUrl) {
Request.sharedInstance.session.dataTaskWithURL(urlImage, completionHandler: { (data, response, error) in
if error != nil {
print(error)
return
}
dispatch_async(dispatch_get_main_queue(), {
let imageToCache = UIImage(data: data!)
if self.imageUrlString == url {
imageView.image = imageToCache
}
imageCache.setObject(imageToCache!, forKey: url!)
});
}).resume()
}
}
}
SingleData
class SingleData {
var imageURL:String? = nil
var videoUrl:String? = nil
var isVideo:Bool? = false
var dataAssets:Dictionary<String, AnyObject>?
init(Data : Dictionary<String, AnyObject>?){
dataAssets = Data
if Data!["MediaContent"]!["MediaType"] as? Int == 2 {
isVideo = true
}
imageURL = dataAssets!["MediaContent"]!["FileUrl"] as? String
videoUrl = dataAssets!["MediaContent"]!["FileName"] as? String
}
}
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
}
}