将结果从 Swift 数组传递到 Segue 到描述

Posted

技术标签:

【中文标题】将结果从 Swift 数组传递到 Segue 到描述【英文标题】:Passing result from Swift Array to Segue to Description 【发布时间】:2017-11-20 12:11:24 【问题描述】:

对 Swift 有点陌生,但目前为止很享受挑战。我已经编写了一些代码,但想知道如何从 Swift 中的 firebase 数组中传递选择以将控制器移动到描述中?我一直以 Chris 为基础编写代码,但其中未涵盖数组。

这是我的搜索数组和 segue 代码。阵列工作正常,但选择单元格时,会出现Segue但没有详细地提出即可。文本标签为空。

提前致谢。

这是将 Firebase 信息解析为数组的代码

import UIKit
import Firebase
class LocateTableViewController: UITableViewController, UISearchResultsUpdating 

@IBOutlet var locateTableView: UITableView!
let searchController = UISearchController(searchResultsController: nil)

var firManager:FirebaseManager?
var placesArray = [NSDictionary?]()
var filtersPlaces = [NSDictionary?]()

override func viewDidLoad() 

    super.viewDidLoad()

    searchController.searchResultsUpdater = self
    self.searchController.searchBar.placeholder = "Search bars, brewpubs, shops..." 
    searchController.dimsBackgroundDuringPresentation = false
    definesPresentationContext = true
    tableView.tableHeaderView = searchController.searchBar
    tableView.tableHeaderView = searchController.searchBar


    let databaseRef = Database.database().reference()
    // search based on name of place
    databaseRef.child("places").queryOrdered(byChild: "name").observe(.childAdded, with:  (snapshot) in

        self.placesArray.append(snapshot.value as? NSDictionary)
        // insert rows
        self.locateTableView.insertRows(at: [IndexPath(row:self.placesArray.count-1,section:0)], with: UITableViewRowAnimation.automatic)


    )  (error) in
        print(error.localizedDescription)
    
    // Uncomment the following line to preserve selection between presentations
    // self.clearsSelectionOnViewWillAppear = false

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem


override func didReceiveMemoryWarning() 
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.


// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int 
    // #warning Incomplete implementation, return the number of sections
    return 1



override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    // #warning Incomplete implementation, return the number of rows

    if searchController.isActive && searchController.searchBar.text != ""
        return filtersPlaces.count
    
    return self.placesArray.count



override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)


    // Configure the cell...
    let place : NSDictionary?
    if searchController.isActive && searchController.searchBar.text != ""
        place = filtersPlaces[indexPath.row]
    
    else 
        place = self.placesArray[indexPath.row]
    

    cell.textLabel?.text = place?["type"] as? String
    cell.detailTextLabel?.text = place?["name"] as? String

    return cell


// MARK: - Navigation

override func prepare(for segue: UIStoryboardSegue, sender: Any?) 

    // Check if there's a selection first
    if let actualIndexPath = tableView.indexPathForSelectedRow 

        if segue.identifier == "detailSegue" 

            // Get the selected place
            let selectedPlace = getPlaceForIndex(indexPath: actualIndexPath)

            // Create the detail model and set the place
            let detailModel = DetailModel()
            detailModel.place = selectedPlace

            // Set the detail model for the Detail View Controller
            let detailVC = segue.destination as! DetailViewController
            detailVC.model = detailModel

        
    


func updateSearchResults(for searchController: UISearchController) 

    filterContent(searchText: self.searchController.searchBar.text!)
    // Update search results


func filterContent(searchText: String)

    self.filtersPlaces = self.placesArray.filter  place in
        let placename = place!["name"] as? String
        return(placename?.lowercased().contains(searchText.lowercased()))!
    
    tableView.reloadData()


func getPlaceForIndex(indexPath:IndexPath) -> Place 
    return places[indexPath.row]
    

这是信息应该进入的详细视图控制器的代码:

import UIKit

class DetailViewController: UIViewController, DetailModelDelegate, WriteReviewChildViewControllerDelegate 

// MARK: - Properties

var place:Place?
var model:DetailModel?
var currentViewController:UIViewController?

@IBOutlet weak var placeImageView: UIImageView!

@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var categoryLabel: UILabel!

@IBOutlet weak var faveButton: UIButton!

@IBOutlet weak var descriptionSectionButton: UIButton!
@IBOutlet weak var reviewsSectionButton: UIButton!
@IBOutlet weak var writeReviewSectionButton: UIButton!

@IBOutlet weak var containerView: UIView!


@IBOutlet weak var viewTopConstraint: NSLayoutConstraint!

@IBOutlet weak var viewBottomConstraint: NSLayoutConstraint!

// MARK: - View Controller Lifecycle

override func viewDidLoad() 
    super.viewDidLoad()

    // Register to listen for keyboard notifications
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardIsShowing(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardIsHiding(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

    // Check if there's a model
    // If so, then fetch the meta data for that place
    if let actualModel = model 

        // Set delegate to self
        actualModel.delegate = self

        // Call the get meta function
        actualModel.getMeta()

    


override func viewWillDisappear(_ animated: Bool) 

    if let actualModel = model 
        actualModel.closeObserver()
    


override func didReceiveMemoryWarning() 
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.


// MARK: - Display

func displayMetaData() 

    // Check if there's a place object in the property
    if place == nil 
        return
    

    // Set image
    model?.getImage(imageName: place!.detailImageName)

    // Set name label
    nameLabel.text = place!.name

    // Set the category label
    categoryLabel.text = place!.type

    // Set description
    if type(of: currentViewController!) == DescriptionChildViewController.self 
        (currentViewController as? DescriptionChildViewController)?.setDescriptionLabel(text: place!.desc)
    


    else if type(of: currentViewController!) == ReviewsChildViewController.self 

        // In case the Reviews Child View Controller is already displayed,
        // We call this function to set the new data and refresh the table
        let reviewsVC = currentViewController as! ReviewsChildViewController
        reviewsVC.reloadReviewsData(newReviewData: place!.reviews)
    

    // See if this place is faved
    setFaveButtonIcon()





// MARK: - Navigation

override func prepare(for segue: UIStoryboardSegue, sender: Any?) 

    // Set the currentViewController property to the Description Child View Controller
    // because that's the first one that is shown in the container view
    currentViewController = segue.destination


@IBAction func sectionButtonTapped(_ sender: UIButton) 

    // Declare a variable to hold the destination VC
    var toVC:UIViewController?

    // Set section button colors to white
    descriptionSectionButton.setTitleColor(UIColor.white, for: .normal)
    reviewsSectionButton.setTitleColor(UIColor.white, for: .normal)
    writeReviewSectionButton.setTitleColor(UIColor.white, for: .normal)

    // Set active button color
    sender.setTitleColor(UIColor.blue, for: .normal)

    // See which tag triggered this
    switch sender.tag 
    case 1:
        // Description button tapped

        // Create an instance of the Description Child View Controller
        toVC = storyboard?.instantiateViewController(withIdentifier: "DescriptionChildVC")

        if let descVC = toVC, let actualPlace = place 
            (descVC as! DescriptionChildViewController).descText = actualPlace.desc
        

    case 2:
        // Reviews button tapped

        // Create an instance of the Description Child View Controller
        toVC = storyboard?.instantiateViewController(withIdentifier: "ReviewsChildVC")

        if let reviewsVC = toVC, let actualPlace = place 
            (reviewsVC as! ReviewsChildViewController).reviews = actualPlace.reviews
        

    case 3:
        // Write a review button tapped

        // Create an instance of the Description Child View Controller
        toVC = storyboard?.instantiateViewController(withIdentifier: "WriteReviewChildVC")

        if let writeReviewsVC = toVC 
            (writeReviewsVC as! WriteReviewChildViewController).delegate = self
        

    default:
        // Description button tapped

        // Create an instance of the Description Child View Controller
        toVC = storyboard?.instantiateViewController(withIdentifier: "DescriptionChildVC")
    

    // Call SwitchChildViewControllers if currentViewController and toVC are not nil
    if let destinationVC = toVC, let currentVC = currentViewController 
        switchChildViewControllers(fromVC: currentVC, toVC: destinationVC)
    



func switchChildViewControllers(fromVC:UIViewController, toVC:UIViewController) 

    // Tell the current VC that its transitioning
    fromVC.willMove(toParentViewController: nil)

    // Add the new VC
    self.addChildViewController(toVC)
    containerView.addSubview(toVC.view)

    // Size the frame of the toVC
    toVC.view.frame = containerView.bounds

    // Set the new VC alpha to 0
    toVC.view.alpha = 0

    // Animate the new VC alpha to 1 and the old VC alpha to 0
    UIView.animate(withDuration: 0.5, animations: 

        toVC.view.alpha = 1
        fromVC.view.alpha = 0

    )  (Bool) in

        // Remove the old VC
        fromVC.view.removeFromSuperview()
        fromVC.removeFromParentViewController()

        // Tell the new VC that it has transitioned
        toVC.didMove(toParentViewController: self)

        // Set the currentViewController property to toVC
        self.currentViewController = toVC

    




// MARK: - Button Action Functions

@IBAction func backButtonTapped(_ sender: UIButton) 

    // Navigate back to the list
    presentingViewController?.dismiss(animated: true, completion: nil)


@IBAction func shareButtonTapped(_ sender: UIButton) 

    if place == nil 
        return
    

    // Data items array
    var activityItems = [Any]()

    // Get the image
    let imageData = CacheManager.getImageFromCache(imageName: place!.detailImageName)

    if let actualImageData = imageData 

        // Since we have the image data, create a UIImage
        let image = UIImage(data: actualImageData)

        if image != nil 
            activityItems.append(image!)
        
    

    // Add the description to the activity items
    activityItems.append(place!.desc)

    // Create a UIActivityViewController
    let activityVC = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)

    // Present it
    present(activityVC, animated: true, completion: nil)



@IBAction func routeButtonTapped(_ sender: UIButton) 

    if place == nil 
        return
    

    // Create a url
    let modifiedAddress = place!.addr.replacingOccurrences(of: " ", with: "+")
    let url = URL(string: "http://maps.apple.com/?address=" + modifiedAddress)

    if let actualURL = url 


    // Open url
    UIApplication.shared.open(actualURL, options: [:], completionHandler: nil)
    



@IBAction func favButtonTapped(_ sender: UIButton) 

    if place != nil 
        CacheManager.toggleFave(placeId: place!.id)
        setFaveButtonIcon()
    



// MARK: - Helper Functions

func setFaveButtonIcon() 

    if CacheManager.isFave(placeId: place!.id) 
        faveButton.setImage(#imageLiteral(resourceName: "Bookmark Ribbon Filled"), for: .normal)
    
    else 
        faveButton.setImage(#imageLiteral(resourceName: "Bookmark Ribbon"), for: .normal)
    



// MARK: - DetailModelDelegate methods

func detailModel(metaDataFor place: Place) 

    // Set the place property
    self.place = place

    // Display the meta data in the views
    displayMetaData()



func detailModel(imageName: String, imageData: Data) 

    // Detail model has returned with image data
    // Now can set image view

    let image = UIImage(data: imageData)

    if let actualImage = image 
        placeImageView.image = actualImage
    


func detailModelSaveReviewError() 

    // Show the error alert

    let alertController = UIAlertController(title: "Error", message: "There was an error saving your review", preferredStyle: .alert)

    let okAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
    alertController.addAction(okAction)

    present(alertController, animated: false, completion: nil)


func detailModelSaveReviewSuccess() 

    // Show the reviews child vc
    sectionButtonTapped(reviewsSectionButton)


// MARK: - WriteReviewChildViewControllerDelegate Methods

func saveReview(name: String, text: String) 

    // Child view controller is calling save review
    // Pass that data to the detail model
    if let actualModel = model 

        // Call saveReview of the detailmodel
        actualModel.saveReview(name, text)
    


@objc func keyboardIsShowing(notification:NSNotification) 

    let userInfo = notification.userInfo

    if let userInfoDictionary = userInfo 

        let keyboardRect = userInfoDictionary["UIKeyboardFrameEndUserInfoKey"] as! CGRect

        // Animate the constraints
        view.layoutIfNeeded()

        UIView.animate(withDuration: 0.25) 

            self.viewTopConstraint.constant = keyboardRect.height * -1
            self.viewBottomConstraint.constant = keyboardRect.height

            self.view.layoutIfNeeded()
        

    


@objc func keyboardIsHiding(notification:NSNotification) 

    // Animate the constraints
    view.layoutIfNeeded()

    UIView.animate(withDuration: 0.25) 

        self.viewTopConstraint.constant = 0
        self.viewBottomConstraint.constant = 0

        self.view.layoutIfNeeded()
    



override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 
    // Call the "hide keyboard" function of the write reviews child view controller
    if type(of: currentViewController!) == WriteReviewChildViewController.self 
        (currentViewController as! WriteReviewChildViewController).hideKeyboard()
    

【问题讨论】:

【参考方案1】:

过去是什么让我头疼。

    确保插座已正确连接。我不得不删除并重新连接。 确保单元格样式设置为详细信息。 确保单元格标识符设置为“单元格”。

有时我不得不删除单元格并重新创建以解决问题。

希望这会有所帮助。

【讨论】:

谢谢 - segue 仍在工作,但现在返回“线程 1:致命错误:索引超出范围”

以上是关于将结果从 Swift 数组传递到 Segue 到描述的主要内容,如果未能解决你的问题,请参考以下文章

Swift - Segue 没有被传递正确的数据

将数据从一个视图控制器传递到下一个视图控制器,而无需在 swift 中进行 segue

快速通过segue传递数组

使用 Segue 在 Swift 中将带有数据的对象从一个视图传递到另一个视图

如何在 Swift 中通过 Segue 将数据传递到下一个视图?

如何在没有 segue 的情况下在 Swift 3 中将数据从 VC 传递到另一个?