UITableView 在点击屏幕之前不可见

Posted

技术标签:

【中文标题】UITableView 在点击屏幕之前不可见【英文标题】:UITableView not visible until tapped on screen 【发布时间】:2020-07-12 18:43:46 【问题描述】:

我是 swift 新手,

我正在开发一个应用程序 对于从服务器获取 URL 的图像,我有 2 个不同的 API 来获取横幅数据和 tableView 数据。

横幅加载并运行后,我正在填充 UITableView, 问题是某个时间(大约 1 分钟)后 UITableView 可见,或者我必须点击屏幕使其可见 当我在屏幕之间切换时,横幅也无法正常滚动。

有人可以查看代码并帮助我解决问题吗?

这是我到目前为止所做的 Controller Code

class CompanyViewController: UIViewController,IndicatorInfoProvider 
    
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var sliderCollectionView: UICollectionView!
    
    @IBOutlet weak var pageView: UIPageControl!
    var activityIndicator: UIActivityIndicatorView=UIActivityIndicatorView()
    var imgArr = [UIImage(named: "LoginHeader")]
    
    var imgArrTemp = [String]()
    
    var sectionHeaderName = [String]()
    var imageArray = [[String]]()
    var sectionImage = [String]()

    
    var timer = Timer()
    var counter = 0
    
    override func viewDidLoad() 
        super.viewDidLoad()
        pageView.numberOfPages = imgArr.count
        pageView.currentPage = 0
        tableView.delegate = self
        tableView.dataSource = self
        tableView.estimatedRowHeight = 120
        tableView.tableFooterView = UIView()
        
    
    
    override func viewDidAppear(_ animated: Bool) 
        super.viewDidAppear(animated)
        self.getBannerData()
         DispatchQueue.main.async() 
            self.getCenterData()
            self.tableView.reloadData()
        
        
    
    
    func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo 
        return IndicatorInfo(title: "COMPANY")
    
    
    @objc func changeImage() 
        if counter < imgArr.count 
            let index = IndexPath.init(item: counter, section: 0)
            self.sliderCollectionView.scrollToItem(at: index, at: .centeredHorizontally, animated: true)
            pageView.currentPage = counter
            counter += 1
         else 
            counter = 0
            let index = IndexPath.init(item: counter, section: 0)
            self.sliderCollectionView.scrollToItem(at: index, at: .centeredHorizontally, animated: true)
            pageView.currentPage = counter
            counter = 1
        
        
    
    
    
    func getCenterData()
        let appId = LocalStorage.getStringDataFromLocalStorage(key:Constants.APPID)
        let token = "Bearer "+LocalStorage.getStringDataFromLocalStorage(key: Constants.TOKEN)
        if let url = URL(string: Constants.EXPERIENCE_CENTER+appId)
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            request.setValue(token, forHTTPHeaderField:"Authorization")
            APIManager.sharedInstance.getCall(request: request)
                (data) in
                if(!data.isEmpty)
                    do
                         let _object = try JSONDecoder().decode(ExperienceCenterModel.self, from: Data(data.utf8))
                        let data = try JSONEncoder().encode(_object.practice)
                        let jsonString = String(data: data, encoding: .utf8)!
                        LocalStorage.saveStringDataInLocalStorage(key: Constants.PRACTICE, value: jsonString)
                        
                        let technology = try JSONEncoder().encode(_object.technology)
                        let technologyJsonString = String(data: technology, encoding: .utf8)!
                        
                        LocalStorage.saveStringDataInLocalStorage(key: Constants.TECTNOLOGY, value: technologyJsonString)
                        
                        for sectionHeader in _object.practice.data
                            self.sectionHeaderName.insert(sectionHeader.practiceName!, at: self.sectionHeaderName.count)
                            self.sectionImage.removeAll()
                            for images in sectionHeader.experience
                                let url = images.experienceImage.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
                                self.sectionImage.insert(url!, at: self.sectionImage.count)
                            
                            self.imageArray.insert(self.sectionImage, at: self.imageArray.count)
                        
                       self.tableView.reloadData()
                    catch
                        let nsError = error as NSError
                        print(nsError.localizedDescription)
                    
                
            
        
        
    
    
    func getBannerData()
        let defaults = UserDefaults.standard
        let appId = defaults.string(forKey: Constants.APPID)
        let token = "Bearer "+defaults.string(forKey: Constants.TOKEN)!
        if let url = URL(string: Constants.EXPERIENCE_BANNER_CENTER+appId!)
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            request.setValue(token, forHTTPHeaderField:"Authorization")
            APIManager.sharedInstance.getCall(request: request)
                (data) in
                if(!data.isEmpty)
                    do
                        let _object = try JSONDecoder().decode(SliderModel.self, from: Data(data.utf8))
                        self.imgArr.removeAll()
                        for images in _object.data
                            let url = images.experience_image.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
                                let imageUrlString = url
                                let imageUrl:URL = URL(string: imageUrlString!)!
//                            self.imgArrTemp.insert(url!, at: self.imgArrTemp.count)
//                                self.pageView.numberOfPages = self.imgArrTemp.count

                            
                                DispatchQueue.main.async() 
                                    let imageData:NSData = NSData(contentsOf: imageUrl)!

                                    self.imgArr.insert(UIImage(data: imageData as Data), at: self.imgArr.count)
                                    self.pageView.numberOfPages = self.imgArr.count
                                
                        
                        self.stopActivityIndicator()
                    
                    catch
                        self.stopActivityIndicator()
                        self.showAlertMessage(alertMessage: Constants.COMMON_ERROR)
                    
                    DispatchQueue.main.async 
                        self.timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(self.changeImage), userInfo: nil, repeats: true)
                    
                
            
        
    
    
    
    func startActivityIndicator()
        activityIndicator.center = CGPoint(x: 180, y: -40)
        activityIndicator.hidesWhenStopped = true
        activityIndicator.style = UIActivityIndicatorView.Style.gray
        view.addSubview(activityIndicator)
        
        activityIndicator.startAnimating()
        UIApplication.shared.beginIgnoringInteractionEvents()
    
    
    func stopActivityIndicator()
        activityIndicator.stopAnimating()
        UIApplication.shared.endIgnoringInteractionEvents()
    
    
    func showAlertMessage(alertMessage:String)
        self.stopActivityIndicator()
        let alert = UIAlertController(title: "Alert", message: alertMessage, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler:  action in
            switch action.style
            case .default:
                print("default")
                
            case .cancel:
                print("cancel")
                
            case .destructive:
                print("destructive")
                
                
            ))
        self.present(alert, animated: true, completion: nil)
    
    
    


extension CompanyViewController: UICollectionViewDelegate, UICollectionViewDataSource 
    
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        return 10
    
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        if(self.imgArr.count > 0)
        if let vc = cell.viewWithTag(111) as? UIImageView 
            if(self.imgArr.count == indexPath.row)
                vc.image = self.imgArr[indexPath.row-1]
            else
                vc.image = self.imgArr[indexPath.row]
            
            
        
        return cell
    
    
    


extension CompanyViewController: UICollectionViewDelegateFlowLayout 
    
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets 
        return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
        let size = sliderCollectionView.frame.size
        return CGSize(width: size.width, height: size.height)
    
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat 
        return 0.0
    
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat 
        return 0.0
    


extension CompanyViewController: UITableViewDelegate, UITableViewDataSource 
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
        if self.sectionHeaderName.count>0 
            return self.sectionHeaderName[section]
        else
            return ""
        
    
    
    //NUMBER OF SECTION WE WANT IN A TABLE
    func numberOfSections(in tableView: UITableView) -> Int 
        return self.sectionHeaderName.count
    
    /*NUMNBER OF ROWS IN A SECTION*/
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return 1
    
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
        return 130
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath) as? TableViewCell else fatalError("Unable to create table view cell")
            cell.cellDataURI = self.imageArray[indexPath.section]
            cell.collectionView.reloadData()
            return cell
    


Table View Cell 的代码

class TableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout  
    
    var  cellData = [UIImage]()
    var  cellDataURI = [String]()
    
    lazy var imageCache: NSCache<NSString,UIImage> = 
        return NSCache<NSString,UIImage>.init()
    ()

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
         return self.cellDataURI.count
    
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
       let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as? CollectionViewCell
        
        let url = cellDataURI[indexPath.row]
        
        guard let image = self.imageCache.object(forKey: NSString(string: url))else
            return loadImage(url: URL(string: url)!, cell: cell!, indexPath: indexPath)
        
        cell?.imageView.image = image//cellDataURI[indexPath.row]
        return cell!
    
    
    func loadImage(url: URL,cell: CollectionViewCell, indexPath: IndexPath) -> CollectionViewCell
        URLSession.shared.dataTask(with: url)
            (rawData, _, _) in
            guard let data = rawData else  return 
            guard let image = UIImage.init(data: data) else
                return
            
             self.imageCache.setObject(image, forKey: NSString(string: url.absoluteString) )
            DispatchQueue.main.async 
                guard let visCell = self.collectionView.cellForItem(at: indexPath) as? CollectionViewCell else return 
                visCell.imageView.image = image
            
            
           
           
            
        .resume()
        return cell
    
    
    
    func numberOfSections(in collectionView: UICollectionView) -> Int 
        return 1
    

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
        let size = CGSize(width: 150, height: 300)
        return size
        
    
    @IBOutlet weak var collectionView: UICollectionView!
    
    override func awakeFromNib() 
        super.awakeFromNib()
        self.collectionView.delegate = self
        self.collectionView.dataSource = self
    

任何帮助将不胜感激

【问题讨论】:

【参考方案1】:

您可以使用 dispatch group 运行多个 api 并在所有任务完成时收到通知。然后重新加载tableview

let dispatchGroup = DispatchGroup()
    
    dispatchGroup.enter()
    firstApiCall(completion:
        dispatchGroup.leave()
    )
    
    dispatchGroup.enter()
    secondApiCall(completion:
        dispatchGroup.leave()
    )
    
    dispatchGroup.notify(queue: .main) [weak self] in
        
        print("All Data fetched")
        self?.tableView.reloadData()
   

【讨论】:

以上是关于UITableView 在点击屏幕之前不可见的主要内容,如果未能解决你的问题,请参考以下文章

UITableView 要求一个在屏幕上不可见的单元格

屏幕外(不可见)TableViewCells 不设置动画

当单元格不可见时,UITableView indexPathForCell 返回 null

在ios中为uitableview的所有单元格下添加不可见的detailview,并在用户点击单元格时使其可见/不可见

当单元格滚动回视图时表格崩溃

解析的提要在 UITableView 上不可见,cellForRowAtIndexPath 方法未被访问