2013-02-26 102 views
1

我正在開發一個容器UIViewController。 我希望容器能夠委派輪換的管理控制器。ios自定義用戶界面旋轉

// this is in every controller by extending uiviewcontroller with a category 
- (BOOL)shouldAutorotate { 
    UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation]; 
    return [self shouldAutorotateToInterfaceOrientation:orientation]; 
} 

// this is only in the root container because it embed the entire view hierarchy 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 
{ 
    return [self.currentController shouldAutorotate]; 
} 

到目前爲止好。

現在我想的容器,在縱向始終保持和旋轉它的內容查看控制旋轉,然後打補丁的方法是這樣的:

// this is only in the root container because it embed the entire view hierarchy 
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 
{ 
     // forward the message to the current controller for automatic behavior 
    [self.currentController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; 
} 

// this is only in the root container because it embed the entire view hierarchy 
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 
{ 
     // forward the message to the current controller for automatic behavior 
    [self.currentController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; 
} 

// this is only in the root container because it embed the entire view hierarchy 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 
    { 
    BOOL supportedOrientation = [self.currentController shouldAutorotate]; 

    if (supportedOrientation && self.currentOrientation != toInterfaceOrientation) 
    { 
     // virtual orientation by rotating the content view 
     CGRect frame = self.contentView.bounds; 

     CGPoint origin = self.contentView.center; 

     float rotation = [self checkRotationForOrientation:toInterfaceOrientation]; 

     float w = frame.size.width; 
     float h = frame.size.height; 

     // check the right height and width for the new orientation 
     if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) { 
      frame.size = CGSizeMake(MIN(w, h), MAX(w, h)); 
     } else { 
      frame.size = CGSizeMake(MAX(w, h), MIN(w, h)); 
     } 

     // manually call willRotateEtc and willAnimateEtc because the rotation is virtual 
     [self.currentController willRotateToInterfaceOrientation:toInterfaceOrientation duration:kAnimationDuration]; 
     [self.currentController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:kAnimationDuration]; 

     // animate the rotation 
     [UIView animateWithDuration:kAnimationDuration animations:^{ 
      self.contentView.transform = CGAffineTransformMakeRotation(rotation); 
      self.contentView.bounds = frame; 
      self.contentView.center = origin; 
     }]; 

     // update the new virtual orientation for the controller and the container 
     self.currentController.currentOrientation = toInterfaceOrientation; 
     self.currentOrientation = toInterfaceOrientation; 
    } 

    return NO; 
} 

的currentController是PhotoViewController的一個實例(舊風格有一些修改蘋果例如PhotoScroller)至極是:

/* 
File: PhotoViewController.h 
Abstract: Configures and displays the paging scroll view and handles tiling and page configuration. 

*/ 

#import <UIKit/UIKit.h> 
#import "ImageScrollView.h" 
#import "CRWCacheProtocol.h" 

@protocol PhotoViewControllerDelegate; 

@interface PhotoViewController : UIViewController <UIScrollViewDelegate, ImageScrollViewDelegate, CRWCacheProtocol> { 
    NSMutableSet *recycledPages; 
    NSMutableSet *visiblePages; 

// these values are stored off before we start rotation so we adjust our content offset appropriately during rotation 
int   firstVisiblePageIndexBeforeRotation; 
CGFloat  percentScrolledIntoFirstVisiblePage; 
} 

@property (unsafe_unretained, nonatomic) IBOutlet UIScrollView *pagingScrollView; 
@property (unsafe_unretained, nonatomic) IBOutlet UILabel *pageLabel; 
@property (retain, nonatomic) NSString *dataFileName; 

@property (assign, nonatomic) NSInteger currentPage; 

@property (unsafe_unretained, nonatomic) id<PhotoViewControllerDelegate> photoViewControllerDelegate; 

- (NSArray *)imageData; 
- (void) setImageData:(NSArray *) customImageData; 

- (void)configurePage:(ImageScrollView *)page forIndex:(NSUInteger)index; 
- (BOOL)isDisplayingPageForIndex:(NSUInteger)index; 

- (CGRect)frameForPagingScrollView; 
- (CGRect)frameForPageAtIndex:(NSUInteger)index; 
- (CGSize)contentSizeForPagingScrollView; 

- (void)tilePages; 
- (ImageScrollView *)dequeueRecycledPage; 

- (NSUInteger)imageCount; 
- (NSString *)imageNameAtIndex:(NSUInteger)index; 
- (CGSize)imageSizeAtIndex:(NSUInteger)index; 
- (UIImage *)imageAtIndex:(NSUInteger)index; 

@end 

@protocol PhotoViewControllerDelegate <NSObject> 

@optional 
- (void) photoViewController:(PhotoViewController *) controller willDisplayPhoto:(ImageScrollView *) photo; 
- (void) photoViewController:(PhotoViewController *) controller didDisplayPhoto:(ImageScrollView *) photo; 

@end 

和 「.M」

/* 
    File: PhotoViewController.m 
Abstract: Configures and displays the paging scroll view and handles tiling and page configuration. 

*/ 

#import "PhotoViewController.h" 
#import "CRWCache.h" 
#import "CRWebKit.h" 

@interface PhotoViewController() 

@property (nonatomic,retain) NSArray *customImageData; 
@property (nonatomic,assign) NSInteger indexForDownloadingImage; 

@end 

@implementation PhotoViewController 

- (void) scrollToStartPage 
{ 
    float pageWidth = self.pagingScrollView.contentSize.width/[self imageCount]; 
    float pageHeight = self.pagingScrollView.contentSize.height; 

    CGRect frame = CGRectMake(pageWidth*self.currentPage, 0, pageWidth, pageHeight); 
    [self.pagingScrollView scrollRectToVisible:frame animated:NO]; 
} 

- (ImageScrollView *) displayedPageForIndex:(NSUInteger)index 
{ 
    ImageScrollView *page = nil; 
    for (ImageScrollView *currentPage in visiblePages) { 
     if (currentPage.index == index) { 
      page = currentPage; 
      break; 
     } 
    } 
    return page; 
} 

#pragma mark - 
#pragma mark View loading and unloading 

-(void)viewDidAppear:(BOOL)animated 
{ 
    self.pagingScrollView.contentSize = [self contentSizeForPagingScrollView]; 
    [self scrollToStartPage]; 
    [self tilePages]; 
} 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // Step 1: make the outer paging scroll view 
    self.pagingScrollView.pagingEnabled = YES; 
    //self.pagingScrollView.backgroundColor = [UIColor blackColor]; 
    self.pagingScrollView.backgroundColor = [UIColor blackColor]; 
    self.pagingScrollView.showsVerticalScrollIndicator = NO; 
    self.pagingScrollView.showsHorizontalScrollIndicator = NO; 
    self.pagingScrollView.delegate = self; 

    // Step 2: prepare to tile content 
    recycledPages = [[NSMutableSet alloc] init]; 
    visiblePages = [[NSMutableSet alloc] init]; 
} 

- (void)viewDidUnload 
{ 
    [self setDataFileName:nil]; 
    [self setPageLabel:nil]; 
    [self setCustomImageData:nil]; 
    [super viewDidUnload]; 
    self.pagingScrollView = nil; 
    recycledPages = nil; 
    visiblePages = nil; 
} 

#pragma mark - 
#pragma mark Tiling and page configuration 

- (void)tilePages 
{ 
    // Calculate which pages are visible 
    CGRect visibleBounds = self.pagingScrollView.bounds; 
    int firstNeededPageIndex = floorf(CGRectGetMinX(visibleBounds)/CGRectGetWidth(visibleBounds)); 
    int lastNeededPageIndex = floorf((CGRectGetMaxX(visibleBounds)-1)/CGRectGetWidth(visibleBounds)); 
    firstNeededPageIndex = MAX(firstNeededPageIndex, 0); 
    lastNeededPageIndex = MIN(lastNeededPageIndex, [self imageCount] - 1); 

    // Recycle no-longer-visible pages 
    for (ImageScrollView *page in visiblePages) { 
     if (page.index < firstNeededPageIndex || page.index > lastNeededPageIndex) { 
      [recycledPages addObject:page]; 
      [page removeFromSuperview]; 
     } 
    } 
    [visiblePages minusSet:recycledPages]; 

    // add missing pages 
    for (int index = firstNeededPageIndex; index <= lastNeededPageIndex; index++) { 

     // imposta il contatore di pagine e la pagina corrente 
     self.pageLabel.text = [NSString stringWithFormat:@"%i/%i", index + 1, [self imageCount]]; 
     self.currentPage = index; 

     ImageScrollView *page = [self displayedPageForIndex:index]; 
     if (page == nil) { 
      page = [self dequeueRecycledPage]; 
      if (page == nil) { 
       page = [[ImageScrollView alloc] init]; 
      } 
      [self configurePage:page forIndex:index]; 

      page.imageScrollViewDelegate = self; 

      // informo il delegate che sto per visualizzare l'immagine 
      if ([self.photoViewControllerDelegate conformsToProtocol:@protocol(PhotoViewControllerDelegate)] && 
       [self.photoViewControllerDelegate respondsToSelector:@selector(photoViewController:willDisplayPhoto:)]) { 
       [self.photoViewControllerDelegate photoViewController:self willDisplayPhoto:page]; 
      } 

      [self.pagingScrollView addSubview:page]; 

      [visiblePages addObject:page]; 

      // informo il delegate che ho visualizzato l'immagine 
      if ([self.photoViewControllerDelegate conformsToProtocol:@protocol(PhotoViewControllerDelegate)] && 
       [self.photoViewControllerDelegate respondsToSelector:@selector(photoViewController:didDisplayPhoto:)]) { 
       [self.photoViewControllerDelegate photoViewController:self didDisplayPhoto:page]; 
      } 
     } 
    }  
} 

- (ImageScrollView *)dequeueRecycledPage 
{ 
    ImageScrollView *page = [recycledPages anyObject]; 
    if (page) { 
     [recycledPages removeObject:page]; 
    } 
    return page; 
} 

- (BOOL)isDisplayingPageForIndex:(NSUInteger)index 
{ 
    BOOL foundPage = NO; 
    for (ImageScrollView *page in visiblePages) { 
     if (page.index == index) { 
      foundPage = YES; 
      break; 
     } 
    } 
    return foundPage; 
} 

- (void)configurePage:(ImageScrollView *)page forIndex:(NSUInteger)index 
{ 
    page.index = index; 
    page.frame = [self frameForPageAtIndex:index]; 
    /* 
    // Use tiled images 
    [page displayTiledImageNamed:[self imageNameAtIndex:index] size:[self imageSizeAtIndex:index]]; 
    /*/ 
    // To use full images instead of tiled images, replace the "displayTiledImageNamed:" call 
    // above by the following line: 
    [page displayImage:[self imageAtIndex:index]]; 
    //*/ 
} 


#pragma mark - 
#pragma mark ScrollView delegate methods 

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 
{ 
    [self tilePages]; 
} 

#pragma mark - 
#pragma mark View controller rotation methods 
/* 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 
{ 
    return YES; 
} 
*/ 
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 
{ 
    // here, our pagingScrollView bounds have not yet been updated for the new interface orientation. So this is a good 
    // place to calculate the content offset that we will need in the new orientation 
    CGFloat offset = self.pagingScrollView.contentOffset.x; 
    CGFloat pageWidth = self.pagingScrollView.bounds.size.width; 

    if (offset >= 0) { 
     firstVisiblePageIndexBeforeRotation = floorf(offset/pageWidth); 
     percentScrolledIntoFirstVisiblePage = (offset - (firstVisiblePageIndexBeforeRotation * pageWidth))/pageWidth; 
    } else { 
     firstVisiblePageIndexBeforeRotation = 0; 
     percentScrolledIntoFirstVisiblePage = offset/pageWidth; 
    }  
} 

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 
{ 
    // recalculate contentSize based on current orientation 
    self.pagingScrollView.contentSize = [self contentSizeForPagingScrollView]; 

    // adjust frames and configuration of each visible page 
    for (ImageScrollView *page in visiblePages) { 
     CGPoint restorePoint = [page pointToCenterAfterRotation]; 
     CGFloat restoreScale = [page scaleToRestoreAfterRotation]; 
     page.frame = [self frameForPageAtIndex:page.index]; 
     [page setMaxMinZoomScalesForCurrentBounds]; 
     [page restoreCenterPoint:restorePoint scale:restoreScale]; 

    } 

    // adjust contentOffset to preserve page location based on values collected prior to location 
    CGFloat pageWidth = self.pagingScrollView.bounds.size.width; 
    CGFloat newOffset = (firstVisiblePageIndexBeforeRotation * pageWidth) + (percentScrolledIntoFirstVisiblePage * pageWidth); 
    self.pagingScrollView.contentOffset = CGPointMake(newOffset, 0); 
} 

#pragma mark - 
#pragma mark Frame calculations 
#define PADDING 10 

- (CGRect)frameForPagingScrollView { 
    CGRect frame = [[UIScreen mainScreen] bounds]; 
    frame.origin.x -= PADDING; 
    frame.size.width += (2 * PADDING); 
    return frame; 
} 

- (CGRect)frameForPageAtIndex:(NSUInteger)index { 
    // We have to use our paging scroll view's bounds, not frame, to calculate the page placement. When the device is in 
    // landscape orientation, the frame will still be in portrait because the pagingScrollView is the root view controller's 
    // view, so its frame is in window coordinate space, which is never rotated. Its bounds, however, will be in landscape 
    // because it has a rotation transform applied. 
    CGRect bounds = self.pagingScrollView.bounds; 
    CGRect pageFrame = bounds; 
    pageFrame.size.width -= (2 * PADDING); 
    pageFrame.origin.x = (bounds.size.width * index) + PADDING; 
    return pageFrame; 
} 

- (CGSize)contentSizeForPagingScrollView { 
    // We have to use the paging scroll view's bounds to calculate the contentSize, for the same reason outlined above. 
    CGRect bounds = self.pagingScrollView.bounds; 
    return CGSizeMake(bounds.size.width * [self imageCount], bounds.size.height); 
} 


#pragma mark - 
#pragma mark Image wrangling 

- (void)setImageData:(NSArray *)customImageData 
{ 
    _customImageData = customImageData; 
} 

- (NSArray *)imageData { 
    static NSArray *__imageData = nil; // only load the imageData array once 
    if (self.customImageData == nil) { 
     // read the filenames/sizes out of a plist in the app bundle 
//  NSString *path = [[NSBundle mainBundle] pathForResource:@"ImageData" ofType:@"plist"]; 
     NSString *path = [[NSBundle mainBundle] pathForResource:self.dataFileName ofType:@"plist"]; 
     NSData *plistData = [NSData dataWithContentsOfFile:path]; 
     NSString *error; NSPropertyListFormat format; 
     __imageData = [NSPropertyListSerialization propertyListFromData:plistData 
                 mutabilityOption:NSPropertyListImmutable 
                    format:&format 
                 errorDescription:&error]; 
     if (!__imageData) { 
      NSLog(@"Failed to read image names. Error: %@", error); 
     } 
    } 
    else if (self.customImageData != nil) 
    { 
     __imageData = self.customImageData; 
    } 
    return __imageData; 
} 

- (UIImage *)imageAtIndex:(NSUInteger)index { 
    // use "imageWithContentsOfFile:" instead of "imageNamed:" here to avoid caching our images 
    NSString *imageName = [self imageNameAtIndex:index]; 

    UIImage *image; 
    if ([imageName rangeOfString:@"http://"].location != NSNotFound) { 
     NSURL *url = [NSURL URLWithString:imageName]; 
     //* 

     NSString *cachedImage = [CRWCache cacheFileFromURL:url waitUntilFinish:NO andDelegate:self]; 

     if ([CRWCache isExpiredURL:url] || ![[NSFileManager defaultManager] fileExistsAtPath:cachedImage]) { 
      self.indexForDownloadingImage = index; 
     } else { 
      self.indexForDownloadingImage = -1; 
     } 

     NSLog(@"cached image file = %@", cachedImage); 
     if (self.indexForDownloadingImage < 0) { 
      image = [UIImage imageWithContentsOfFile:cachedImage]; 
     } else { 
      image = [UIImage imageWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:kPhotoViewControllerLoadingImage]]; 
     } 
     /*/ 
     NSData *data = [NSData dataWithContentsOfURL:url]; 
     image = [UIImage imageWithData:data]; 
     //*/ 
    } 
    else { 
     NSString *path = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:imageName]; 
     NSLog(@"image name = %@", path); 
     image = [UIImage imageWithContentsOfFile:path]; 
    } 

    return image; 
} 

- (NSString *)imageNameAtIndex:(NSUInteger)index { 
    NSString *name = nil; 
    if (index < [self imageCount]) { 
     NSDictionary *data = [[self imageData] objectAtIndex:index]; 
     name = [data valueForKey:kPhotoViewControllerImageName]; 
    } 
    return name; 
} 

- (CGSize)imageSizeAtIndex:(NSUInteger)index { 
    CGSize size = CGSizeZero; 
    if (index < [self imageCount]) { 
     NSDictionary *data = [[self imageData] objectAtIndex:index]; 
     size.width = [[data valueForKey:@"width"] floatValue]; 
     size.height = [[data valueForKey:@"height"] floatValue]; 
    } 
    return size; 
} 

- (NSUInteger)imageCount { 
    /* 
    static NSUInteger __count = NSNotFound; // only count the images once 
    if (__count == NSNotFound) { 
     __count = [[self imageData] count]; 
    } 
    return __count; 
    */ 
    return [[self imageData] count]; 
} 

#pragma mark - ImageScrollViewDelegate 

- (void)imageScrollViewRecivedTouch:(ImageScrollView *)view 
{ 

} 

#pragma mark - CRWCacheProtocol 

- (void)finischCacheingWithPath:(NSString *)downloadedFilePath 
{ 
    ImageScrollView *page = [self displayedPageForIndex:self.indexForDownloadingImage]; 
    if (page != nil) 
    { 
     [page removeFromSuperview]; 
     [recycledPages addObject:page]; 
     [visiblePages minusSet:recycledPages]; 
    } 
    [self tilePages]; 
} 

- (void)failedCacheingWithPath:(NSString *)downloadedFilePath 
{ 
    NSLog(@"FAILED for path: %@",downloadedFilePath); 
} 

@end 

好吧,現在的問題。當我以模態方式引入PhotoViewController時,旋轉是正確的,圖片顯示正確的位置。

但是,當我在容器中顯示PhotoViewController(如其contentView)時,除了在原始方向(縱向)和滾動視圖內保留「浮動」的圖像之外,所有子視圖都會正確更改。一些調試後,我發現,這是因爲該方法

- (CGRect) frameForPageAtIndex: (NSUInteger) index {...} 

稱爲由

- (void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation duration: (NSTimeInterval) duration {...} 

反過來人工呼叫通過

- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) {...} toInterfaceOrientation container 

沒有收到pagingScrollView旋轉,但總是收到縱向。這似乎是正確的,因爲那個時候輪換還沒有被應用。顯然,使用模式轉換,一切正常,但由於各種原因,我需要容器來管理旋轉。

有沒有辦法將旋轉傳播到pagingScrollView或者有另一種讓容器控制旋轉的方法?

編輯:小變通方法,工作,但不是最好的...糾正以這種方式

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 
{ 
     ... 

     // manually call willRotateEtc because the rotation is virtual 
     [self.currentController willRotateToInterfaceOrientation:toInterfaceOrientation duration:kAnimationDuration]; 

     // animate the rotation 
     [UIView animateWithDuration:kAnimationDuration animations:^{ 
      self.contentView.transform = CGAffineTransformMakeRotation(rotation); 
      self.contentView.bounds = frame; 
      self.contentView.center = origin; 
     }]; 

     // manually call willAnimateEtc because the rotation is virtual 
     [self.currentController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:kAnimationDuration]; 

     ... 
} 

的方法我得到pagingScrollView被正確調整。旋轉效果很好,但動畫對第二,第三,...等圖像會產生strafe效果,因爲它在旋轉後居中,而不是在旋轉執行時居中。

回答

0

最後有一個更好的wokaround :)

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 
    { 
    ... 

    // manually call willRotateEtc because the rotation is virtual 
    [self.currentController willRotateToInterfaceOrientation:toInterfaceOrientation duration:kAnimationDuration]; 

    // animate the rotation 
    [UIView animateWithDuration:kAnimationDuration animations:^{ 
     self.contentView.transform = CGAffineTransformMakeRotation(rotation); 
     self.contentView.bounds = frame; 
     self.contentView.center = origin; 
    // manually call willAnimateEtc because the rotation is virtual 
    [self.currentController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:kAnimationDuration]; 
    }]; 

    ... 
} 

由於而不是動畫過程中,簡單地說,我感動的動畫塊內的調用willAnimateEtc後的圖像進行旋轉。通過這種方式,我得到了一個非常類似於由操作系統自動執行的輪換。唯一的東西仍然是:

1)上的圖像的右側有輕微甩尾,但它是完全可以接受

2)狀態欄被定向在肖像,這不是很好,但確定目前爲止

我將問題留出,以便任何人都可以輕鬆寫出更好的選擇並評估此答案。

感謝您對未來的幫助:)