【问题标题】:Selecting data to be loaded into table view Swift选择要加载到表视图 Swift 中的数据
【发布时间】:2016-05-28 05:32:15
【问题描述】:

我有一个表格视图控制器,单元格上方是一个分段控件。分段控件有 3 个选项。过去的帖子、当前的帖子和未来的帖子。我试图弄清楚如何根据分段控件上选择的索引将特定数据加载到表视图中。

例如,如果选择了 Past Posts,我想将 Past Post 数据从 Parse Server 加载到表视图中。 Or of Future Posts 被选中,将来自 Parse Server 的 Future Posts 日期加载到表视图中。

我完全不确定如何加载“选定”数据,然后在索引更改时删除并加载不同的数据。非常感谢任何帮助!

另外,我知道如何从 Parse Server 获取数据。我提到这一点只是为了解释我的数据来自哪里。

【问题讨论】:

  • 如果选择了过去的帖子,则为整个 tableView 或仅选定的单元格加载过去的帖子??
  • 假设有 5 个“过去的帖子”保存到 Parse Server。如果选择“过去的帖子”,那么它将加载 5 个包含过去帖子数据的单元格。这有意义吗?

标签: ios swift uitableview uisegmentedcontrol


【解决方案1】:

我会创建一个控制器来执行获取、解析并返回一个带有相关标识符的闭包(如果它发生变化),您仍然可以使用这种方法。类似的东西。

更新

Rob's answer 的帮助下,为了完整起见,我想在我的答案中加入一些背景信息。

typealias PostsCompletionClosure = (requestIdentifier : String, posts : [Post])->Void

class PostController {

    func fetchPastPosts(requestIdentifier : String,
                        completion : PostsCompletionClosure,
                        queue : dispatch_queue_t?) {

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {

            let queryParams = ["status" : "past"]

            self.performQuery(queryParams, completion: { (requestID, posts) in
                dispatch_async(queue != nil ? queue : dispatch_get_main_queue()) {
                   completion(requestIdentifier : requestIdentifier, posts : posts)
                }
            })
        }
    }

    func fetchCurrentPosts(requestIdentifier : String, 
                           completion : PostsCompletionClosure, 
                           queue : dispatch_queue_t?) {
        // Same as Above
    }

    func fetchFuturePosts(requestIdentifier : String, 
                          completion : PostsCompletionClosure, 
                          queue : dispatch_queue_t?) { {
        // Same as Above
    }

    private func performQuery(queryParams: [String : String], 
                              completion : PostsCompletionClosure) {

         let query = PFQuery(className: "Posts")

         for {key, value) in queryParams {
             query.whereKey(key, equalTo: value)
         }

         query.findObjectsInBackgroundWithBlock { objects, error in
            guard let error == nil else {
                // Handle Error
                return
            }

             if let results = objects as? [Post] {
                dispatch_get_main_queue()) {
                   completion(requestIdentifier : requestIdentifier, posts : posts)
             }
          })
    }
}

您甚至可以为分段请求创建一个发布请求队列,如果您要开始一个新的请求,则取消所有先前的操作,这样一开始就没有机会重新加载您的数据。


这是关于如何实现 viewController 的一种可能方法:)

class ViewController: UIViewController, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet var segnmentControl: UISegmentedControl!

    var posts: [Post]?
    var activeRequestId: String = ""

    // This should prolly be injected or a singleton
    let postsController = PostController()

    override func viewDidLoad() {
        super.viewDidLoad()
        didSelectSelegment(segnmentControl)
    }

    @IBAction func didSelectSelegment(sender: UISegmentedControl) {
        posts = nil
        tableView.reloadData()

        activeRequestId = "\(sender.selectedSegmentIndex)"

        switch sender.selectedSegmentIndex {
        case 0:
            self.postsController.fetchPastPosts(activeRequestId, completion: { (requestIdentifier, posts) in
                 self.reloadDataWith(requestIdentifier, posts : [Post])
            })
        case 1:
            self.postsController.fetchCurrentPosts(activeRequestId, completion: { (requestIdentifier, posts) in
                 self.reloadDataWith(requestIdentifier, posts : [Post])
            })
        case 2:
            self.postsController.fetchFuturePosts(activeRequestId, completion: { (requestIdentifier, posts) in
                 self.reloadDataWith(requestIdentifier, posts : [Post])
            })
        default:
            fatalError("unexpected segment index")
        }
    }

    func reloadDataWith(requestIdentifier : String,
                        posts : [Post]) {

        if self.requestIdentifier == requestIdentifier {
            self.posts = posts
            self.tableView.reloadData()
        }
     }                         
}

【讨论】:

  • 我喜欢将视图控制器从复杂的查询逻辑中分离出来的想法。不过,您可能希望将posts 参数设为可选参数(并传递NSError?),以便视图控制器可以成功识别解析失败。
  • @Rob 合并了您的内容,并为几乎任何人设置了一个好的模板:)
  • @Anton 我真的很喜欢你的想法。我正在努力实现这一点,如有任何问题,我都会回复您。
  • @m1234 我很高兴听到,我非常支持将事物分解成各自的部分。您甚至可以通过添加队列并取消先前的操作来更进一步。甚至更进一步,将控制器中定义的方法抽象为协议。然后,您可以使用 Swinject 之类的东西来切换控制器,而不会影响 UI 代码
  • @Anton 添加队列绝对是我会做的事情。现在我唯一的问题是我在typealias PostsCompletionClosure = (requestIdentifier : String, posts : [Post])->Void 行收到错误Use of unresolved type 'Post'
【解决方案2】:

基本思想是,随着分段控件的更改,您将启动一个 PFQuery 来填充您的模型,然后触发重新加载表。例如,类似:

class ViewController: UIViewController, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    var posts: [Post]?

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func didChangeValueForSegmentedControl(sender: UISegmentedControl) {
        // first empty the table

        posts = nil
        tableView.reloadData()

        // prepare new query

        let query = PFQuery(className: "Posts")

        switch sender.selectedSegmentIndex {
        case 0:
            query.whereKey("status", equalTo: "past")
        case 1:
            query.whereKey("status", equalTo: "current")
        case 2:
            query.whereKey("status", equalTo: "future")
        default:
            fatalError("unexpected segment index")
        }

        // now perform query

        query.findObjectsInBackgroundWithBlock { objects, error in
            guard error == nil else {
                // report error
                return
            }

            guard let searchResults = objects as? [Post] else {
                // handle situation where results were not an array of `Post` objects
                return
            }

            self.posts = searchResults
            self.tableView.reloadData()
        }
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return posts?.count ?? 0
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! PostCell

        let post = posts![indexPath.row]

        // configure `cell` using `post`

        return cell
    }
}

现在,那些whereKey 子句肯定是不正确的,这会根据您的对象模型的设置方式而改变,但这说明了基本思想。在选择分段控制的基础上启动PFQuery,然后相应地更新结果。

现在,这一切都做了很多假设(您已经定义了表格视图并将视图控制器指定为其数据源;您已经连接了表格视图的插座;您已经连接了IBAction 代表分段控件上的valueChanged;您已经定义了具有自定义单元格类型的单元格原型;等等),但它说明了解决方案的关键部分。

【讨论】:

  • 我很乐意将示例代码合并到我的解决方案中,并感谢您,这是将线程部分添加到我的解决方案中的一项重要内容
  • @AntonDoudarev - 我当然不介意,只要它被正确归因。或者,也许更简单,您可以只说“根据查询的复杂性,我可能会将查询逻辑保留在一个单独的控制器中,如下所示,但像 Rob 在他的回答中所做的那样刷新表视图。”什么都适合你。谢谢你的提问。
  • 当然 :) 我真的很感激!我还没有使用足够的 Parse 来准备一个示例
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-08
  • 1970-01-01
  • 2017-01-05
  • 2017-01-06
相关资源
最近更新 更多