我最近开始学习 UIKit 并尝试以编程方式而不是故事板的方式为我的简历制作另一个项目。我从 SwiftUI 开始,但有人建议我学习 UIKit 并在我的简历上有一个 UIKit 项目。老实说,我非常困惑。我正在检索数据并将其放入具有收藏夹按钮的表格视图中。我有这样的功能,当用户点击收藏夹的按钮时,空星被填充,但我希望收藏夹显示在新的视图控制器中,因为我有一个包含数据的标签栏,另一个是收藏夹的标签.我的问题是 addDigimonToFavorites() 方法,我尝试在该方法中传递参数但收到 [Digimon] 到预期参数类型“Digimon”的错误。到目前为止,这是我的代码。如果您需要任何其他代码,请告诉我。
视图控制器
class ViewController: UIViewController {
// MARK: - Properties
private let tableView: UITableView = {
let tableView = UITableView()
tableView.backgroundColor = .systemBackground
tableView.register(DigimonCell.self, forCellReuseIdentifier: DigimonCell.identifier)
return tableView
}()
private let searchController = UISearchController(searchResultsController: nil)
var viewModel = DigimonViewModel()
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
viewModel.delegate = self
viewModel.getDigimonData()
configureTableView()
setTableViewDelegates()
configNavBar()
configSearchBar()
}
// MARK: - SetupUI
func configureTableView() {
//View is the main view of the view controller
view.addSubview(tableView)
tableView.frame = view.bounds
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor), t
ableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
func setTableViewDelegates() {
tableView.delegate = self
tableView.dataSource = self
}
func configNavBar() {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .mainOrange()
appearance.titleTextAttributes = [.foregroundColor: UIColor.white]
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = navigationController?.navigationBar.standardAppearance
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.barStyle = .black //bar style gives us the white status bar/white text look
navigationController?.navigationBar.isTranslucent = false
navigationItem.title = "Digimon"
}
func configSearchBar() {
self.searchController.searchResultsUpdater = self
self.searchController.obscuresBackgroundDuringPresentation = false
self.searchController.hidesNavigationBarDuringPresentation = false
self.searchController.searchBar.placeholder = "Search Digimon"
self.searchController.searchBar.searchTextField.backgroundColor = .white
self.navigationItem.searchController = searchController
self.definesPresentationContext = false
self.navigationItem.hidesSearchBarWhenScrolling = false
}
// MARK: - Selectors
@objc func markAsFavorite(sender: UIButton) {
sender.isSelected = !sender.isSelected //Toggle the button state
print("This is my favorite Digimon")
//received an error of [Digimon]to expected argument type 'Digimon'
addDigimonToFavorites(digimon: viewModel.characters)
}
func addDigimonToFavorites(digimon: Digimon) {
//Need to append to the empty favorites
var favorites = Digimon(name: digimon.name, img: digimon.img, level: digimon.level)
PersistenceManager.updateWith(favorite: favorites, actionType: .add) { [weak self] error in
guard let self = self else { return }
}
}
}
// MARK: - Extension for TableView delegate and data source
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let inSearchMode = viewModel.inSearchMode(searchController)
return inSearchMode ? viewModel.filteredDigimon.count : viewModel.characters.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: DigimonCell.identifier, for: indexPath) as? DigimonCell else { fatalError("tableview could not dequeue digimoncell in viewcontroller")}
let inSearchMode = viewModel.inSearchMode(searchController)
let digimon = inSearchMode ? viewModel.filteredDigimon[indexPath.row] : viewModel.characters[indexPath.row]
cell.set(imageUrlString: digimon.img, label: digimon.name, level: digimon.level)
let favoriteButton = UIButton(type: .custom)
favoriteButton.setImage(UIImage(systemName: "star"), for: .normal)
favoriteButton.setImage(UIImage(systemName: "star.fill")?.withTintColor(.orange, renderingMode: .alwaysOriginal), for: .selected)
favoriteButton.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
favoriteButton.addTarget(self, action: #selector(markAsFavorite), for: .touchUpInside)
cell.accessoryView = favoriteButton
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 110
}
}
// MARK: - Extension for ViewController to conform to DigimonViewModelProtocol
extension ViewController: DigimonViewModelProtocol {
// MARK: - Digimon View Model Protocol Methods
func didFinish() {
tableView.reloadData()
}
func didFail(error: Error) {
print(error)
}
}
// MARK: - Extension for Search Controller Functions
extension ViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
self.viewModel.updateSearchController(searchBarText: searchController.searchBar.text)
tableView.reloadData()
}
}
我的最爱列表VC
class FavoritesListViewController: UIViewController {
// MARK: - Properties
var favorites: [Digimon] = []
private let tableView: UITableView = {
let tableView = UITableView()
tableView.backgroundColor = .systemBackground
tableView.register(FavoriteCell.self, forCellReuseIdentifier: FavoriteCell.identifier)
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
title = "Favorites"
navigationController?.navigationBar.prefersLargeTitles = true
configFavoritesTableView()
}
//ViewDidLoad only gets called once. But if the user sees they have no favorites and then adds favorites and goes back to favorites list will get called then. Always refreshing favorites
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getFavorites()
}
func configFavoritesTableView() {
view.addSubview(tableView)
tableView.frame = view.bounds
tableView.delegate = self
tableView.dataSource = self
tableView.removeExcessCells()
}
func getFavorites() {
PersistenceManager.retrieveFavorites { result in
switch result {
case .success(let favorites):
self.favorites = favorites
DispatchQueue.main.async {
self.tableView.reloadData()
self.view.bringSubviewToFront(self.tableView)
}
case .failure(let error):
fatalError("error")
}
}
}
}
// MARK: - Extension for the TableView
extension FavoritesListViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return favorites.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: FavoriteCell.identifier, for: indexPath) as?
FavoriteCell else {
fatalError("Could not dequeue favoritecell")
}
//Get favorites
let favorite = favorites[indexPath.row]//IndexPath.row is grabbing the index from the array for that row
cell.setFave(favorite: favorite)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 110
}
}
我的持久性管理器
enum PersistenceActionType {
case add
case remove
}
enum Keys {
static let favorites = "favorites"
}
enum PersistenceManager {
static private let defaults = UserDefaults.standard
// MARK: - Retreive Favorites
static func retrieveFavorites(completed: @escaping (Result<[Digimon], Error>) -> Void) {
//When saving to defaults. Need to give it a key name
guard let favoritesData = defaults.object(forKey: Keys.favorites) as? Data else { return
//First time use when there are no favorites added. We don't want an error to display. Just an empty array
completed(.success([]))
return
}
do {
let decoder = JSONDecoder()
let favorites = try decoder.decode([Digimon].self, from: favoritesData)
completed(.success(favorites))
} catch {
fatalError("Unable to favorite")
}
}
// MARK: - Save Favorites
static func saveFavorites(favorites: [Digimon]) -> Error? {
do {
let encoder = JSONEncoder()
let encodedFavorites = try encoder.encode(favorites)
defaults.set(encodedFavorites, forKey: Keys.favorites)
return nil //Returning nil because there is no Error
}catch {
fatalError("Unable to favorite")
}
}
static func updateWith(favorite: Digimon, actionType: PersistenceActionType, completed: @escaping (Error?) -> Void) {
//Need to reach in the user defaults and retrieve the array
retrieveFavorites { result in
switch result {
case .success(var favorites):
switch actionType {
case .add:
guard !favorites.contains(favorite) else {
completed("Already in favorites" as? Error)
return
}
favorites.append(favorite)
case .remove:
favorites.removeAll { $0.name == favorite.name && $0.img == favorite.img && $0.level == favorite.level} //Shorthand syntax $0 is each item as it iterates through
}
case .failure(let error):
completed(error)
}
}
}
}
错误消息“expected argument type 'Digimon', got '[Digimon]'”表示您正在尝试将 Digimon 对象数组传递给需要单个 Digimon 对象的函数。
在您的
markAsFavorite
函数中,当用户点击任何 Digimon 的收藏按钮时,您尝试将包含所有 Digimon 对象的 viewModel.characters
数组添加到收藏夹列表。相反,您需要将选定的 Digimon 对象添加到收藏夹列表中。
您可以通过访问包含收藏夹按钮的单元格行并使用它索引到适当的数组,
viewModel.characters
或 viewModel.filteredDigimon
,这取决于搜索栏是否处于活动状态,从而获取所选的 Digimon 对象。
@objc func markAsFavorite(sender: UIButton) {
sender.isSelected = !sender.isSelected //Toggle the button state
// Find the selected cell and get its corresponding Digimon
guard let cell = sender.superview?.superview as? DigimonCell,
let indexPath = tableView.indexPath(for: cell) else {
return
}
let inSearchMode = viewModel.inSearchMode(searchController)
let digimon = inSearchMode ? viewModel.filteredDigimon[indexPath.row] : viewModel.characters[indexPath.row]
addDigimonToFavorites(digimon: digimon)
}
在
addDigimonToFavorites
函数中,您还需要将参数类型更改为数码宝贝对象数组,因为您将向收藏夹列表添加多个数码宝贝对象。这是该功能的更新版本:
func addDigimonToFavorites(digimon: [Digimon]) {
// Append to the existing favorites array
var favorites = PersistenceManager.retrieveFavorites()
favorites.append(contentsOf: digimon)
PersistenceManager.updateWith(favorites: favorites, actionType: .add) { [weak self] error in
guard let self = self else { return }
// Handle error or update UI as needed
}
}
请注意,此函数检索现有的收藏夹列表,将选定的 Digimon 对象附加到其中,然后使用
PersistenceManager.updateWith
保存更新的列表。您可能还想更新您的 PersistenceManager
以处理一组 Digimon
对象而不是单个对象。