我有一个包含数据的 UITableView,它是从 url 解析的。
UITableView 将加载更多数据,当向右滚动到底部时(或有更多空间滚动,但接近尾部 - 两者都执行,结果相同)
当加载更多数据时 - 我简单地将它附加到我的类的数组,其中包含 TableView 的数据,然后列表向后滚动超过列表的一半(例如,有 40 个项目,再加载 10 个-> 滚动回 20-25)。
追加完成后调用 TableView.reloadData()。
做的计划有没有错误? 我可以共享代码,但这很常见。
class TabAllTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, XMLParserDelegate {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var BlogAllTableView: UITableView!
var loadMoreStatus = false
var maxPage = 1.0
var currentPageLoad = 1.0
var blogList: [DetailAndRenderBlogObject] = []
var eName: String = String()
var category = String()
var full_text = String()
var short_text = String()
var blog_title = String()
var avatar = String()
var full_name = String()
var url = String()
var created_at = String()
private let CATEGORY = ConfigData.CATEGORY_ALL
let cellIdentifier = "BlogTableViewCell"
override func viewDidLoad() {
super.viewDidLoad()
setupNavMenuButtons()
self.BlogAllTableView.insertSubview(refreshControl, at: 0)
self.BlogAllTableView.tableFooterView?.isHidden = true
downloadBlogData(1, true, true)
BlogAllTableView.delegate = self
BlogAllTableView.dataSource = self
}
var refreshControl: UIRefreshControl = {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action:
#selector(TabAllTableViewController.handleRefresh(_:)),
for: UIControlEvents.valueChanged)
refreshControl.tintColor = Colors.ColorLoadingIndicator
return refreshControl
}()
@objc func handleRefresh(_ refreshControl: UIRefreshControl) {
downloadBlogData(1, true, false)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let currentOffset = scrollView.contentOffset.y
let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
let deltaOffset = maximumOffset - currentOffset
if deltaOffset <= 0 && currentPageLoad < maxPage {
loadMore()
}
}
func loadMore() {
if ( !loadMoreStatus ) {
self.loadMoreStatus = true
self.activityIndicator.startAnimating()
self.BlogAllTableView.tableFooterView?.isHidden = false
downloadBlogData(currentPageLoad, false, false)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.sideMenuController?.isLeftViewEnabled = true
AppDelegate.tabBarReference.isHidden = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.navigationBar.topItem?.title = "all".localized()
}
private func setupNavMenuButtons() {
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(handleMenuRefresh))
let image = UIImage(named:"menu_ham.png")
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
imageView.contentMode = .scaleAspectFit
imageView.isUserInteractionEnabled = true
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
imageView.image = image
let imageHeight = navigationController?.navigationBar.frame.size.height
let wrapperView = UIView(frame: CGRect(x: 0, y: 0, width: imageHeight!, height: imageHeight!))
wrapperView.addSubview(imageView)
imageView.center = CGPoint(x: imageView.frame.size.width / 2, y: wrapperView.frame.size.height / 2)
let tap = UITapGestureRecognizer(target: self, action: #selector(TabAllTableViewController.menuButtonClick))
wrapperView.addGestureRecognizer(tap)
let btnHamburgerMenu: UIBarButtonItem = UIBarButtonItem(customView: wrapperView)
navigationItem.setLeftBarButton(btnHamburgerMenu, animated: false)
}
@objc private func menuButtonClick()
{
self.sideMenuController?.showLeftViewAnimated()
}
@objc private func handleMenuRefresh() {
downloadBlogData(1, true, true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return blogList.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if (section != 0) {
return 12
} else {
return CGFloat.leastNonzeroMagnitude
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = UIColor.black.withAlphaComponent(0.0)
return view
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return CGFloat(ConfigData.CELL_HEIGHT)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Configure the cell...
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? BlogTableViewCell else {
fatalError("The dequeued cell is not an instance of BlogTableViewCell.")
}
cell.layer.cornerRadius = 10
let blogItem = blogList[indexPath.section]
cell.avatarBackground.contentMode = .scaleAspectFill
cell.avatarBackground.layer.cornerRadius = cell.avatarBackground.frame.size.width / 2;
cell.avatarBackground.clipsToBounds = true;
if let authorImgUrl = URL(string: blogItem.authorImg) {
cell.authorRoundImage.contentMode = .scaleAspectFill
cell.authorRoundImage.layer.cornerRadius = cell.authorRoundImage.frame.size.width / 2;
cell.authorRoundImage.clipsToBounds = true;
//Helper.downloadImage(url: authorImgUrl, imageview: cell.authorRoundImage)
cell.authorRoundImage.sd_setImage(with: authorImgUrl)
}
if let headerImageUrl = URL(string: blogItem.image) {
cell.bigImage.contentMode = .scaleToFill
//Helper.downloadImage(url: headerImageUrl, imageview: cell.bigImage)
cell.bigImage.sd_setImage(with: headerImageUrl)
}
cell.authorLabel.text = blogItem.author
cell.dateLabel.text = blogItem.date
cell.title.text = blogItem.title
cell.titleDescription.text = blogItem.shortDescription
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let secondViewController = self.storyboard!.instantiateViewController(withIdentifier: "ShowArticleViewController") as! ShowArticleViewController
secondViewController.blogItem = blogList[indexPath.section]
self.navigationController!.pushViewController(secondViewController, animated: true)
}
private func downloadBlogData(_ page : Double, _ refresh : Bool, _ showOverlay : Bool) {
// print(InAppProperties.sharedInstance.getDefaulLang())
if showOverlay {
LoadingOverlay.shared.showOverlay(view: self.view)
}
let req = NSMutableURLRequest(url: NSURL(string: Helper.getBlogUrl(CATEGORY, Int(page)))! as URL)
req.httpMethod = "GET"
req.httpBody = "key=\"value\"".data(using: String.Encoding.utf8) //This isn't for GET requests, but for POST requests so you would
URLSession.shared.dataTask(with: req as URLRequest) { data, response, error in
if error != nil {
//Your HTTP request failed.
print(error?.localizedDescription)
} else {
//Your HTTP request succeeded
if refresh {
self.currentPageLoad = 1
} else {
self.currentPageLoad += 1
}
let stringData = String(data: data!, encoding: String.Encoding.utf8)!
//print(stringData)
let xml = SWXMLHash.parse(stringData)
if page == 1 {
self.blogList = [DetailAndRenderBlogObject]()
}
for elem in xml["response"]["provider"].all {
let itemList = elem["item"]
for article in itemList.all {
let blogItem = DetailAndRenderBlogObject()
blogItem.articleUrl = article["url"].element!.text
blogItem.author = article["full_name"].element!.text
blogItem.authorImg = article["avatar"].element!.text
blogItem.text = article["full_text"].element!.text
blogItem.shortDescription = article["short_text"].element!.text
blogItem.title = article["title"].element!.text
blogItem.categoryUrl = article["category"].element!.text
blogItem.image = self.repairLink(article["thumbnail"].element!.text)
blogItem.date = self.formatDate(article["created_at"].element!.text)
if (blogItem.categoryUrl.lowercased().range(of:"video") == nil &&
blogItem.title.lowercased().range(of: "видео") == nil) {
self.blogList.append(blogItem)
}
}
if let totalItemsCount = xml["response"]["pagination"]["totalCount"].element?.text {
self.maxPage = (Double(totalItemsCount)! / 10).rounded(.up)
}
}
DispatchQueue.main.async {
self.BlogAllTableView.reloadData()
self.refreshControl.endRefreshing()
self.loadMoreStatus = false
self.activityIndicator.stopAnimating()
self.BlogAllTableView.tableFooterView?.isHidden = true
if showOverlay {
LoadingOverlay.shared.hideOverlayView()
}
if (page == 1) {
let indexPath = NSIndexPath(row: 0, section: 0)
self.BlogAllTableView.scrollToRow(at: indexPath as IndexPath, at: .top, animated: true)
}
}
}
}.resume()
}
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
eName = elementName
if elementName == "item" {
category = String()
full_text = String()
short_text = String()
blog_title = String()
avatar = String()
full_name = String()
url = String()
created_at = String()
}
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == "item" {
let blogListItem = DetailAndRenderBlogObject()
blogListItem.categoryUrl = category
blogListItem.text = full_text
blogListItem.shortDescription = short_text
blogListItem.title = blog_title
blogListItem.authorImg = avatar
blogListItem.author = full_name
blogListItem.articleUrl = url
blogListItem.date = created_at
print("WRITING DATA")
blogList.append(blogListItem)
}
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
//print("ENAME: " + eName)
switch eName {
case "category":
print("writing category: " + string)
category = string;
case "full_text":
full_text = string;
case "short_text":
short_text = string;
case "title":
title = string;
case "thumbnail":
avatar = string;
case "avatar":
avatar = string;
case "full_name":
full_name = string;
case "url":
url = string;
case "created_at":
created_at = string;
default:
()
}
}
func parserDidEndDocument(_ parser: XMLParser) {
}
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
print(parseError)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func repairLink(_ link : String) -> String { //sometimes links looks like https://asdasd.comhttps://asdasd.com/...
let newLink = link.suffix(from: link.index(link.startIndex, offsetBy: 1))
if let range = newLink.range(of: "http") {
let substring = newLink[range.lowerBound...]
return String(substring)
}
return link
}
func formatDate(_ date : String) -> String { //date String "2018-01-22 08:59:43"
if let dividerIndex = date.index(of: " ") {
return String(date[..<dividerIndex])
}
return date
}
}
最佳答案
重新加载整个表是一项相当密集的操作。更好的方法可能是使用 tableView.insertRows(at:with:) 方法。一个例子是这样的-
func didFinishLoadingData(newBlogs: [DetailAndRenderBlogObject]) {
tableView.beginUpdates()
var indexPaths: [IndexPath] = []
for row in (blogList.count..<(blogList.count + newBlogs.count)) {
indexPaths.append(IndexPath(row: row, section: 0))
}
blogList.append(contentsOf: newBlogs)
tableView.insertRows(at: indexPaths, with: .fade)
tableView.endUpdates()
}
关于ios - Swift,将更多数据加载到 TableView 中会导致滚动滞后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49027412/
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co
我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame
print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上
我正在尝试获得良好的Ruby编码风格。为防止意外调用具有相同名称的局部变量,我总是在适当的地方使用self.。但是现在我偶然发现了这个:classMyClass上面的代码导致错误privatemethodsanitize_namecalled但是当删除self.并仅使用sanitize_name时,它会起作用。这是为什么? 最佳答案 发生这种情况是因为无法使用显式接收器调用私有(private)方法,并且说self.sanitize_name是显式指定应该接收sanitize_name的对象(self),而不是依赖于隐式接收器(也是