【问题标题】:UITableView with MVVM using Swift使用 Swift 的带有 MVVM 的 UITableView
【发布时间】:2017-12-25 15:29:48
【问题描述】:

我正在使用UITableView 在 Swift 中开发 MVVM 架构。为此,我创建了示例表视图。

谁能建议我是否正确或需要做任何其他改进?

以下是此架构的类。

  1. ViewController - 包含 UITableView 及其 delegatedatasource 方法。

    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
        }
    }
    
  2. 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
        }
    }
    
  3. 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
        }
    }
    
  4. ProductTableViewCell - 这是 UITableViewCell 类

    class ProductTableViewCell: UITableViewCell {
        @IBOutlet weak var productQuantity: UILabel!
        @IBOutlet weak var productName: UILabel!
        @IBOutlet weak var productImageView: UIImageView!
    }
    

【问题讨论】:

  • 您正在考虑在 View/ViewController 中获取与 MVVM 概念相矛盾的模型数组,View 不应访问模型,它应该从视图模型中获取所需的一切。

标签: ios swift uitableview mvvm


【解决方案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

【讨论】:

    猜你喜欢
    • 2018-10-30
    • 1970-01-01
    • 2016-07-18
    • 1970-01-01
    • 2016-01-30
    • 1970-01-01
    • 2017-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多