简体   繁体   English

CollectionView与TableView(Swift)

[英]CollectionView vs TableView (Swift)

I have two classes that do exactly the same thing, load places of a chosen category near the user's location but one uses a collectionView and the other uses a tableView (and a particular animation). 我有两个类做的完全相同,在用户位置附近加载选定类别的位置,但一个使用collectionView,另一个使用tableView(和特定的动画)。

I added only the part of code that serves the question, and although some names are different, all links with the storyboards and other classes are the same as you can see much of the code is very similar between these two classes. 我只添加了解决该问题的代码部分,尽管一些名称不同,但与情节提要和其他类的所有链接都是相同的,因为您可以看到这两个类之间的许多代码非常相似。

My problem is that the class with collectionView work perfectly, but the class using the tableView does not show the cells of the tableView (as if it did not load it properly) if anyone could see if there is a mistake, I would be grateful. 我的问题是具有collectionView的类可以正常工作,但是使用tableView的类不能显示tableView的单元格(好像没有正确加载它),如果有人可以看到是否有错误,我将不胜感激。

This is the class using the collectionView : 这是使用collectionView的类:

import UIKit
import CoreLocation
import AVFoundation

private let reuseIdentifier = "PlacesCell"

extension UIViewController {
    func present(viewController : UIViewController, completion : (() -> ())? = nil ){
        if let presented = self.presentedViewController {
            presented.removeFromParentViewController()
        }
        self.present(viewController, animated: true, completion: completion)
    }
}

class NearbyPlacesViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

    var locationManager:CLLocationManager?

    let minimumSpacing : CGFloat = 15 //CGFloat(MAXFLOAT)
    let cellWidth: CGFloat = 250
    let radius = 5000 // 5km
    var category : QCategory?
    var currentLocation : CLLocationCoordinate2D?
    var places: [QPlace] = []

    var isLoading = false
    var response : QNearbyPlacesResponse?

    override func viewDidLoad() {
        super.viewDidLoad()

        //self.collectionView
        self.title = category?.name

        collectionView?.contentInset = UIEdgeInsets.init(top: 0, left: minimumSpacing, bottom: 0, right: minimumSpacing)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        determineMyCurrentLocation()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillAppear(animated)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        category?.markView()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func canLoadMore() -> Bool {
        if isLoading {
            return false
        }

        if let response = self.response {
            if (!response.canLoadMore()) {
                return false
            }
        }
        return true
    }

    func loadPlaces(_ force:Bool) {

        if !force {
            if !canLoadMore() {
                return
            }
        }

        print("load more")
        isLoading = true
        NearbyPlacesController.getNearbyPlaces(by: category?.name ?? "food", coordinates: currentLocation!, radius: radius, token: self.response?.nextPageToken, completion: didReceiveResponse)
    }

    func didReceiveResponse(response:QNearbyPlacesResponse?, error : Error?) -> Void {
        if let error = error {
            let alertController = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
            let actionDismiss = UIAlertAction(title: "Dismiss", style: .cancel, handler: nil)
            let actionRetry = UIAlertAction(title: "Retry", style: .default, handler: { (action) in
                DispatchQueue.main.async {
                    self.loadPlaces(true)
                }
            })
            alertController.addAction(actionRetry)
            alertController.addAction(actionDismiss)
            DispatchQueue.main.async {
                self.present(viewController: alertController)
            }
        }
        if let response = response {
            self.response = response
            if response.status == "OK" {
                if let placesDownloaded = response.places {
                    places.append(contentsOf: placesDownloaded)
                }

                self.collectionView?.reloadData()
            } else {
                let alert = UIAlertController.init(title: "Error", message: response.status, preferredStyle: .alert)
                alert.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil))
                alert.addAction(UIAlertAction.init(title: "Retry", style: .default, handler: { (action) in
                    DispatchQueue.main.async {
                        self.loadPlaces(true)
                    }
                }))
                self.present(viewController: alert)
            }
            isLoading = false
        }
        else {
            print("response is nil")
        }
    }

    // MARK: UICollectionViewDataSource

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return places.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PlacesCell

        let place = places[indexPath.row]
        cell.update(place: place)

        if indexPath.row == places.count - 1 {
            loadPlaces(false)
        }

        return cell
    }

    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        performSegue(withIdentifier: "maps-vc", sender: indexPath)
    }

    // MARK: UICollectionViewDelegateFlowLayoutDelegate

    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return minimumSpacing
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        let cellPadding: CGFloat = 20.0
        let columnWidth:CGFloat = cellWidth
        let imageWidth = columnWidth
        let labelWidth = columnWidth - cellPadding * 2

        let photoHeight = heightForPhotoAtIndexPath(indexPath: indexPath, withWidth: imageWidth)
        let annotationHeight = heightForAnnotationAtIndexPath(indexPath: indexPath, withWidth: labelWidth)
        let height = photoHeight + annotationHeight

        return CGSize.init(width: columnWidth, height: height)
    }

    // Calculates the height of photo
    func heightForPhotoAtIndexPath(indexPath: IndexPath,
                                   withWidth width: CGFloat) -> CGFloat {

        var size = CGSize.init(width: CGFloat(MAXFLOAT), height: 1)
        let place = places[indexPath.row]

        guard let photo = place.photos?.first, place.photos?.first?.photoRef != nil else {
            return 0
        }

        size = CGSize.init(width: CGFloat(photo.width!), height: CGFloat(photo.height!))

        let boundingRect =  CGRect(x: 0, y: 0, width: width, height: CGFloat(MAXFLOAT))
        let rect  = AVMakeRect(aspectRatio: size, insideRect: boundingRect)

        return rect.size.height
    }

    // Calculates the height label
    func heightForAnnotationAtIndexPath(indexPath: IndexPath, withWidth width: CGFloat) -> CGFloat {

        let place = places[indexPath.row]
        let annotationPadding = CGFloat(5)

        let font = UIFont.systemFont(ofSize: 15)
        let commentHeight = place.heightForComment(font, width: width)

        let height = annotationPadding + commentHeight + annotationPadding

        return height
    }

    // did receive location

    func didReceiveUserLocation(_ userLocation:CLLocation) {
        currentLocation = userLocation.coordinate

        loadPlaces(true)
    }        
     // 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?) {
        if segue.identifier == "maps-vc" && sender is IndexPath {
            let dvc = segue.destination as! MapViewController
            dvc.index = (sender as! IndexPath).row
            dvc.places = places
            dvc.userLocation = currentLocation
        }
     }
}

This is the class using the tableView: 这是使用tableView的类:

import UIKit
import MapKit
import CoreLocation
import GoogleMaps
import GooglePlaces
import Social
import AVFoundation

private let resueIdentifier = "MyTableViewCell"

extension UIViewController {
    func present(viewController : UIViewController, completion : (() -> ())? = nil ){
        if let presented = self.presentedViewController {
            presented.dismiss(animated: true, completion: {
                self.present(viewController, animated: true, completion: completion)
            })
        } else {
            self.present(viewController, animated: true, completion: completion)
        }
    }
}

class CourseClass2: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    var locationManager:CLLocationManager?
    let minimumSpacing : CGFloat = 15 //CGFloat(MAXFLOAT)
    let cellWidth: CGFloat = 250
    let radius = 5000 // 5km
    var category : QCategoryy?
    var currentLocation : CLLocationCoordinate2D?
    var places: [QPlace] = []
    var isLoading = false
    var response : QNearbyPlacesResponse?
    var rows = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = category?.name
}

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        determineMyCurrentLocation()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillAppear(animated)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        rows = 0
        insertRowsMode3()
        tableView.reloadData()
        category?.markView()
}
    @IBAction func refreshTapped(_ sender: Any) {
        rows = 0
        insertRowsMode3()
        tableView.reloadData()
    }

    func canLoadMore() -> Bool {
        if isLoading {
            return false
        }
        if let response = self.response {
            if (!response.canLoadMore()) {
                return false
            }
        }
        return true
    }

    func loadPlaces(_ force:Bool) {

        if !force {
            if !canLoadMore() {
                return
            }
        }

        print("load more")
        isLoading = true
        NearbyPlaces.getNearbyPlaces(by: category?.name ?? "food", coordinates: currentLocation!, radius: radius, token: self.response?.nextPageToken, completion: didReceiveResponse)
    }

    func didReceiveResponse(response:QNearbyPlacesResponse?, error : Error?) -> Void {
        if let error = error {
            let alertController = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
            let actionDismiss = UIAlertAction(title: "Dismiss", style: .cancel, handler: nil)
            let actionRetry = UIAlertAction(title: "Retry", style: .default, handler: { (action) in
                DispatchQueue.main.async {
                    self.loadPlaces(true)
                }
            })
            alertController.addAction(actionRetry)
            alertController.addAction(actionDismiss)
            DispatchQueue.main.async {
                self.present(viewController: alertController)

            }
        }
        if let response = response {
            self.response = response
            if response.status == "OK" {
                if let placesDownloaded = response.places {
                    places.append(contentsOf: placesDownloaded)
                }

                self.tableView?.reloadData()
            } else {
                let alert = UIAlertController.init(title: "Error", message: response.status, preferredStyle: .alert)
                alert.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil))
                alert.addAction(UIAlertAction.init(title: "Retry", style: .default, handler: { (action) in
                    DispatchQueue.main.async {
                        self.loadPlaces(true)
                    }
                }))
                 self.present(viewController: alert)
            }
            isLoading = false
        }
        else {
            print("response is nil")
        }
    }
    func insertRowsMode2() {

        tableView.beginUpdates()
        for i in 0..<places.count {
            insertRowMode2(ind: i, usr: places[i])
        }
        tableView.endUpdates()
    }

    func insertRowMode2(ind:Int,usr:QPlace) {
        tableView.beginUpdates()
        let indPath = IndexPath(row: ind, section: 0)

        rows = ind + 1
      tableView.insertRows(at: [indPath], with: .right)
       tableView.endUpdates()
    }

    func insertRowsMode3() {
        tableView.beginUpdates()
        rows = 0

        insertRowMode3(ind: 0)
        tableView.endUpdates()
    }
    func insertRowMode3(ind:Int) {
        tableView.beginUpdates()
        let indPath = IndexPath(row: ind, section: 0)
        rows = ind + 1
        tableView.insertRows(at: [indPath], with: .right)

        guard ind < places.count-1 else { return }
        DispatchQueue.main.asyncAfter(deadline: .now()+0.20) {

            self.insertRowMode3(ind: ind+1)
        }
        tableView.endUpdates()
    }
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
       return  places.count    /*  rows   */
    }
    public  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell

        let place = places[indexPath.row]
        cell.update(place: place)

        if indexPath.row == places.count - 1 {
            loadPlaces(false)
        }

        return (cell)
    }

     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        tableView.deselectRow(at: indexPath, animated: true)

        UIView.animate(withDuration: 0.2, animations: {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
    })

        performSegue(withIdentifier: "goToLast" , sender: indexPath.row)
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

        if editingStyle == UITableViewCellEditingStyle.delete {

            places.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)

        }
    }
    func didReceiveUserLocation(_ userLocation:CLLocation) {
        currentLocation = userLocation.coordinate

        loadPlaces(true)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "goToLast" && sender is IndexPath {

            let dvc = segue.destination as! FinalClass
            dvc.index = (sender as! IndexPath).row
            dvc.places = places
            dvc.userLocation = currentLocation

            }
        }

    @IBAction func IndTapped(_ sender: Any) {
    dismiss(animated: true, completion: nil)

    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

Did you set the CourseClass2 class as the delegate/dataSource for your tableView? 您是否将CourseClass2类设置为tableView的委托/数据源?

Try adding 尝试添加

tableView.dataSource = self
tableView.delegate = self

to your viewDidLoad method. 到您的viewDidLoad方法。

Almost every time I run into an issue like yours is because I forgot to set the delegate or dataSource. 几乎每次遇到像您这样的问题时,都是因为我忘记设置委托或数据源。

Edit: I had previously only added the delegate on my answer, thanks Dennis for reminding me of the dataSource. 编辑:我以前只在我的答案上添加了委托,感谢丹尼斯使我想起了dataSource。

I saw your are doing an async update, but a synced reloadData... 我看到您正在执行异步更新,但是同步了reloadData ...

You will have to do the update inside the asyncAfter, or add a closure to your insertRowMode3 and reloadData in that closure... 您将必须在asyncAfter内部进行更新,或在您的insertRowMode3中添加一个闭包,并在该闭包中重新加载数据...

Could you try this? 你可以试试这个吗?

func insertRowMode3(ind:Int) {
    DispatchQueue.main.asyncAfter(deadline: .now()+0.20) {
        tableView.reloadData()
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM