在集合视图和分页详细视图之间转换的正确方法

Posted

技术标签:

【中文标题】在集合视图和分页详细视图之间转换的正确方法【英文标题】:Correct way to transition between collection view and paged detail view 【发布时间】:2013-08-06 15:10:19 【问题描述】:

目前我有一个 uicollection 视图,它在用户照片中显示特定相册(ALAssets 库)。

在我的 mainView.m 中,我收集了图片:

+ (ALAssetsLibrary *)defaultAssetsLibrary 
    static dispatch_once_t pred = 0;
    static ALAssetsLibrary *library = nil;
    dispatch_once(&pred, ^
        library = [[ALAssetsLibrary alloc] init];
    );
    return library;



- (void)beginLoadingPhotoInfo 

...

[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
                               usingBlock:assetGroupEnumerator
                             failureBlock:^(NSError *error) NSLog(@"Probs");
         ];

将它们(缩略图版本)全部加载到集合视图中,一切正常。

然后当用户选择一张照片时,我调用这个 prepareToSegue 方法:(仍在 mainView.m 中)

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 

    if([[segue identifier] isEqualToString:@"showDetail"])
    
        NSIndexPath *indexPath = [[self.collectionView indexPathsForSelectedItems] lastObject];
        DetailViewController *detailviewcontroller = [segue destinationViewController];
        detailviewcontroller.photoArrayIndex = indexPath.row;

        //photos array
        detailviewcontroller.photosArray = _photoListArray;






目前我正在发送一个包含照片信息的数组并尝试滚动到数组中的位置。

我在这里找到了水平分页的资源:

http://adoptioncurve.net/archives/2013/04/creating-a-paged-photo-gallery-with-a-uicollectionview/

允许使用集合视图进行分页。我写了一个 detailViewController 类。


问题来了。我应该如何将两者联系起来?

想法 1:让我的 mainView 发送一个代表所选照片的​​整数,然后 detailViewController 将加载该整数并开始延迟加载照片。

想法2:以某种方式预加载一些全屏照片,然后将整数与数组中的点一起发送。

想法 3:将数字和我的数组对象都发送到 detailViewController,这样我就不必再次枚举资产库。

这些是正确的方法还是我完全错过了这个想法?


编辑:

我的细节控制器中有一个启用分页的 uicollectionview 流布局。 这是我设置布局的方法:

- (void) setCollectionView 

    [self.collectionView registerClass:[DetailViewCell class] forCellWithReuseIdentifier:@"detailViewCell"];

    //Flow Layout
    UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
    [flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    [flowLayout setMinimumInteritemSpacing:0.0f];
    [flowLayout setMinimumLineSpacing:0.0f];
    [self.collectionView setPagingEnabled:YES];
    [self.collectionView setCollectionViewLayout:flowLayout];


    CGFloat pageWidth = self.collectionView.frame.size.width;
    NSInteger num = _photosArrayIndex + 1;
    CGPoint scrollTo = CGPointMake(pageWidth * num, 0);
    NSLog(@"scroll to: %@", NSStringFromCGPoint(scrollTo));
    [self.collectionView setContentOffset:scrollTo];



它应该做的是从我的主视图中获取值并移动到该图像。不幸的是,它没有。我不知道为什么,而且我觉得有更好的方法来做到这一点。它看起来有点像Hackish。

如何更好地连接两个更好的控制器以及加载照片的正确方法是什么/如何获取我在网格布局中使用的照片(在全尺寸细节视图中)。

感谢您的帮助。

【问题讨论】:

我已经在我最新的应用程序中做到了这一点。当我到达我的电脑时会提供代码。 谢谢,期待看到一个例子。 好的,完成了。它有很多内容,但我已按班级对其进行了细分。每个班级都有一份工作(保持清洁)。如果您有任何问题,请告诉我。 【参考方案1】:

好的,这分为三个部分。

首先是 UICollectionViewController 子类,用于显示照片库 (UIImage)。 其次是UIPageViewController 子类,用于管理每个人PhotoViewController 的左右滑动。 第三个是 UIViewController 子类 (PhotoViewController) 来显示单张照片。

故事板看起来像这样......

左边是UICollectionViewController,它与中间的UIPageViewController 有一个segue。右边是一个UIViewController,在属性窗格中设置了一个Identifier(注意,这里没有segue)。

PhotoViewController 的标识符...

PhotoPageViewController 我有一个自定义对象...

在属性窗格中设置类类型PhotoPageModelController...这是作为PhotoPageViewController 的数据源连接的。

这几乎就是所有需要的情节提要设置。

所以,首先要设置的是PhotoPageModelController。这是PhotoPageViewController 的数据源,因此将分配UIViewController 的子类,以便PhotoPageViewController 可以显示它们。

模型控制器

PhotoPageModelController.h

@class PhotoViewController;

@interface PhotoPageModelController : NSObject <UIPageViewControllerDataSource>

// this is the array of the photos. Either an array of UIImages or objects containing
// them or something. My personal project had an array of photoIDs that I could use to
// pull the photos out of Core Data.

// In this example the array will contain instances of UIImage.
@property (nonatomic, strong) NSArray *photos;

- (PhotoViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard;

- (NSUInteger)indexOfViewController:(PhotoViewController *)controller;

@end

PhotoPageModelController.m

#import "PhotoPageModelController.h"
#import "PhotoViewController.h"

@implementation PhotoPageModelController

- (UIImage *)photoAtIndex:(NSUInteger)index

    // check that the index is in bounds and then return the UIImage to display.
    // In my project I just returned the ID of the photo and let the photo
    // controller load the actual image from core data. (See below)

    if ([self.photos count] == 0
            || index >= [self.photos count]) 
        return nil;
    

    return self.photos[index];


#pragma mark - convenience methods

- (PhotoViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard

    UIImage *photo = [self photoAtIndex:index];

    if (photo == nil) 
        return nil;
    

    // This is why we don't have a segue. We are loading it manually
    // from the storyboard using the identifier.
    EventPhotoViewController *controller = [storyboard instantiateViewControllerWithIdentifier:@"PhotoViewController"];

    // The model controller is where the PhotoViewController gets the actual image from.
    // Or an object containing the image with a name, date, details, etc...
    // The controller doesn't know anything about the other photos. Only the one it's displaying.
    controller.photo = photo;

    return controller;


- (NSUInteger)indexOfViewController:(PhotoViewController *)controller

    // Return the index of the given data view controller.
    // For simplicity, this implementation uses a static array of model objects and the view controller stores the model object; you can therefore use the model object to identify the index.
    return [self.photos indexOfObject:controller.photo];


#pragma mark - page view data source

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController

    // We need to find the index of the current controller so we can get the index
    // and then the view controller for the one before it.
    NSUInteger index = [self indexOfViewController:(PhotoViewController *) viewController];
    if ((index == 0) || (index == NSNotFound)) 
        // We have reached the beginning of the photos array so return nil.
        // This tells the Page View Controller that there isn't another page.
        return nil;
    

    index--;
    return [self viewControllerAtIndex:index storyboard:viewController.storyboard];



// This is the same as above but going forward instead of backward.
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController

    NSUInteger index = [self indexOfViewController:(EventPhotoViewController *) viewController];
    if (index == NSNotFound) 
        return nil;
    

    index++;
    if (index == [self.photoIDs count]) 
        return nil;
    
    return [self viewControllerAtIndex:index storyboard:viewController.storyboard];


@end

好的。这就是照片页面模型控制器。

页面视图控制器

接下来是PhotoPageViewController

PhotoPageViewController.h

#import <Foundation/Foundation.h>

@interface PhotoPageViewController : UIPageViewController

@property (nonatomic, strong) NSArray *photos;
@property (nonatomic) NSUInteger initialIndex;

@end

PhotoPageViewController.m

#import "PhotoPageViewController.h"
#import "PhotoPageModelController.h"

@interface PhotoPageViewController ()

// this property is connected in the storyboard
@property (nonatomic, weak) IBOutlet PhotoPageModelController *modelController;

@end

@implementation PhotoPageViewController

- (void)viewDidLoad

    [super viewDidLoad];

    self.modelController.photos = self.photos;

    // We use the initialIndex property to get the first controller and display it.
    UIViewController *initialController = (UIViewController *)[self.modelController viewControllerAtIndex:self.initialIndex storyboard:self.storyboard];

    [self setViewControllers:@[initialController]
                   direction:UIPageViewControllerNavigationDirectionForward
                    animated:NO
                  completion:^(BOOL finished) 

                  ];

    // That's it. Because we have the datasource class it makes this class really easy and short.

    // It doesn't even need to know anything about the view controllers it is displaying.
    // It's just a dispensing machine.


@end

照片视图控制器

接下来是显示实际照片的视图控制器。

它只需要一个 UIImage 类型的属性,称为 photo,然后是一个 UIImageView 来放置它。我将把它留给你,因为你可以通过多种不同的方式进行操作。

我在我的中放了一个可缩放的UIScrollView,以便用户可以捏缩放照片。我还有一些额外的信息,例如拍摄照片的人的姓名和拍摄日期等...随意设置。

集合视图转场

最后一部分(最后)是从集合视图到页面视图控制器。

这是在prepareForSegue 中完成的。

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

    if ([segue.identifier isEqualToString:@"PhotoSegue"]) 
        PhotoPageViewController *controller = segue.destinationViewController;

        NSIndexPath *selectedIndex = [self.collectionView indexPathsForSelectedItems][0];

        // The PageViewController doesn't need anything except the index to start on...
        // i.e. the index of the photo that the user just selected.
        controller.initialIndex = (NSUInteger)selectedIndex.item;

        // ...and the array of photos it will be displaying.
        controller.photos = self.photos;

        // Everything else is done by the PageViewController.
    

【讨论】:

哇很好的答案,但我有一个问题,它似乎在 PhotoPageViewController.m 中断,错误为 -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0] '。任何的想法?我目前正在尝试解决这个问题。同样在同一个文件中,我假设 self.event 部分只是您项目中的额外代码。 在您的 photopagemodel 控制器中,您的方法 viewControllerAtIndex 在 .h 和 .m 之间有所不同。 再想一想,插入错误的尝试可能是我的错。让我检查一下。 你的意思是错字索引与索引1?解决了这个问题。如果不是,是哪个?听起来 viewDidLoad 方法中的 initialViewController 对象是 nil。可能是情节提要中 PhotoViewController 对象上的标识符有问题。确保设置 StoryBoardID。如果这不起作用,则逐步执行 viewControllerAtIndex 方法。确保它工作正常。 另外,您是否将情节提要中的DataSource 对象连接到PhotoPageViewControllermodelController 属性?我说得不恰当。您必须将 dataSource 和属性连接到该对象。

以上是关于在集合视图和分页详细视图之间转换的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

集合视图中的分页未正确居中

在表格视图单元格内设置集合视图约束的正确方法

从集合视图中删除项目

带有数组的集合/表视图的详细视图?

iOS 中带有分页的 UICollectionView 的页数

WPF 中的分页集合视图