简体   繁体   English

在Swift中从视图控制器之间传递数据(从TableView到DetailViewController)

[英]Passing data between View Controllers in Swift (From TableView to DetailViewController)

I have two files, MyTableViewController and myViewController. 我有两个文件,MyTableViewController和myViewController。 I set UIImageView on TableCell in MyTableVIewController. 我在MyTableVIewController中的TableCell上设置了UIImageView。 myViewController does not contain anything. myViewController不包含任何内容。 And I created an array called ImageArray which contains an array of Images. 我创建了一个名为ImageArray的数组,其中包含一个Images数组。

What I am aiming to do here is when I click an image on TableCell in myTableViewController, I want the clicked image to appear in the myViewController. 我在这里要做的是当我在myTableViewController中单击TableCell上的图像时,我希望单击的图像出现在myViewController中。 And some description about the clicked image beside the image too. 还有关于图像旁边点击图像的一些描述。 I want to use myViewController for users to get detailed information of the selected image. 我想使用myViewController让用户获取所选图像的详细信息。

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell
    var ImageView = cell.viewWithTag(1) as! UIImageView
    ImageView.image = UIImage(named: ImageArray[indexPath.row])
    ImageView.contentMode = .ScaleAspectFit

    var TextView = cell.viewWithTag(2) as! UILabel
    TextView.text = ImageArray[indexPath.row]
    return cell
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    performSegueWithIdentifier("next", sender: indexPath)
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "next") {
        let destination = segue.destinationViewController as! myViewController
    }
}

I don't know what to do to make it happen. 我不知道如何做到这一点。 I really appreciate if you could help me figure out! 如果你能帮助我搞清楚,我真的很感激! Thanks! 谢谢!

First and foremost, I'm assuming your MyTableViewController class conforms to both UITableViewDataSource and UITableViewDelegate protocols and that you've set your MyTableViewController class to be the delegate in code or via Storyboard. 首先,我假设您的MyTableViewController类符合UITableViewDataSourceUITableViewDelegate协议,并且您已将MyTableViewController类设置为代码或通过Storyboard的委托。

With that sorted out,there are multiple ways to achieve the result you seek. 经过整理,有多种方法可以实现您所寻求的结果。 You can declare your ImageArray in an independent class and call it inside your MyTableViewController class, index them onto a tableView using the tableView delegate methods, and finally using the prepareForSegue method to push your images onto your myViewController. 您可以在一个独立的类中声明ImageArray并在MyTableViewController类中调用它,使用tableView委托方法将它们索引到tableView上,最后使用prepareForSegue方法将图像推送到myViewController上。 Or you can simply declare and initialize your ImageArray at the top of your MyTableViewController class like below: 或者您可以在MyTableViewController类的顶部简单地声明和初始化ImageArray,如下所示:

var ImageArray = [("Moscow Russia.jpg", "Europe"),
    ("London England.jpg", "Europe")]  

In the ImageArray above, ensure that your image name matches exactly as the asset name you've imported into your Xcode project. 在上面的ImageArray中 ,确保您的图像名称与您导入Xcode项目的资产名称完全匹配。

Then we specify how many rows in section we need ImageArray to occupy on our tableView (ie basically count our ImageArray into our TableView) with below required method: 然后我们使用下面所需的方法指定我们需要ImageArray在tableView上占用的部分行数(即基本上将我们的ImageArray计入我们的TableView):

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

Next, you want to present your ImageArray in each row of of the cell using the tableView:cellForRowAtIndexPath: method. 接下来,您希望使用tableView:cellForRowAtIndexPath:方法在单元格的每一行中显示ImageArray

Side Note on your TableCell: Hopefully your TableCell is subclassed from UITableViewCell and you have already declared and connected two IBOutlets, say, imageView and textLabel respectively. 您的TableCell侧面说明:希望您的TableCell的是从子类的UITableViewCell,并且您已经申报并分别连接的两个IBOutlets,也就是说,imageview的并且为textLabel。 Also, ensure your TableCell is properly linked to your prototype cell in Storyboard under Identity Inspector ) Your TableCell class should look something like below: 此外,确保您的TableCell正确地链接到Identity Inspector下的Storyboard中的原型单元格。您的TableCell类应如下所示:

import UIKit
class CustomTableViewCell: UITableViewCell {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var textLabel: UILabel!
}

Now back into your MyTableVIewController class. 现在回到MyTableVIewController类。 From your code, I see you're casting the line 'let cell = ...' as 'UITableViewCell. 从你的代码中,我看到你正在将'let cell = ...'作为'UITableViewCell'。 You should rather cast it as 'TableCell' instead since you're subclassing it. 您应该将其转换为“TableCell”,因为您正在对其进行子类化。 Implement the tableView:cellForRowAtIndexPath: method as follows: 实现tableView:cellForRowAtIndexPath:方法如下:

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

//Note that I'm using tuples here. Pretty cool huh. Got to love Swift!
  let (imageName, textNameToGoWithImage) = ImageArray[indexPath.row]

    cell.textLabel.text = textNameToGoWithImage
    cell.imageView.image = UIImage(named: imageName)
    cell.imageView.contentMode = .ScaleAspectFit

// You could have also used 'if let' in optional binding to safely unwrap your image if you want like below.
   //    if let image = UIImage(named: imageName){
   //        cell.imageView?.image = image
  //         cell.imageView.contentMode = .ScaleAspectFit
  //     }

    return cell
}

It looks like you're a little confused about when and where to use performSegueWithIdentifier method as opposed to using - prepareForSegue method. 看起来你对使用performSegueWithIdentifier方法的时间和地点感到有点困惑,而不是使用 - prepareForSegue方法。 And even when to use the tableView:didSelectRowAtIndexPath method . 甚至何时使用tableView:didSelectRowAtIndexPath方法

Let me briefly explain here. 我在这里简单解释一下。 You use the performSegueWithIdentifier method when you didn't control-drag a segue from one ViewController's scene to another in Storyboard. 当您没有控制时,可以使用performSegueWithIdentifier方法 - 将一个ViewController的场景中的segue拖动到Storyboard中的另一个场景。 This way, using the performSegueWithIdentifier method will allow you to move between ViewController scenes as long as you specify the right identifier which you've set in Storyboard under ' Attributes Inspector. 这样,只要您在“ 属性检查器 ”下的“故事板”中指定了正确的标识符,使用performSegueWithIdentifier方法就可以在ViewController场景之间移动 '

Now if you're using Storyboard instead, you wouldn't need the tableView:didSelectRowAtIndexPath method. 现在,如果您使用的是Storyboard,则不需要tableView:didSelectRowAtIndexPath方法。 What the tableView:didSelectRowAtIndexPath method does is that it tells the delegate that the specified row is now selected and we can do something within its code body (like push an image or a text onto another ViewController Scene like you're trying to do). tableView:didSelectRowAtIndexPath方法的作用是告诉委托现在选择了指定的行,我们可以在其代码体内执行某些操作(例如将图像或文本推送到另一个ViewController场景,就像您尝试做的那样)。 But that becomes redundant when you use segues. 但是当你使用segues时,这就变得多余了。 All you have to do, is to control-drag a segue from the table cell on your MyTableViewController scene to your myViewController scene. 您所要做的就是控制 - 将您的MyTableViewController场景中的表格单元格中segue 拖动myViewController场景。 Choose ' Show ' and give the segue an identifier name like you've done " next ". 选择“ 显示 ”并为segue指定一个标识符名称,就像你“ 下一步 ”一样。 ( A little side note: if you want the Back button functionality to display at top navigator bar when you run your app, you simply embed your MyTableViewController in a UINavigationController to give you that ' Back ' button functionality. With your MyTableViewController Scene selected in Storyboard, Go to the top menu and select Editor >> Embed In >> Navigation Controller . Then walla!!) 小边注:如果你想后退按钮的功能,在顶部导航栏,当你运行你的应用程序中显示,只需嵌入您MyTableViewController在一个UINavigationController给你,“ 后退 ”按钮功能与您的MyTableViewController场景在故事板中选择。 ,转到顶部菜单,然后选择编辑>>嵌入>>导航控制器 。然后walla !!)

Lets now go ahead and implement our tableView:prepareForSegue method like below: 现在让我们继续实现我们的tableView:prepareForSegue方法,如下所示:

 override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "next" {
        //Note that, originally, destinationViewController is of Type UIViewController and has to be casted as myViewController instead since that's the ViewController we trying to go to. 
        let destinationVC = segue.destinationViewController as! myViewController


        if let indexPath = self.tableView.indexPathForSelectedRow{

            let selectedRow = ImageArray[indexPath.row]

            destinationVC.imageName2 = selectedRow.0
            destinationVC.textName2 = selectedRow.1
}

From the above code, make sure you set the ' imageName ' and ' textName ' as properties in your myViewController class first before you can access them with ' destinationVC ' which is now of type myViewController. 从上面的代码中,确保首先在myViewController类中将' imageName '和' textName '设置为属性 ,然后才能使用' destinationVC '访问它们,而' destinationVC现在是myViewController类型。 These two properties will hold the data we are passing from MyTableViewController class to myViewController class. 这两个属性将保存我们从MyTableViewController类传递到myViewController类的数据。 And we are using the array index to pass data to these two properties accordingly. 我们正在使用数组索引相应地将数据传递给这两个属性。

You can then create two IBOutlets to display your image and text by passing these set ' imageName2 ' and ' textName2 ' properties to your outlets (or any UI control for that matter). 然后,您可以通过将这些设置' imageName2 '和' textName2 '属性传递到您的出口(或任何UI控件)来创建两个IBOutlet来显示您的图像和文本。

  • Now the reason why you will have to set properties first in myViewController class before you pass them on or around (ie to a UI element, closure, another VC etc) is that, when you hit a tableView cell from MyTableViewController scene to segue onto your next ViewController scene (ie your myViewController scene), iOS hasn't instantiated that second scene just yet. 现在为什么你会有你把它们或周围之前先设置在myViewController类属性的原因(即UI元素,关闭,另一个VC等)的是,当你从MyTableViewController场面创下的tableView细胞Segue公司到您的下一个ViewController场景(即你的myViewController场景),iOS还没有实例化第二个场景。 And so you need a property to hold onto the data you're trying to pass onto your second scene View Controller first so that you can make use of it later when that class finally loads. 因此,您需要一个属性来保留您尝试首先传递到第二个场景View Controller的数据,以便以后在该类最终加载时可以使用它。

So your myViewController class should look something like below: 所以你的myViewController类应该如下所示:

import UIKit

class myViewController : UIViewController {

   //Your two strings to initially hold onto the data 
  //being passed to myViewController class
var imageName2 : String?
var textName2 : String?

@IBOutlet weak var detailImageView: UIImageView!
@IBOutlet weak var detailTextNameLabel: UITextField!

override func viewDidLoad() {
    super.viewDidLoad()

    detailTextNameLabel.text = textName2!

    if let image = UIImage(named: imageName2!) {
        self.detailImageView.image = image
    }
}

And that's it! 就是这样!

Things to note on labelling and conventions in Swift: 关于Swift中标签和约定的注意事项:

  • Start naming classes with block letters (ie class ViewController() {}) 用块字母开始命名类(即类ViewController(){})
  • Classes and properties should capture the meaning of what they represent. 类和属性应该捕获它们所代表的含义。 I will recommend you change your MyTableViewController and ' myViewController 'classes accordingly to reflect what they truly mean or do (You can go with 'MainTableViewController' and 'DetailViewController'. That will do just fine). 我建议您相应地更改MyTableViewController和' myViewController '类以反映它们的真正含义或做什么(您可以使用'MainTableViewController'和'DetailViewController'。这样做会很好)。
  • Use camelToe labelling for properties and methods. 使用camelToe标签来表示属性和方法。 (I used the labels you provided in your question in order not to confuse you too much). (我使用了你在问题中提供的标签,以免过多地混淆你)。

    Enjoy! 请享用!

This should help out: 这应该有助于:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "next") {
        let destination = segue.destinationViewController as! myViewController
        destination.imageView.image = UIImage(named: ImageArray[tableView.indexPathForSelectedRow?.row])
        destination.textView.text = ImageArray[tableView.indexPathForSelectedRow?.row]
    }

} }

(Where imageView and textView are views in your new viewController.) (imageView和textView是新viewController中的视图。)

Note: 注意:

  1. tableView.indexPathForSelectedRow?.row should give you the selected row, as the name implies, but it can be nil , so be careful. tableView.indexPathForSelectedRow?.row应该给你所选择的行,顾名思义,但它可以是nil ,所以要小心。
  2. In addition, Swift variable naming conventions are camelCase, so imageView is the correct way, while ImageView is incorrect. 另外,Swift变量命名约定是camelCase,因此imageView是正确的方法,而ImageView是不正确的。

In swift 3 you can do something like this: 在swift 3中,您可以执行以下操作:

In your MyTableViewController class: MyTableViewController类中:

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

    // for imageview

    var ImageView : UIImageView = cell.contentView.viewWithTag(1) as! UIImageView
    ImageView.image = UIImage(named: ImageArray[indexPath.row])


    // for text

    var TextView : UILabel = cell.contentView.viewWithTag(2) as! UILabel
    ImageView.text = ImageArray[indexPath.row]

    return cell
}

And in your didSelectRow method: 在你的didSelectRow方法中:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
   let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "myViewController") as! myViewController
   nextVC.myImgView.image = UIImame(named: ImageArray[indexPath.row])
   nextVC.myLabel.text = ImageArray[indexPath.row]
   self.present(nextVC!, animated: true)
}

And in myViewController class: myViewController类中:

class myViewController: UIViewController
{
    let myImageView = UIImageView()
    let myLabel = UILabel()
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM