【问题标题】:Swift: How to handle wait time for parse queriesSwift:如何处理解析查询的等待时间
【发布时间】:2016-09-18 07:25:12
【问题描述】:

我正在制作一个类似 Tinder 的应用程序,在该应用程序中我通过解析查询数组并将数据显示在一副纸牌中。我有一个查询数组的方法,在同样的方法中我使用返回的列表。但是我收到一个错误,因为在数据下载完成之前调用了后卡。 “致命错误:在展开可选值时意外发现 nil”

我该如何处理,以便代码等待查询被检索?

谢谢

import UIKit
import MDCSwipeToChoose

class ChoosePersonViewController: UIViewController, MDCSwipeToChooseDelegate {

var people:[Person] = []
let ChoosePersonButtonHorizontalPadding:CGFloat = 80.0
let ChoosePersonButtonVerticalPadding:CGFloat = 20.0
var currentPerson:Person!
var frontCardView:ChoosePersonView!
var backCardView:ChoosePersonView!
let nameData = ""

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    let userObjectID = PFUser.currentUser()!.objectId!
    let query = PFQuery(className:"Restaurants")
    query.whereKey("Pointer", equalTo: userObjectID)
    query.findObjectsInBackgroundWithBlock {
        (objects: [PFObject]?, error: NSError?) -> Void in
        if error == nil {
            // The find succeeded.
            print("Successfully retrieved \(objects!.count) scores.")
            // Do something with the found objects
            if let objects = objects {
                for object in objects {
                    var nameData = object.objectForKey("Name") as? String
                    var imageData = object.objectForKey("Image") as? PFFile
                    imageData!.getDataInBackgroundWithBlock { (imageData, error) -> Void in
                        if error == nil {
                            if let imageData = imageData
                            {
                                let image = UIImage(data: imageData)
                                self.people.append(Person(name: nameData, image: image, age: 21, sharedFriends: 3, sharedInterest: 4, photos: 5))
                                print(self.people)
                            }
                        }
                    }
                }
            }
        } else {
            // Log details of the failure
            print("Error: \(error!) \(error!.userInfo)")
        }
    }
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    // Here you can init your properties
    let imageData: PFFile

}

override func viewDidLoad(){
    super.viewDidLoad()
   // Display the first ChoosePersonView in front. Users can swipe to indicate
    // whether they like or dislike the person displayed.
    self.setMyFrontCardView(self.popPersonViewWithFrame(frontCardViewFrame())!)
    self.view.addSubview(self.frontCardView)

    // Display the second ChoosePersonView in back. This view controller uses
    // the MDCSwipeToChooseDelegate protocol methods to update the front and
    // back views after each user swipe.
    self.backCardView = self.popPersonViewWithFrame(backCardViewFrame())!
    self.view.insertSubview(self.backCardView, belowSubview: self.frontCardView)

    // Add buttons to programmatically swipe the view left or right.
    // See the `nopeFrontCardView` and `likeFrontCardView` methods.
    constructNopeButton()
    constructLikedButton()
}

func suportedInterfaceOrientations() -> UIInterfaceOrientationMask{
    return UIInterfaceOrientationMask.Portrait
}

// This is called when a user didn't fully swipe left or right.
func viewDidCancelSwipe(view: UIView) -> Void{

    print("You couldn't decide on \(self.currentPerson.Name)");
}

// This is called then a user swipes the view fully left or right.
func view(view: UIView, wasChosenWithDirection: MDCSwipeDirection) -> Void{

    // MDCSwipeToChooseView shows "NOPE" on swipes to the left,
    // and "LIKED" on swipes to the right.
    if(wasChosenWithDirection == MDCSwipeDirection.Left){
        print("You noped: \(self.currentPerson.Name)")
    }
    else{

        print("You liked: \(self.currentPerson.Name)")
    }

    // MDCSwipeToChooseView removes the view from the view hierarchy
    // after it is swiped (this behavior can be customized via the
    // MDCSwipeOptions class). Since the front card view is gone, we
    // move the back card to the front, and create a new back card.
    if(self.backCardView != nil){
        self.setMyFrontCardView(self.backCardView)
    }

    backCardView = self.popPersonViewWithFrame(self.backCardViewFrame())
    //if(true){
    // Fade the back card into view.
    if(backCardView != nil){
        self.backCardView.alpha = 0.0
        self.view.insertSubview(self.backCardView, belowSubview: self.frontCardView)
        UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseInOut, animations: {
            self.backCardView.alpha = 1.0
            },completion:nil)
    }
}
func setMyFrontCardView(frontCardView:ChoosePersonView) -> Void{

    // Keep track of the person currently being chosen.
    // Quick and dirty, just for the purposes of this sample app.
    self.frontCardView = frontCardView
    self.currentPerson = frontCardView.person
}

func popPersonViewWithFrame(frame:CGRect) -> ChoosePersonView?{
    if(self.people.count == 0){
        return nil;
    }

    // UIView+MDCSwipeToChoose and MDCSwipeToChooseView are heavily customizable.
    // Each take an "options" argument. Here, we specify the view controller as
    // a delegate, and provide a custom callback that moves the back card view
    // based on how far the user has panned the front card view.
    let options:MDCSwipeToChooseViewOptions = MDCSwipeToChooseViewOptions()
    options.delegate = self
    //options.threshold = 160.0
    options.onPan = { state -> Void in
        if(self.backCardView != nil){
            let frame:CGRect = self.frontCardViewFrame()
            self.backCardView.frame = CGRectMake(frame.origin.x, frame.origin.y-(state.thresholdRatio * 10.0), CGRectGetWidth(frame), CGRectGetHeight(frame))
        }
    }

    // Create a personView with the top person in the people array, then pop
    // that person off the stack.

    let personView:ChoosePersonView = ChoosePersonView(frame: frame, person: self.people[0], options: options)
    self.people.removeAtIndex(0)
    return personView

}

【问题讨论】:

  • 哪一行失败了?为什么在图像加载时添加这样的视图?你不能保证图片加载的顺序...
  • 失败的行是“self.backCardView = self.popPersonViewWithFrame(self.backCardViewFrame())!”我应该如何添加视图?

标签: arrays swift parse-platform closures


【解决方案1】:

我建议在硬循环中启动所有图像下载是无效的,并且可能导致网络泛滥和多次请求超时。

考虑只存储所有返回的对象并仅请求第一张(也许是第二张)图像。

保存返回的项目后立即添加视图,并在图像可用时将其添加到视图。

看来您的问题实际上可能与尝试删除尚不存在的视图有关,尽管尚不清楚该函数的作用,它可能更像是它想要访问一个没有'尚未添加到数组中,因为下一个图像尚未下载,因此无法找到/创建合适的项目以返回...更改您使用图像和视图的方式将解决这些原因中的任何一个。

【讨论】:

  • 我很难概念化如何编写代码。你能把我推向正确的方向吗?由于对象是异步的,我不知道除了块本身之外还有什么地方可以加载视图,否则我会在“self.setMyFrontCardView(self.popPersonViewWithFrame(self.frontCardViewFrame())!)”处收到错误
  • 您在任何时候最多需要 2 个视图和 2 个图像,当前一个和下一个。从技术上讲,即使图像还没有准备好,视图也可以存在。因此,立即创建前 2 个视图。过渡动画结束时应创建其他视图(销毁不再需要的第一个视图并在屏幕外创建第二个视图以准备动画)。
  • 我已经更新了代码。我正在尝试按照您的建议在图像加载之前创建卡片视图,但它似乎依赖于人员数组的存在。我在“ self.setMyFrontCardView(self.popPersonViewWithFrame(self.frontCardViewFrame())!)‌​处收到错误消息
  • 因为你在做的时候访问了那个人
猜你喜欢
  • 1970-01-01
  • 2015-03-07
  • 1970-01-01
  • 2020-07-24
  • 2013-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-28
相关资源
最近更新 更多