使用 Swift 的带有 MVVM 的 UITableView
Posted
技术标签:
【中文标题】使用 Swift 的带有 MVVM 的 UITableView【英文标题】:UITableView with MVVM using Swift 【发布时间】:2017-12-25 15:29:48 【问题描述】:我正在使用UITableView
在 Swift 中开发 MVVM 架构。为此,我创建了示例表视图。
谁能建议我是否正确或需要做任何其他改进?
以下是此架构的类。
ViewController - 包含 UITableView
及其 delegate
和 datasource
方法。
class ViewController: UIViewController
let PRODUCT_CELL_IDENTIFIER = "ProductCellIdentifier"
@IBOutlet weak var productTableView: UITableView!
var productViewModel: ProductViewModel = ProductViewModel()
//UITableView Delegate Methods
extension ViewController
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return productViewModel.numberOfRowsInSection()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: PRODUCT_CELL_IDENTIFIER) as! ProductTableViewCell
let product = productViewModel.productsArray[indexPath.row]
cell.productName.text = product.name
cell.productQuantity.text = "\(product.quantity)"
return cell
ProductViewModel: - 这是 ViewModel 类。
class ProductViewModel: NSObject
var productsArray = Array<Product>()
override init()
let product1 = Product(name: "Prodcut1", image_url: "", quantity: 2)
let product2 = Product(name: "Prodcut2", image_url: "", quantity: 3)
productsArray.append(product1)
productsArray.append(product2)
func numberOfRowsInSection() -> Int
return productsArray.count
Product - 这是模型类
class Product: NSObject
var name: String
var image_url: String
var quantity: Int
init(name: String, image_url: String, quantity: Int)
self.name = name
self.image_url = image_url
self.quantity = quantity
ProductTableViewCell - 这是 UITableViewCell 类
class ProductTableViewCell: UITableViewCell
@IBOutlet weak var productQuantity: UILabel!
@IBOutlet weak var productName: UILabel!
@IBOutlet weak var productImageView: UIImageView!
【问题讨论】:
您正在考虑在 View/ViewController 中获取与 MVVM 概念相矛盾的模型数组,View 不应访问模型,它应该从视图模型中获取所需的一切。 【参考方案1】:您做得很好,但您甚至可以通过添加以下功能来改进您的产品模型以获取直接模型数组。当您从 Web Api 响应创建数组时,它非常有用。
class Product : NSObject
var imgUrl : String!
var name : String!
var quantity : Int!
init(dictionary: [String:Any])
imgUrl = dictionary["img_url"] as? String
name = dictionary["name"] as? String
quantity = dictionary["quantity"] as? Int
init(name: String, image_url: String, quantity: Int)
self.name = name
self.imgUrl = image_url
self.quantity = quantity
public class func modelsFromArray(array:[[String:Any]]) -> [Product]
var models:[Product] = []
for item in array
models.append(Product.init(dictionary:item))
return models
像用法一样
let product1 = Product(name: "Prodcut1", image_url: "", quantity: 2) //Normal Case
let productList:[[String:Any]] =
[
["name":"Jaydeep","img_url":"xyz","quantity":1],
["name":"Jaydeep","img_url":"xyz","quantity":2],
["name":"Jaydeep","img_url":"xyz","quantity":3],
["name":"Jaydeep","img_url":"xyz","quantity":4],
["name":"Jaydeep","img_url":"xyz","quantity":5],
["name":"Jaydeep","img_url":"xyz","quantity":6]
]
//Assign Direct Dictionary to Get Array Of Models
/* Very useful when productList is dictionary from server response*/
let productArray:[Product] = Product.modelsFromArray(array: productList)
而且你的 Cell 类也被改进了
class ProductTableViewCell: UITableViewCell
@IBOutlet weak var productQuantity: UILabel!
@IBOutlet weak var productName: UILabel!
@IBOutlet weak var productImageView: UIImageView!
func setProductData(product:Product)
self.productName.text = product.name
self.productQuantity.text = "\(product.quantity)"
用法:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: PRODUCT_CELL_IDENTIFIER) as! ProductTableViewCell
let product = productViewModel.productsArray[indexPath.row]
cell.setProductData(product:product)
return cell
【讨论】:
非常感谢。modelsFromArray
方法有点混乱。
您正在使用两个数组。一个是使用中,另一个是Product
模型类。
第一个数组是用来演示你的,它可能是服务器响应而不是你可以直接传递响应来生成模型数组。另请注意,我也为您的单元格更新了代码
非常感谢杰迪普。它真的很有帮助。我会更新我的代码。
还有一个疑问。你已经使用了implicit uwrapped
变量。例如var quantity : Int!
。如果我想从ViewController
类访问它,则值为Options(1). AS per my knowledge
Implicit Unwrapped` 对象不能强制解包。你能告诉我为什么吗?【参考方案2】:
ios 中的 MVVM 可以轻松实现,无需使用第三方依赖项。对于数据绑定,我们可以使用 Closure 和 didSet 的简单组合来避免第三方依赖。
public final class Observable<Value>
private var closure: ((Value) -> ())?
public var value: Value
didSet closure?(value)
public init(_ value: Value)
self.value = value
public func observe(_ closure: @escaping (Value) -> Void)
self.closure = closure
closure(value)
来自 ViewController 的数据绑定示例:
final class ExampleViewController: UIViewController
private func bind(to viewModel: ViewModel)
viewModel.items.observe(on: self) [weak self] items in
self?.tableViewController?.items = items
// self?.tableViewController?.items = viewModel.items.value // This would be Momory leak. You can access viewModel only with self?.viewModel
// Or in one line:
viewModel.items.observe(on: self) [weak self] in self?.tableViewController?.items = $0
override func viewDidLoad()
super.viewDidLoad()
bind(to: viewModel)
viewModel.viewDidLoad()
protocol ViewModelInput
func viewDidLoad()
protocol ViewModelOutput
var items: Observable<[ItemViewModel]> get
protocol ViewModel: ViewModelInput, ViewModelOutput
final class DefaultViewModel: ViewModel
let items: Observable<[ItemViewModel]> = Observable([])
// Implmentation details...
以后可以用 SwiftUI 和 Combine 替换(当您的应用的最低 iOS 版本为 13 时)
在这篇文章中,对 MVVM 有更详细的描述 https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3
【讨论】:
以上是关于使用 Swift 的带有 MVVM 的 UITableView的主要内容,如果未能解决你的问题,请参考以下文章
我想通过在 Swift 中以编程方式将 UINavigationController 与 UITableViewController 嵌入 UITableViewController 这是 UITab