如何在不减慢 uipageViewController 滚动速度的情况下,在 Data ViewController 的 viewDidLoad 中加载数据?
How to load data in viewDidLoad of DataViewController, without slowing down pageViewController scrolling?
我有一个 dataViewController,它需要来自 API 的数据来填充图形和一些新闻。 news API 调用非常快速和简单,但是图形调用完全减慢了页面滚动,因为它是在 viewDidLoad() 中调用的。我编辑了图表 API 下载数据然后将其存储到缓存,而不是在 viewDidLoad 上,它检查缓存中是否有任何数据,然后如果有则使用它,但它仍然很糟糕滚动页面时速度慢。我怎样才能解决这个问题?这是我的一些 DataViewController 代码,用于处理与图表有关的所有事情:
extension DataViewController {
func GetOnlyDateMonthYearFromFullDate(currentDateFormate:NSString , conVertFormate:NSString , convertDate:NSString ) -> NSString
{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = currentDateFormate as String
let formatter = DateFormatter()
formatter.dateFormat = "yyyy'-'MM'-'dd'-'HH':'mm':'ssZZZ" as String
let finalDate = formatter.date(from: convertDate as String)
formatter.dateFormat = conVertFormate as String
let dateString = formatter.string(from: finalDate!)
return dateString as NSString
}
//Charts
@objc func handleLongPress(longPressGesture:UILongPressGestureRecognizer) {
let p = longPressGesture.location(in: self.chartView)
if longPressGesture.state == UIGestureRecognizer.State.began && isLineChartExpanded == false {
mainLabelContainer.fadeOut()
chartViewExpandedConstraints()
isLineChartExpanded = true
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
} else if longPressGesture.state == UIGestureRecognizer.State.began && isLineChartExpanded == true {
mainLabelContainer.fadeIn()
chartViewLandscapeConstraints()
isLineChartExpanded = false
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
}
}
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
var xInt = Int()
//The Currency Unit taken from the exchange section of the API.
xInt = Int(entry.x)
let currencyUnit = CGExchange.shared.exchangeData[0].rates[defaultCurrency]!.unit
chartPriceLabel.textColor = UIColor.white.withAlphaComponent(0.5)
chartDateLabel.textColor = UIColor.white.withAlphaComponent(0.5)
chartPriceLabel.isHidden = false
chartDateLabel.isHidden = false
chartPriceLabel.text = "\(currencyUnit)\(round(1000*entry.y)/1000)"
let date = self.GetOnlyDateMonthYearFromFullDate(currentDateFormate: "yyyy-MM-dd'T'HH:mm:ss.SSSZ", conVertFormate: "MMM d, h:mm a", convertDate: self.days[xInt] as NSString)
chartDateLabel.text = "\(date as String)"
}
//Graph Buttons and states
enum GraphStat {
case day, fortnight, month
}
@objc func todayButtonAction(sender: UIButton!) {
self.prices = []
self.days = []
CGCharts.shared.graphSetup = .day
CGCharts.shared.getData(coin: self.dataObject, defaultCurrency: self.defaultCurrency, arr: true, completion: { (success) in
self.prepareGraph(arr: true, completion: { (success) in
DispatchQueue.main.async {
self.chartView.animate(xAxisDuration: 4.0)
}
})
})
}
@objc func fortnightButtonAction(sender: UIButton!) {
self.prices = []
self.days = []
CGCharts.shared.graphSetup = .week
CGCharts.shared.getData(coin: self.dataObject, defaultCurrency: self.defaultCurrency, arr: true, completion: { (success) in
self.prepareGraph(arr: true, completion: { (success) in
DispatchQueue.main.async {
self.chartView.animate(xAxisDuration: 4.0)
}
})
})
}
@objc func monthButtonAction(sender: UIButton!) {
self.prices = []
self.days = []
CGCharts.shared.graphSetup = .month
CGCharts.shared.getData(coin: self.dataObject, defaultCurrency: self.defaultCurrency, arr: true, completion: { (success) in
self.prepareGraph(arr: true, completion: { (success) in
DispatchQueue.main.async {
self.chartView.animate(xAxisDuration: 4.0)
}
})
})
}
func lineChartUpdate(dataPoints: [String], values: [Double]) {
if CGCharts.shared.graphSetup == .day {
graphSetup = .day
} else if CGCharts.shared.graphSetup == .week {
graphSetup = .fortnight
} else if CGCharts.shared.graphSetup == .month {
graphSetup = .month
}
//Graph State buttons switch status for highlighting buttons.
switch graphSetup {
case .day:
todayButton.alpha = 0.5
fortnightButton.alpha = 1.0
monthButton.alpha = 1.0
case .fortnight:
fortnightButton.alpha = 0.5
todayButton.alpha = 1.0
monthButton.alpha = 1.0
case .month:
monthButton.alpha = 0.5
todayButton.alpha = 1.0
fortnightButton.alpha = 1.0
}
//Graph data management
var lineChartEntry = [ChartDataEntry]()
for i in 0..<prices.count {
//Graph marker from extension
if prices != [] {
let value = ChartDataEntry(x: Double(i), y: values[i])
lineChartEntry.append(value)
let line1 = LineChartDataSet(values: lineChartEntry, label: "Price")
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
dateFormatter.locale = Locale(identifier: "en_US")
let dateObjects = self.days.compactMap { dateFormatter.date(from: [=10=]) }
let dateStrings = dateObjects.compactMap { dateFormatter.string(from: [=10=]) }
self.chartView.xAxis.valueFormatter = DefaultAxisValueFormatter(block: {(index, _) in
return dateStrings[Int(index)]
})
line1.setColor(.white)
line1.drawVerticalHighlightIndicatorEnabled = true
line1.drawHorizontalHighlightIndicatorEnabled = true
line1.mode = .cubicBezier
line1.lineWidth = 2.0
line1.drawValuesEnabled = true
line1.valueTextColor = UIColor.white
line1.drawCirclesEnabled = false
chartView.xAxis.valueFormatter = IndexAxisValueFormatter(values:dateStrings)
chartView.xAxis.granularity = 1
chartView.leftAxis.drawGridLinesEnabled = false
chartView.xAxis.drawGridLinesEnabled = false
//Expanded
chartView.rightAxis.enabled = false
chartView.leftAxis.enabled = false
chartView.xAxis.enabled = false
chartView.rightAxis.drawGridLinesEnabled = false
chartView.legend.enabled = false
chartView.dragEnabled = false
chartView.pinchZoomEnabled = false
chartView.drawMarkers = false
chartView.doubleTapToZoomEnabled = false
chartView.isUserInteractionEnabled = true
//Graph Data.
let data = LineChartData()
data.addDataSet(line1)
chartView.data = data
}
}
}
//Dismiss Keyboard when Tap
override func touchesBegan(_ touches: Set<UITouch>,
with event: UIEvent?) {
self.view.endEditing(true)
}
//GraphData
func prepareGraph(arr: Bool, completion: @escaping (Bool) -> ()) {
if Storage.fileExists("\(dataObject)GraphData", in: Storage.Directory.caches) {
print("Exists")
self.priceData = Storage.retrieve("\(dataObject)GraphData", from: Storage.Directory.caches, as: [Price].self)
self.days = self.priceData.map({ [=10=].date.description })
self.prices = self.priceData.map({ [=10=].price })
DispatchQueue.main.async {
// self.chartView.animate(xAxisDuration: 4.0)
self.lineChartUpdate(dataPoints: self.days, values: self.prices)
}
} else {
self.prices = []
self.days = []
print("didn'tExist")
CGCharts.shared.graphSetup = .day
print("ChartsCleared")
CGCharts.shared.getData(coin: self.dataObject, defaultCurrency: self.defaultCurrency, arr: true) { (success) in
self.updateGraph()
DispatchQueue.main.async {
self.lineChartUpdate(dataPoints: self.days, values: self.prices)
}
}
}
}
//GraphData In Storage
func updateGraph() {
self.priceData = Storage.retrieve("\(dataObject)GraphData", from: Storage.Directory.caches, as: [Price].self)
self.days = self.priceData.map({ [=10=].date.description })
self.prices = self.priceData.map({ [=10=].price })
DispatchQueue.main.async {
// self.chartView.animate(xAxisDuration: 4.0)
self.lineChartUpdate(dataPoints: self.days, values: self.prices)
}
}
}
这是来自 API 管理器文件的实际 API 调用:
import Foundation
struct Root: Codable {
let prices: [Price]
}
struct Price: Codable {
let date: Date
let price: Double
}
class CGCharts {
var priceData = [Price]()
static let shared = CGCharts()
var currency = ""
var days = ""
enum GraphStatus {
case day, week, month
}
var graphSetup = GraphStatus.day
func getAllCharts(arr: Bool, completion: @escaping (Bool) -> ()) {
}
func getData(coin: String, defaultCurrency: String, arr: Bool, completion: @escaping (Bool) -> ()) {
switch graphSetup {
case .day:
days = "1"
case .week:
days = "14"
case .month:
days = "30"
}
let urlJSON = "https://api.coingecko.com/api/v3/coins/\(coin)/market_chart?vs_currency=\(defaultCurrency)&days=\(days)"
guard let url = URL(string: urlJSON) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let prices = try JSONDecoder().decode(Root.self, from: data).prices
print(prices.first!.date.description(with:.current)) // "Saturday, September 1, 2018 at 6:25:38 PM Brasilia Standard Time\n"
print(prices[0].price)
self.priceData = prices
Storage.store(self.priceData, to: Storage.Directory.caches, as: "\(coin)GraphData")
completion(arr)
} catch {
print(error)
}
}.resume()
}
}
extension Price {
public init(from decoder: Decoder) throws {
var unkeyedContainer = try decoder.unkeyedContainer()
let date = try unkeyedContainer.decode(UInt64.self).date
let price = try unkeyedContainer.decode(Double.self)
self.init(date: date, price: price)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(date.unixEpochTime)
try container.encode(price)
}
}
extension UInt64 {
var date: Date {
return Date(timeIntervalSince1970: TimeInterval(self)/1000)
}
}
extension Date {
var unixEpochTime: UInt64 {
return UInt64(timeIntervalSince1970*1000)
}
}
这两个文件之一中的某些内容太慢了,导致页面滚动非常烦人。它只有大约 2 秒,但它会冻结那 2 秒的分页动画,我需要找到一种使滚动平滑和响应迅速的方法,即使这意味着在加载之前的几秒钟内不显示任何图表数据。我该怎么做,到底是什么让一切都变慢了?我也怀疑它可能与以下内容有关:
self.priceData = Storage.retrieve("\(dataObject)GraphData", from: Storage.Directory.caches, as: [Price].self)
self.days = self.priceData.map({ [=12=].date.description })
self.prices = self.priceData.map({ [=12=].price })
因为这是循环遍历一个包含数据的大结构,然后将它们添加到 2 个数组中,也许这会在它发生时阻止一切?谢谢。
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let self = self else {
return
}
self.priceData = Storage.retrieve("\(dataObject)GraphData", from:Storage.Directory.caches, as: [Price].self)
self.days = self.priceData.map({ [=10=].date.description })
self.prices = self.priceData.map({ [=10=].price })
DispatchQueue.main.async { [weak self] in
// update your UI here based on the data
}
}
我有一个 dataViewController,它需要来自 API 的数据来填充图形和一些新闻。 news API 调用非常快速和简单,但是图形调用完全减慢了页面滚动,因为它是在 viewDidLoad() 中调用的。我编辑了图表 API 下载数据然后将其存储到缓存,而不是在 viewDidLoad 上,它检查缓存中是否有任何数据,然后如果有则使用它,但它仍然很糟糕滚动页面时速度慢。我怎样才能解决这个问题?这是我的一些 DataViewController 代码,用于处理与图表有关的所有事情:
extension DataViewController {
func GetOnlyDateMonthYearFromFullDate(currentDateFormate:NSString , conVertFormate:NSString , convertDate:NSString ) -> NSString
{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = currentDateFormate as String
let formatter = DateFormatter()
formatter.dateFormat = "yyyy'-'MM'-'dd'-'HH':'mm':'ssZZZ" as String
let finalDate = formatter.date(from: convertDate as String)
formatter.dateFormat = conVertFormate as String
let dateString = formatter.string(from: finalDate!)
return dateString as NSString
}
//Charts
@objc func handleLongPress(longPressGesture:UILongPressGestureRecognizer) {
let p = longPressGesture.location(in: self.chartView)
if longPressGesture.state == UIGestureRecognizer.State.began && isLineChartExpanded == false {
mainLabelContainer.fadeOut()
chartViewExpandedConstraints()
isLineChartExpanded = true
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
} else if longPressGesture.state == UIGestureRecognizer.State.began && isLineChartExpanded == true {
mainLabelContainer.fadeIn()
chartViewLandscapeConstraints()
isLineChartExpanded = false
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
}
}
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
var xInt = Int()
//The Currency Unit taken from the exchange section of the API.
xInt = Int(entry.x)
let currencyUnit = CGExchange.shared.exchangeData[0].rates[defaultCurrency]!.unit
chartPriceLabel.textColor = UIColor.white.withAlphaComponent(0.5)
chartDateLabel.textColor = UIColor.white.withAlphaComponent(0.5)
chartPriceLabel.isHidden = false
chartDateLabel.isHidden = false
chartPriceLabel.text = "\(currencyUnit)\(round(1000*entry.y)/1000)"
let date = self.GetOnlyDateMonthYearFromFullDate(currentDateFormate: "yyyy-MM-dd'T'HH:mm:ss.SSSZ", conVertFormate: "MMM d, h:mm a", convertDate: self.days[xInt] as NSString)
chartDateLabel.text = "\(date as String)"
}
//Graph Buttons and states
enum GraphStat {
case day, fortnight, month
}
@objc func todayButtonAction(sender: UIButton!) {
self.prices = []
self.days = []
CGCharts.shared.graphSetup = .day
CGCharts.shared.getData(coin: self.dataObject, defaultCurrency: self.defaultCurrency, arr: true, completion: { (success) in
self.prepareGraph(arr: true, completion: { (success) in
DispatchQueue.main.async {
self.chartView.animate(xAxisDuration: 4.0)
}
})
})
}
@objc func fortnightButtonAction(sender: UIButton!) {
self.prices = []
self.days = []
CGCharts.shared.graphSetup = .week
CGCharts.shared.getData(coin: self.dataObject, defaultCurrency: self.defaultCurrency, arr: true, completion: { (success) in
self.prepareGraph(arr: true, completion: { (success) in
DispatchQueue.main.async {
self.chartView.animate(xAxisDuration: 4.0)
}
})
})
}
@objc func monthButtonAction(sender: UIButton!) {
self.prices = []
self.days = []
CGCharts.shared.graphSetup = .month
CGCharts.shared.getData(coin: self.dataObject, defaultCurrency: self.defaultCurrency, arr: true, completion: { (success) in
self.prepareGraph(arr: true, completion: { (success) in
DispatchQueue.main.async {
self.chartView.animate(xAxisDuration: 4.0)
}
})
})
}
func lineChartUpdate(dataPoints: [String], values: [Double]) {
if CGCharts.shared.graphSetup == .day {
graphSetup = .day
} else if CGCharts.shared.graphSetup == .week {
graphSetup = .fortnight
} else if CGCharts.shared.graphSetup == .month {
graphSetup = .month
}
//Graph State buttons switch status for highlighting buttons.
switch graphSetup {
case .day:
todayButton.alpha = 0.5
fortnightButton.alpha = 1.0
monthButton.alpha = 1.0
case .fortnight:
fortnightButton.alpha = 0.5
todayButton.alpha = 1.0
monthButton.alpha = 1.0
case .month:
monthButton.alpha = 0.5
todayButton.alpha = 1.0
fortnightButton.alpha = 1.0
}
//Graph data management
var lineChartEntry = [ChartDataEntry]()
for i in 0..<prices.count {
//Graph marker from extension
if prices != [] {
let value = ChartDataEntry(x: Double(i), y: values[i])
lineChartEntry.append(value)
let line1 = LineChartDataSet(values: lineChartEntry, label: "Price")
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
dateFormatter.locale = Locale(identifier: "en_US")
let dateObjects = self.days.compactMap { dateFormatter.date(from: [=10=]) }
let dateStrings = dateObjects.compactMap { dateFormatter.string(from: [=10=]) }
self.chartView.xAxis.valueFormatter = DefaultAxisValueFormatter(block: {(index, _) in
return dateStrings[Int(index)]
})
line1.setColor(.white)
line1.drawVerticalHighlightIndicatorEnabled = true
line1.drawHorizontalHighlightIndicatorEnabled = true
line1.mode = .cubicBezier
line1.lineWidth = 2.0
line1.drawValuesEnabled = true
line1.valueTextColor = UIColor.white
line1.drawCirclesEnabled = false
chartView.xAxis.valueFormatter = IndexAxisValueFormatter(values:dateStrings)
chartView.xAxis.granularity = 1
chartView.leftAxis.drawGridLinesEnabled = false
chartView.xAxis.drawGridLinesEnabled = false
//Expanded
chartView.rightAxis.enabled = false
chartView.leftAxis.enabled = false
chartView.xAxis.enabled = false
chartView.rightAxis.drawGridLinesEnabled = false
chartView.legend.enabled = false
chartView.dragEnabled = false
chartView.pinchZoomEnabled = false
chartView.drawMarkers = false
chartView.doubleTapToZoomEnabled = false
chartView.isUserInteractionEnabled = true
//Graph Data.
let data = LineChartData()
data.addDataSet(line1)
chartView.data = data
}
}
}
//Dismiss Keyboard when Tap
override func touchesBegan(_ touches: Set<UITouch>,
with event: UIEvent?) {
self.view.endEditing(true)
}
//GraphData
func prepareGraph(arr: Bool, completion: @escaping (Bool) -> ()) {
if Storage.fileExists("\(dataObject)GraphData", in: Storage.Directory.caches) {
print("Exists")
self.priceData = Storage.retrieve("\(dataObject)GraphData", from: Storage.Directory.caches, as: [Price].self)
self.days = self.priceData.map({ [=10=].date.description })
self.prices = self.priceData.map({ [=10=].price })
DispatchQueue.main.async {
// self.chartView.animate(xAxisDuration: 4.0)
self.lineChartUpdate(dataPoints: self.days, values: self.prices)
}
} else {
self.prices = []
self.days = []
print("didn'tExist")
CGCharts.shared.graphSetup = .day
print("ChartsCleared")
CGCharts.shared.getData(coin: self.dataObject, defaultCurrency: self.defaultCurrency, arr: true) { (success) in
self.updateGraph()
DispatchQueue.main.async {
self.lineChartUpdate(dataPoints: self.days, values: self.prices)
}
}
}
}
//GraphData In Storage
func updateGraph() {
self.priceData = Storage.retrieve("\(dataObject)GraphData", from: Storage.Directory.caches, as: [Price].self)
self.days = self.priceData.map({ [=10=].date.description })
self.prices = self.priceData.map({ [=10=].price })
DispatchQueue.main.async {
// self.chartView.animate(xAxisDuration: 4.0)
self.lineChartUpdate(dataPoints: self.days, values: self.prices)
}
}
}
这是来自 API 管理器文件的实际 API 调用:
import Foundation
struct Root: Codable {
let prices: [Price]
}
struct Price: Codable {
let date: Date
let price: Double
}
class CGCharts {
var priceData = [Price]()
static let shared = CGCharts()
var currency = ""
var days = ""
enum GraphStatus {
case day, week, month
}
var graphSetup = GraphStatus.day
func getAllCharts(arr: Bool, completion: @escaping (Bool) -> ()) {
}
func getData(coin: String, defaultCurrency: String, arr: Bool, completion: @escaping (Bool) -> ()) {
switch graphSetup {
case .day:
days = "1"
case .week:
days = "14"
case .month:
days = "30"
}
let urlJSON = "https://api.coingecko.com/api/v3/coins/\(coin)/market_chart?vs_currency=\(defaultCurrency)&days=\(days)"
guard let url = URL(string: urlJSON) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let prices = try JSONDecoder().decode(Root.self, from: data).prices
print(prices.first!.date.description(with:.current)) // "Saturday, September 1, 2018 at 6:25:38 PM Brasilia Standard Time\n"
print(prices[0].price)
self.priceData = prices
Storage.store(self.priceData, to: Storage.Directory.caches, as: "\(coin)GraphData")
completion(arr)
} catch {
print(error)
}
}.resume()
}
}
extension Price {
public init(from decoder: Decoder) throws {
var unkeyedContainer = try decoder.unkeyedContainer()
let date = try unkeyedContainer.decode(UInt64.self).date
let price = try unkeyedContainer.decode(Double.self)
self.init(date: date, price: price)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(date.unixEpochTime)
try container.encode(price)
}
}
extension UInt64 {
var date: Date {
return Date(timeIntervalSince1970: TimeInterval(self)/1000)
}
}
extension Date {
var unixEpochTime: UInt64 {
return UInt64(timeIntervalSince1970*1000)
}
}
这两个文件之一中的某些内容太慢了,导致页面滚动非常烦人。它只有大约 2 秒,但它会冻结那 2 秒的分页动画,我需要找到一种使滚动平滑和响应迅速的方法,即使这意味着在加载之前的几秒钟内不显示任何图表数据。我该怎么做,到底是什么让一切都变慢了?我也怀疑它可能与以下内容有关:
self.priceData = Storage.retrieve("\(dataObject)GraphData", from: Storage.Directory.caches, as: [Price].self)
self.days = self.priceData.map({ [=12=].date.description })
self.prices = self.priceData.map({ [=12=].price })
因为这是循环遍历一个包含数据的大结构,然后将它们添加到 2 个数组中,也许这会在它发生时阻止一切?谢谢。
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let self = self else {
return
}
self.priceData = Storage.retrieve("\(dataObject)GraphData", from:Storage.Directory.caches, as: [Price].self)
self.days = self.priceData.map({ [=10=].date.description })
self.prices = self.priceData.map({ [=10=].price })
DispatchQueue.main.async { [weak self] in
// update your UI here based on the data
}
}