我正在尝试构建一个应用程序,使用有关加密货币的数据对 JSON 进行解码。获取数据后,我计算每种代币的每日周转率,并根据我将代币放入四分之一列表中的百分比。
如果我更改 viewModel 中的 selectedList 属性并运行应用程序,tableView 将加载适当的列表。但是当我在运行时更改 selectedList 的值时,什么也没有发生。 selectedList -> didSet 中的 print 语句的输出发生了变化,但重新加载 tableview 的视图中 didLoadCoins 方法内的 print 语句的输出与应用程序启动时保持不变。
我做错了什么?
视图控制器
class TVMainViewController: UIViewController, TVMainViewControllerViewButtonDelegate {
private let mainView = TVMainViewControllerView()
private let viewModel = TVMainViewControllerViewModel()
//MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.isNavigationBarHidden = true
mainView.delegate = self
viewModel.delegate = mainView
setupUI()
}
func didTapSelectRangeButton() {
let destinationVC = TVSelectRangeViewController()
destinationVC.modalPresentationStyle = .pageSheet
destinationVC.sheetPresentationController?.detents = [.medium()]
destinationVC.sheetPresentationController?.prefersGrabberVisible = true
destinationVC.viewModel = viewModel
present(destinationVC, animated: true)
}
//MARK: - UI Setup
private func setupUI() {
...
}
查看
protocol TVMainViewControllerViewButtonDelegate {
func didTapSelectRangeButton()
}
final class TVMainViewControllerView: UIView, TVMainViewControllerViewModelDelegate {
//MARK: - Variables
private var viewModel = TVMainViewControllerViewModel()
var delegate: TVMainViewControllerViewButtonDelegate?
//MARK: - Initializers
override init(frame: CGRect) {
super.init(frame: frame)
viewModel.delegate = self
setupTableView()
setupUI()
Task {
await viewModel.decodeData {
self.viewModel.delegate?.didLoadCoins()
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//MARK: - Functions
private func setupTableView() {
tableView.register(VTMainTableViewCell.self, forCellReuseIdentifier: Constants.mainTableViewCell)
tableView.dataSource = viewModel
}
func didLoadCoins() {
print(viewModel.selectedList)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
@objc func selectRange() {
delegate?.didTapSelectRangeButton()
}
//MARK: - UI Setup
private func setupUI() {
...
}
}
视图模型
import UIKit
/// Communicate with the View to notify it that the data are loaded
protocol TVMainViewControllerViewModelDelegate {
func didLoadCoins()
}
class TVMainViewControllerViewModel: NSObject {
//MARK: - Variables
private let networkManager = NetworkManager()
// private let baseURL = "https://api.coinlore.net/api/tickers/?start=0&limit=10"
private var coinList = [Cryptocurrency]()
private var liquidityAbove30List = [CryptoLiquidityModel]()
private var liquidityAbove20List = [CryptoLiquidityModel]()
private var liquidityAbove10List = [CryptoLiquidityModel]()
private var liquidityBelow10List = [CryptoLiquidityModel]()
private var coinLiquidityList = [CryptoLiquidityModel]()
var delegate: TVMainViewControllerViewModelDelegate?
var selectedList: Int = 0 {
didSet {
print(selectedList)
delegate?.didLoadCoins()
}
}
//MARK: - Functions
/// Run networking task and fetch data. It makes 5 network call each of which fetches 100 coins
func decodeData(completion: @escaping () -> Void) async {
coinList = []
var page = 0
while page < 200 {
let baseURL = "https://api.coinlore.net/api/tickers/?start=\(page)&limit=100"
networkManager.execute(url: baseURL) { result in
switch result {
case.success(let decodedData):
for coin in decodedData.data {
self.coinList.append(coin)
self.calculateDailyTurnoverRatio(coin: coin)
self.delegate?.didLoadCoins()
}
case .failure(let error):
print(error)
}
}
page += 100
} //endwhile
completion()
}
/// Calculate liquidity by dividing daily volume/market cap
/// Creates 4 lists. Each list contains the coins that have the given turnover ration
func calculateDailyTurnoverRatio(coin: Cryptocurrency) {
coinLiquidityList = []
let coinLiquidity24 = (coin.volume24 / Double(coin.market_cap_usd)!)*100
let newCoin = CryptoLiquidityModel(cryptocurrency: coin, liquidity24: coinLiquidity24)
switch coinLiquidity24 {
case 30 ... 100:
liquidityAbove30List.append(newCoin)
case 20 ... 29:
liquidityAbove20List.append(newCoin)
case 10 ... 19:
liquidityAbove10List.append(newCoin)
default:
liquidityBelow10List.append(newCoin)
}
}
}
extension TVMainViewControllerViewModel: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch selectedList {
case 0:
return liquidityBelow10List.count
case 1:
return liquidityAbove10List.count
case 2:
return liquidityAbove20List.count
case 3:
return liquidityAbove30List.count
default:
return liquidityBelow10List.count } }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.mainTableViewCell, for: indexPath) as! VTMainTableViewCell
switch selectedList {
case 0:
let coin = liquidityBelow10List[indexPath.row]
cell.set(coinSymbol: coin.cryptocurrency.name,
coinPrice: coin.cryptocurrency.price_usd,
change24: coin.cryptocurrency.percent_change_24h,
liquidity24: String(format: "%.2f", coin.liquidity24))
case 1:
let coin = liquidityAbove10List[indexPath.row]
cell.set(coinSymbol: coin.cryptocurrency.name,
coinPrice: coin.cryptocurrency.price_usd,
change24: coin.cryptocurrency.percent_change_24h,
liquidity24: String(format: "%.2f", coin.liquidity24))
case 2:
let coin = liquidityAbove20List[indexPath.row]
cell.set(coinSymbol: coin.cryptocurrency.name,
coinPrice: coin.cryptocurrency.price_usd,
change24: coin.cryptocurrency.percent_change_24h,
liquidity24: String(format: "%.2f", coin.liquidity24))
case 3:
let coin = liquidityAbove30List[indexPath.row]
cell.set(coinSymbol: coin.cryptocurrency.name,
coinPrice: coin.cryptocurrency.price_usd,
change24: coin.cryptocurrency.percent_change_24h,
liquidity24: String(format: "%.2f", coin.liquidity24))
default:
let coin = liquidityBelow10List[indexPath.row]
cell.set(coinSymbol: coin.cryptocurrency.name,
coinPrice: coin.cryptocurrency.price_usd,
change24: coin.cryptocurrency.percent_change_24h,
liquidity24: String(format: "%.2f", coin.liquidity24))
}
return cell
}
}
您正在使用两个不同的
TVMainViewControllerViewModel
实例。如果不是有意的,我想这可能是导致此问题的原因之一。您可能认为视图和视图控制器正在与同一个视图模型实例进行通信,但事实并非如此(因为有两个)。
final class TVMainViewControllerView: UIView, TVMainViewControllerViewModelDelegate {
//MARK: - Variables
private let viewModel: TVMainViewControllerViewModel
var delegate: TVMainViewControllerViewButtonDelegate?
//MARK: - Initializers
init(vm: TVMainViewControllerViewModel) {
super.init(frame: .zero)
self.viewModel = vm //Allocate view model from initializer.
self.viewModel.delegate = self
setupTableView()
setupUI()
Task {
await viewModel.decodeData {
self.viewModel.delegate?.didLoadCoins()
}
}
}
class TVMainViewController: UIViewController, TVMainViewControllerViewButtonDelegate {
private let viewModel = TVMainViewControllerViewModel()
private lazy var mainView = TVMainViewControllerView(vm: self.viewModel)
//MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.isNavigationBarHidden = true
mainView.delegate = self
// viewModel.delegate = mainView // This one is not needed.
setupUI()
}
我只是简单修改了您的代码,以向您展示我仅使用一个
TVMainViewControllerViewModel
实例的含义。请看一下什么是依赖注入,希望这能给您一些线索。