【问题标题】:Change value / image in view controller by selecting a cell within a collection view within an embedded container通过在嵌入式容器内的集合视图中选择一个单元格来更改视图控制器中的值/图像
【发布时间】:2016-03-05 16:23:24
【问题描述】:

在细节视图控制器中,我在左上角有一个“featureImage”,下面是一条薄薄的水平图像条。图像条是由自定义 CollectionViewController 管理的嵌入式容器视图,它显示图像数组。初始的featureImage是images[0]数组中的第一个图像,同样的数组被传递给collection view。

如果容器视图中的单元格被选中/点击,我希望 featureImage 更新为相同的图像。

我想我需要调用委托方法 didSelectItemAtIndexPath,它会给我 indexPath。正确的?但是,我如何将已经来自委托的 indexPath 传递回详细视图控制器。

已编辑 - 代码显示了响应者链和委托方法之间的代码重叠和差异。在 didSelectItemAtIndex 路径中取消注释,响应者链方法有效,而委托方法无效。

协议定义并包含在 DetailViewController 的顶部(我似乎并不关心协议在哪个文件中,并且只输入到类以允许委托属性为“弱”)。两种方法都需要。

protocol FeatureImageController: class {
  func featureImageSelected(indexPath: NSIndexPath)
}
class DetailViewController: UIViewController, FeatureImageController {

在自定义 UICollectionViewController 类中声明的委托属性。只需要委托方法。

 weak var delegate: FeatureImageController?

在 DetailViewController 中启动的委托属性。只需要委托方法。

    override func viewDidLoad() {
    super.viewDidLoad()

    let photoCollectionVC = PhotoCollectionVC()
    photoCollectionVC.delegate = self as FeatureImageController ... }

集合视图控制器 didSelectItemAtIndexPath 方法中的响应者链(活动)或委托方法(已注释掉)。

    override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
    if let imageSelector =  targetForAction("featureImageSelected:", withSender: self) as? FeatureImageController {
        imageSelector.featureImageSelected(indexPath)
    }
//        self.delegate?.featureImageSelected(indexPath)
}

DetailViewController 中的委托方法。两者都需要。

func featureImageSelected(indexPath: NSIndexPath) {
    record?.featureImage = record?.images[indexPath.row]
    self.configureView()
}

【问题讨论】:

  • 随着评论的反转,在 collectionView 中,delegate?.featureImageSelected(indexPath) 和 delegate 都打印 nil;并且永远不会调用 DetailViewController 中的委托方法。

标签: ios objective-c swift uicollectionview uicontainerview


【解决方案1】:

根据我的经验,View Controller 之间的数据选择通信可以通过两种方式最好地实现——委托或响应者链式路由。无论哪种方式,第一步都是创建一个您的 DetailViewController 将遵守的协议。比如:

protocol FeatureImageController: class {
    func featureImageSelected(image: UIImage)
}

然后您的 DetailViewController 将实现此功能并使用它来更改“特征图像”。然后,如何传达这取决于您是使用委托还是响应者链。

代表团 如果您更喜欢使用委托,请在您的 CollectionViewController 上声明一个委托属性,如下所示:

weak var delegate: FeatureImageController?

然后在 didSelectItemAtIndexPath 中,您将使用提供的 indexPath 确定所选图像并将其传递给您的委托:

delegate?.featureImageSelected(selectedImage)

其中 selectedImage 是从集合视图中选择的图像。

响应者链 如果您决定使用响应者链,则无需声明委托属性。相反,您会要求响应您的协议方法的第一个目标。所以在 didSelectItemAtIndexPath 里面你会说:

if let imageController = targetForAction("featureImageSelected:", withSender: self) as? FeatureImageController {
   imageController.featureImageSelected(selectedImage)
}

这两种方法(委托或响应者链)都允许集合视图控制器将其选择传递给细节控制器。委托路线在框架中更为常见,但我发现当我们更频繁地在容器中使用容器时,在没有大量“耦合”的情况下正确管理委托链变得非常讨厌,我对此并不满意。另一方面,响应者链已由框架提供,用于“挖掘”控制器的层次结构,以找到愿意处理您的操作的控制器。

【讨论】:

  • 谢谢!响应者链方法有效。这是我猜的主要问题,但我仍然无法让委托方法发挥作用。如果我理解的话,唯一的区别实际上是在 didSelectItemAtIndexPath 中有委托?.feature.ImageSelected(indexPath) 或响应者链代码(传递 indexPath)。如果我注释掉其中一个,则只有响应者链代码有效。 (另请注意,我得到了一个错误,不使用weak for weak var delegate: FeatureImageController?)
  • 您是否将详细视图控制器分配为集合视图控制器上的委托?我没有明确提到这一点。这是我能想象的与您的委托方案有关的唯一问题。
  • 我不这么认为。我在集合视图控制器中有“var 委托:FeatureImageController”,在详细视图控制器中有委托方法。我在视图中尝试了这个并加载了详细视图控制器: let photoCollectionVC = PhotoCollectionVC(); photoCollectionVC.delegate = self as?特征图像控制器。但这没有用。我将 print("bla") 放入委托方法中,但这仅使用响应者链进行打印,从不使用委托。
  • 您是否声明详细视图控制器遵循 FeatureImageController 协议?我想你必须有,因为响应者链版本有效。当集合视图控制器尝试调用委托方法时,委托属性为 nil 还是设置为您的详细视图控制器的实例?
  • 当您尝试访问委托时,只要委托为 nil,委托方法肯定不会起作用。在您的行“photoCollectionVC.delegate = self as?FeatureImageController”中,您是否收到演员将始终成功的警告?如果您已正确声明 DetailViewController 遵循 FeatureImageController,则不需要强制转换,您应该会看到警告。如果你没有什么不对劲,delegate 是 nil,因为 DetailViewController 不遵守 FeatureImageController。