I am developing a container UIViewController.
I wish the container to delegate the management controllers of the rotation.
// 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];
}
So far so good.
Now I would like the container to remain always in portrait and rotate its contentView to control the rotation, then patching method like this:
// 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;
}
the currentController is an instance of the PhotoViewController (old style of the Apple example PhotoScroller with some modifications) wich is:
/*
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
and the ".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
Ok, now the problem. When I introduce the PhotoViewController modally, the rotation is correct and the pictures show the correct position.
When, however, I present the PhotoViewController in the container (so as its contentView) all subview are changed correctly except the images that remain in their original orientation (portrait) and "float" within the scrollview. After some debugging I found that this is because the method
- (CGRect) frameForPageAtIndex: (NSUInteger) index {...}
called by
- (void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation duration: (NSTimeInterval) duration {...}
in turn called manually by
- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) {...} toInterfaceOrientation container
does not receive the pagingScrollView rotated but always receives portrait orientation. Which seems correct because the rotation has not yet been applied at that moment. Obviously, using the modal transitions, everything works fine but, for various reasons, I need the container to manage the rotation.
Is there any way to propagate the rotation to pagingScrollView or is there another way to let container control the rotation?
EDIT: a small workaround that works but is not the best ... Correcting the method in this manner
- (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];
...
}
I get that pagingScrollView being resized correctly. The rotation work well but the animation get a strafe effect on the second,third, ... and so on image because it is centered after the rotation and not while the rotation is performing.
Finally got a better 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];
}];
...
}
Since the image is rotated after and not during the animation, simply, I moved the call to willAnimateEtc inside the animation block. In this way I get a rotation very similar to that carried out automatically by the OS. The only things still remain are:
1) a slight flick on the right side of the image, but it is quite acceptable
2) the status bar is oriented in portrait, which is not fine, but ok for now
I leave the question open so that anyone can write easily a better alternative and evaluate this answer.
Thanks for the future help :)
Related
I have a UIScrollView with 10+ pages configured in a way that contents from 3 pages will always appear on a screen at a time (screenshot below).
For a feature, I need to temporarily disable the inactive pages on the screen, and I was wondering if there is a way to hide all the inactive pages, and only keep the active page visible.
Alternatively, if this is not feasible, is it possible to extract all the views that's in the active page?
Thanks!
Try the function below , and adjust it to fit your situation:
func disableInactiveView(){
let offset = scrollView.contentOffset;
let frame = scrollView.frame;
let shownFrame = CGRect(x: offset.x, y: offset.y , width: frame.size.width, height: frame.size.height)
for colorView in scrollView.subviews{
if shownFrame.contains(colorView.frame) {
colorView.backgroundColor = UIColor.green;
}else{
colorView.backgroundColor = UIColor.red;
}
}
}
Adding all the views to scrollView is not good for performance. The best solution for managing such view is to use UICollectionView with custom UICollectionViewCells. Collection view will do everything you need - reuse your views and place them correctly according to visible rect. In case you cannot use UICollectionView try something like that:
#interface SampleClass : UIViewController <UIScrollViewDelegate>
#property (nonatomic) UIScrollView *scrollView;
#property (nonatomic) NSUInteger viewsCount;
#property (nonatomic) NSDictionary<NSNumber *, UIView *> *visibleViewsByIndexes;
#end
#implementation SampleClass
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self updateViews];
[self hideInactiveViews];
}
- (void)updateViews
{
CGPoint offset = self.scrollView.contentOffset;
CGRect frame = self.scrollView.bounds;
CGRect visibleRect = frame;
visibleRect.origin.x += offset.x;
// Determine indexes of views which should be displayed.
NSMutableArray<NSNumber *> *viewsToDisplay = [NSMutableArray new];;
for (NSUInteger index = 0; index < self.viewsCount; index++) {
CGRect viewFrame = [self frameForViewAtIndex:index];
if (CGRectIntersectsRect(viewFrame, visibleRect)) {
[viewsToDisplay addObject:#(index)];
}
}
// Remove not visible views.
NSDictionary<NSNumber *, UIView *> *oldVisibleViewsByIndexes = self.visibleViewsByIndexes;
NSMutableDictionary<NSNumber *, UIView *> *newVisibleViewsByIndexes = [NSMutableDictionary new];
for (NSNumber *indexNumber in oldVisibleViewsByIndexes.allKeys) {
if (![viewsToDisplay containsObject:indexNumber]) {
UIView *viewToRemove = oldVisibleViewsByIndexes[indexNumber];
[viewToRemove removeFromSuperview];
} else {
newVisibleViewsByIndexes[indexNumber] = oldVisibleViewsByIndexes[indexNumber];
}
}
// Add new views.
for (NSNumber *indexNumber in viewsToDisplay) {
if (![oldVisibleViewsByIndexes.allKeys containsObject:indexNumber]) {
UIView *viewToAdd = [self viewAtIndex:indexNumber.unsignedIntegerValue];
viewToAdd.frame = [self frameForViewAtIndex:indexNumber.unsignedIntegerValue];
[self.scrollView addSubview:viewToAdd];
}
}
self.visibleViewsByIndexes = newVisibleViewsByIndexes;
}
- (CGRect)frameForViewAtIndex:(NSUInteger)index
{
// Return correct frame for view at index.
return CGRectZero;
}
- (UIView *)viewAtIndex:(NSUInteger)index
{
return [UIView new];
}
- (void)hideInactiveViews
{
CGPoint offset = self.scrollView.contentOffset;
CGRect frame = self.scrollView.bounds;
CGRect visibleRect = frame;
visibleRect.origin.x += offset.x;
for (UIView *view in self.visibleViewsByIndexes.allValues) {
if (CGRectContainsRect(visibleRect, view.frame)) {
// Active view.
view.alpha = 1;
} else {
// Inactive view.
view.alpha = 0.2;
}
}
}
#end
I'm using viewWillLayoutSubviews to detect orientation changes but as in iOS 8 and Xcode 6 is deprecated I need to use new viewWillTransitionToSize. My problem is that I'm not able to use it.
My viewWillLayoutSubviews is:
- (void)viewWillLayoutSubviews{
NSInteger maxQuantityForWidth = MAX(3, [self.barButtons count]);
CGFloat buttonWidth = (CGRectGetWidth(self.tabBar.bounds) - (kBarButtonSeparation * (maxQuantityForWidth - 1))) / maxQuantityForWidth;
for (NSInteger i = 0; i < [self.barButtons count]; i++) {
[self.barButtons[i] setFrame:CGRectMake(i * ((buttonWidth + kBarButtonSeparation)), 2.0, buttonWidth, CGRectGetHeight(self.tabBar.bounds) - 2.0)];
}
if ([self isShowingTabBar]) {
self.functionalityContainer.frame = CGRectMake(0.0, CGRectGetHeight(self.tabBar.bounds), CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - CGRectGetHeight(self.tabBar.bounds));
} else {
self.functionalityContainer.frame = self.view.bounds;
}
if (IS_IPAD){
[self updateToolbar];
CGRect frame = self.slidingViewController.topViewController.view.frame;
if (LANDSCAPE) {
frame.size.width = 700;
} else {
frame.size.width = 768;
}
[self.slidingViewController.topViewController.view setFrame:frame];
}
}
and it's part of the Navigation section using ECSLidingViewController.
I tried the following:
- (void) viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
[self updateToolbar];
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
if (IS_IPAD){
CGRect frame = self.slidingViewController.topViewController.view.frame;
if (LANDSCAPE) {
NSInteger maxQuantityForWidth = MAX(3, [self.barButtons count]);
CGFloat buttonWidth = (758 - (kBarButtonSeparation * (maxQuantityForWidth - 1))) / maxQuantityForWidth;
for (NSInteger i = 0; i < [self.barButtons count]; i++) {
[self.barButtons[i] setFrame:CGRectMake(i * ((buttonWidth + kBarButtonSeparation)), 2.0, buttonWidth, CGRectGetHeight(self.tabBar.bounds) - 2.0)];
}
frame.size.width = 768;
} else {
NSInteger maxQuantityForWidth = MAX(3, [self.barButtons count]);
CGFloat buttonWidth = (758 - (kBarButtonSeparation * (maxQuantityForWidth - 1))) / maxQuantityForWidth;
for (NSInteger i = 0; i < [self.barButtons count]; i++) {
[self.barButtons[i] setFrame:CGRectMake(i * ((buttonWidth + kBarButtonSeparation)), 2.0, buttonWidth, CGRectGetHeight(self.tabBar.bounds) - 2.0)];
}
frame.size.width = 768;
}
[self.slidingViewController.topViewController.view setFrame:frame];
}
if([self isShowingTabBar]){
[self showTabBar];
}else{
[self hideTabBar];
}
if (IS_IPAD && LANDSCAPE) {
self.slidingViewController.resetStrategy = ECNone;
[self.navigationItem setLeftBarButtonItems:#[] animated:YES];
if (!self.slidingViewController.underLeftShowing) {
[self.slidingViewController anchorTopViewTo:ECRight];
}
} else {
self.slidingViewController.resetStrategy = ECTapping | ECPanning;
[self.navigationItem setLeftBarButtonItems:#[self.displayMenuBarButtonItem] animated:YES];
[self.slidingViewController resetTopView];
}
[self.slidingViewController.topViewController.view setNeedsDisplay];
[self.functionalityNavigationController.view setNeedsDisplay];
[self.view setNeedsDisplay];
}];
NSString *orientation = [[NSString alloc]initWithFormat:#"Larghezza: %f Altezza: %f", self.view.frame.size.width, self.view.frame.size.height];
[self.view makeToast:orientation duration:3.0 position:#"bottom"];
[super viewWillTransitionToSize: size withTransitionCoordinator: coordinator];
}
but it doesn't work, it seems doesn't "understand" when it's landscape or portrait. Any idea on how to fix it?
*** EDIT:
here the definitions I use
#define LANDSCAPE UIInterfaceOrientationIsLandscape(self.interfaceOrientation)
#define LANDSCAPE_RIGHT [UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft
#define LANDSCAPE_LEFT [UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight
#define PORTRAIT UIInterfaceOrientationIsPortrait(self.interfaceOrientation)
#define PORTRAIT_REVERSE [UIDevice currentDevice].orientation == UIDeviceOrientationPortraitUpsideDown
The question seems a bit "old", anyway, at least with the introduction of iOS9 (especially multitasking), it is not recommended to draw conclusions regarding the current screen format based on device-orientation - instead, one should evaluate the current screen size. The following indicates whether the screen format is portrait or rather landscape-ish:
- (void) viewWillTransitionToSize : (CGSize) screenSize withTransitionCoordinator : (id<UIViewControllerTransitionCoordinator> _Nonnull) coordinator
{
BOOL isLandscapeOrientation = (screenSize.width > screenSize.height);
...
...
}
You should be using the trait collection method and then check the size class.
/*
This method is called when the view controller's trait collection is changed by its parent.
If you override this method, you should either call super to propagate the change to children or manually forward the change to children.
*/
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator NS_AVAILABLE_IOS(8_0);
I don't know how to flip my uiview vertically , i have many views and i every view
there are some photos and their description, i want a flip view like a book but not
left to right it must be top to bottom or bottom to top,
i want to flip the whole page vertically like top to bottom or bottom to top,
how to do this kind of work in ios ?
i m seraching on google but thats not working i'm new in developement
So please kindly anyone can guide me properly how can i flip my views Please Please help me
out
Thanks in advance.
download the code from https://github.com/mtabini/AFKPageFlipper
and change the AFKPageFlipper.h and AFKPageFlipper.m file
//
// AFKPageFlipper.h
// AFKPageFlipper
//
// Created by Marco Tabini on 10-10-11.
// Copyright 2010 AFK Studio Partnership. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#class AFKPageFlipper;
#protocol AFKPageFlipperDataSource
- (NSInteger) numberOfPagesForPageFlipper:(AFKPageFlipper *) pageFlipper;
- (UIView *) viewForPage:(NSInteger) page inFlipper:(AFKPageFlipper *) pageFlipper;
#end
typedef enum {
AFKPageFlipperDirectionTop,
AFKPageFlipperDirectionBottom,
} AFKPageFlipperDirection;
#interface AFKPageFlipper : UIView {
NSObject <AFKPageFlipperDataSource> *dataSource;
NSInteger currentPage;
NSInteger numberOfPages;
UIView *currentView;
UIView *nextView;
CALayer *backgroundAnimationLayer;
CALayer *flipAnimationLayer;
AFKPageFlipperDirection flipDirection;
float startFlipAngle;
float endFlipAngle;
float currentAngle;
BOOL setNextViewOnCompletion;
BOOL animating;
BOOL disabled;
}
#property (nonatomic,retain) NSObject <AFKPageFlipperDataSource> *dataSource;
#property (nonatomic,assign) NSInteger currentPage;
#property (nonatomic, retain) UITapGestureRecognizer *tapRecognizer;
#property (nonatomic, retain) UIPanGestureRecognizer *panRecognizer;
#property (nonatomic,assign) BOOL disabled;
- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated;
#end
//
// AFKPageFlipper.m
// AFKPageFlipper
//
// Created by Marco Tabini on 10-10-12.
// Copyright 2010 AFK Studio Partnership. All rights reserved.
//
#import "AFKPageFlipper.h"
#pragma mark -
#pragma mark UIView helpers
#interface UIView(Extended)
- (UIImage *) imageByRenderingView;
#end
#implementation UIView(Extended)
- (UIImage *) imageByRenderingView {
CGFloat oldAlpha = self.alpha;
self.alpha = 1;
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.alpha = oldAlpha;
return resultingImage;
}
#end
#pragma mark -
#pragma mark Private interface
#interface AFKPageFlipper()
#property (nonatomic,assign) UIView *currentView;
#property (nonatomic,assign) UIView *nextView;
#end
#implementation AFKPageFlipper
#synthesize tapRecognizer = _tapRecognizer;
#synthesize panRecognizer = _panRecognizer;
#pragma mark -
#pragma mark Flip functionality
- (void) initFlip {
// Create screenshots of view
UIImage *currentImage = [self.currentView imageByRenderingView];
UIImage *newImage = [self.nextView imageByRenderingView];
// Hide existing views
self.currentView.alpha = 0;
self.nextView.alpha = 0;
// Create representational layers
CGRect rect = self.bounds;
rect.size.height /= 2;
backgroundAnimationLayer = [CALayer layer];
backgroundAnimationLayer.frame = self.bounds;
backgroundAnimationLayer.zPosition = -300000;
CALayer *topLayer = [CALayer layer];
topLayer.frame = rect;
topLayer.masksToBounds = YES;
topLayer.contentsGravity = kCAGravityBottom;
[backgroundAnimationLayer addSublayer:topLayer];
rect.origin.y = rect.size.height;
CALayer *bottomLayer = [CALayer layer];
bottomLayer.frame = rect;
bottomLayer.masksToBounds = YES;
bottomLayer.contentsGravity = kCAGravityTop;
[backgroundAnimationLayer addSublayer:bottomLayer];
if (flipDirection == AFKPageFlipperDirectionBottom) {
topLayer.contents = (id) [newImage CGImage];
bottomLayer.contents = (id) [currentImage CGImage];
} else {
topLayer.contents = (id) [currentImage CGImage];
bottomLayer.contents = (id) [newImage CGImage];
}
[self.layer addSublayer:backgroundAnimationLayer];
rect.origin.y = 0;
flipAnimationLayer = [CATransformLayer layer];
flipAnimationLayer.anchorPoint = CGPointMake(0.5, 1);
flipAnimationLayer.frame = rect;
[self.layer addSublayer:flipAnimationLayer];
CALayer *backLayer = [CALayer layer];
backLayer.frame = flipAnimationLayer.bounds;
backLayer.doubleSided = NO;
backLayer.masksToBounds = YES;
[flipAnimationLayer addSublayer:backLayer];
CALayer *frontLayer = [CALayer layer];
frontLayer.frame = flipAnimationLayer.bounds;
frontLayer.doubleSided = NO;
frontLayer.masksToBounds = YES;
frontLayer.transform = CATransform3DMakeRotation(M_PI, 1.0, 0.0, 0);
[flipAnimationLayer addSublayer:frontLayer];
if (flipDirection == AFKPageFlipperDirectionBottom) {
backLayer.contents = (id) [currentImage CGImage];
backLayer.contentsGravity = kCAGravityBottom;
frontLayer.contents = (id) [newImage CGImage];
frontLayer.contentsGravity = kCAGravityTop;
CATransform3D transform = CATransform3DMakeRotation(1.1/M_PI, 1.0, 0.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.transform = transform;
currentAngle = startFlipAngle = 0;
endFlipAngle = M_PI;
} else {
//down
backLayer.contents = (id) [newImage CGImage];
backLayer.contentsGravity = kCAGravityBottom;
frontLayer.contents = (id) [currentImage CGImage];
frontLayer.contentsGravity = kCAGravityTop;
CATransform3D transform = CATransform3DMakeRotation(M_PI/1.1, 1.0, 0.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.transform = transform;
currentAngle = startFlipAngle = M_PI;
endFlipAngle = 0;
}
}
- (void) cleanupFlip {
[backgroundAnimationLayer removeFromSuperlayer];
[flipAnimationLayer removeFromSuperlayer];
backgroundAnimationLayer = Nil;
flipAnimationLayer = Nil;
animating = NO;
if (setNextViewOnCompletion) {
[self.currentView removeFromSuperview];
self.currentView = self.nextView;
self.nextView = Nil;
} else {
[self.nextView removeFromSuperview];
self.nextView = Nil;
}
self.currentView.alpha = 1;
}
- (void) setFlipProgress:(float) progress setDelegate:(BOOL) setDelegate animate:(BOOL) animate {
if (animate) {
animating = YES;
}
float newAngle = startFlipAngle + progress * (endFlipAngle - startFlipAngle);
float duration = animate ? 0.5 * fabs((newAngle - currentAngle) / (endFlipAngle - startFlipAngle)) : 0;
currentAngle = newAngle;
CATransform3D endTransform = CATransform3DIdentity;
endTransform.m34 = 1.0f / 2500.0f;
endTransform = CATransform3DRotate(endTransform, newAngle, 1.0, 0.0, 0.0);
[flipAnimationLayer removeAllAnimations];
[CATransaction begin];
[CATransaction setAnimationDuration:duration];
flipAnimationLayer.transform = endTransform;
[CATransaction commit];
if (setDelegate) {
[self performSelector:#selector(cleanupFlip) withObject:Nil afterDelay:duration];
}
}
- (void) flipPage {
[self setFlipProgress:1.0 setDelegate:YES animate:YES];
}
#pragma mark -
#pragma mark Animation management
- (void)animationDidStop:(NSString *) animationID finished:(NSNumber *) finished context:(void *) context {
[self cleanupFlip];
}
#pragma mark -
#pragma mark Properties
#synthesize currentView;
- (void) setCurrentView:(UIView *) value {
if (currentView) {
[currentView release];
}
currentView = [value retain];
}
#synthesize nextView;
- (void) setNextView:(UIView *) value {
if (nextView) {
[nextView release];
}
nextView = [value retain];
}
#synthesize currentPage;
- (BOOL) doSetCurrentPage:(NSInteger) value {
if (value == currentPage) {
return FALSE;
}
flipDirection = value < currentPage ? AFKPageFlipperDirectionBottom : AFKPageFlipperDirectionTop;
currentPage = value;
self.nextView = [self.dataSource viewForPage:value inFlipper:self];
[self addSubview:self.nextView];
return TRUE;
}
- (void) setCurrentPage:(NSInteger) value {
if (![self doSetCurrentPage:value]) {
return;
}
setNextViewOnCompletion = YES;
animating = YES;
self.nextView.alpha = 0;
[UIView beginAnimations:#"" context:Nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
self.nextView.alpha = 1;
[UIView commitAnimations];
}
- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated {
if (![self doSetCurrentPage:value]) {
return;
}
setNextViewOnCompletion = YES;
animating = YES;
if (animated) {
[self initFlip];
[self performSelector:#selector(flipPage) withObject:Nil afterDelay:0.091];
} else {
[self animationDidStop:Nil finished:[NSNumber numberWithBool:NO] context:Nil];
}
}
#synthesize dataSource;
- (void) setDataSource:(NSObject <AFKPageFlipperDataSource>*) value {
if (dataSource) {
[dataSource release];
}
dataSource = [value retain];
numberOfPages = [dataSource numberOfPagesForPageFlipper:self];
currentPage = 0;
self.currentPage = 1;
}
#synthesize disabled;
- (void) setDisabled:(BOOL) value {
disabled = value;
self.userInteractionEnabled = !value;
for (UIGestureRecognizer *recognizer in self.gestureRecognizers) {
recognizer.enabled = !value;
}
}
#pragma mark -
#pragma mark Touch management
- (void) tapped:(UITapGestureRecognizer *) recognizer {
if (animating || self.disabled) {
return;
}
if (recognizer.state == UIGestureRecognizerStateRecognized) {
NSInteger newPage;
if ([recognizer locationInView:self].y < (self.bounds.size.height - self.bounds.origin.y) / 2) {
newPage = MAX(1, self.currentPage - 1);
} else {
newPage = MIN(self.currentPage + 1, numberOfPages);
}
[self setCurrentPage:newPage animated:YES];
}
}
- (void) panned:(UIPanGestureRecognizer *) recognizer {
if (animating) {
return;
}
static BOOL hasFailed;
static BOOL initialized;
static NSInteger oldPage;
float translation = [recognizer translationInView:self].y;
float progress = translation / self.bounds.size.height;
if (flipDirection == AFKPageFlipperDirectionTop) {
progress = MIN(progress, 0);
} else {
progress = MAX(progress, 0);
}
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
hasFailed = FALSE;
initialized = FALSE;
animating = NO;
setNextViewOnCompletion = NO;
break;
case UIGestureRecognizerStateChanged:
if (hasFailed) {
return;
}
if (!initialized) {
oldPage = self.currentPage;
if (translation > 0) {
if (self.currentPage > 1) {
[self doSetCurrentPage:self.currentPage - 1];
} else {
hasFailed = TRUE;
return;
}
} else {
if (self.currentPage < numberOfPages) {
[self doSetCurrentPage:self.currentPage + 1];
} else {
hasFailed = TRUE;
return;
}
}
hasFailed = NO;
initialized = TRUE;
setNextViewOnCompletion = NO;
[self initFlip];
}
[self setFlipProgress:fabs(progress) setDelegate:NO animate:NO];
break;
case UIGestureRecognizerStateFailed:
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
break;
case UIGestureRecognizerStateRecognized:
if (hasFailed) {
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
return;
}
if (fabs((translation + [recognizer velocityInView:self].y / 4) / self.bounds.size.height) > 0.5) {
setNextViewOnCompletion = YES;
[self setFlipProgress:1.0 setDelegate:YES animate:YES];
} else {
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
}
break;
default:
break;
}
}
#pragma mark -
#pragma mark Frame management
- (void) setFrame:(CGRect) value {
super.frame = value;
numberOfPages = [dataSource numberOfPagesForPageFlipper:self];
if (self.currentPage > numberOfPages) {
self.currentPage = numberOfPages;
}
}
#pragma mark -
#pragma mark Initialization and memory management
+ (Class) layerClass {
return [CATransformLayer class];
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
_tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
_panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panned:)];
[_tapRecognizer requireGestureRecognizerToFail:_panRecognizer];
[self addGestureRecognizer:_tapRecognizer];
[self addGestureRecognizer:_panRecognizer];
}
return self;
}
- (void)dealloc {
self.dataSource = Nil;
self.currentView = Nil;
self.nextView = Nil;
self.tapRecognizer = Nil;
self.panRecognizer = Nil;
[super dealloc];
}
#end
becoz of AFKPageFlipper, i am able to do the above code....credit goes to Mr. mtabini ( AFKPageFlipper author )
You could set a negative scale, like:
[theView setTransform:CGAffineTransformMakeScale(1, -1)];
Same as Pradeep, but with shadows, also ARC adapted:
AFKPageFlipper.h
//
// AFKPageFlipper.h
// AFKPageFlipper
//
// Created by Marco Tabini on 10-10-11.
// Copyright 2010 AFK Studio Partnership. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#class AFKPageFlipper;
#protocol AFKPageFlipperDataSource
- (NSInteger) numberOfPagesForPageFlipper:(AFKPageFlipper *) pageFlipper;
- (UIView *) viewForPage:(NSInteger) page inFlipper:(AFKPageFlipper *) pageFlipper;
#end
typedef enum {
AFKPageFlipperDirectionTop,
AFKPageFlipperDirectionBottom,
} AFKPageFlipperDirection;
#interface AFKPageFlipper : UIView {
NSObject <AFKPageFlipperDataSource> *dataSource;
NSInteger currentPage;
NSInteger numberOfPages;
// shadows
CALayer *frontLayerShadow;
CALayer *backLayerShadow;
CALayer *leftLayerShadow;
CALayer *rightLayerShadow;
// shadows
CALayer *backgroundAnimationLayer;
CALayer *flipAnimationLayer;
AFKPageFlipperDirection flipDirection;
float startFlipAngle;
float endFlipAngle;
float currentAngle;
BOOL setNextViewOnCompletion;
BOOL animating;
BOOL disabled;
}
#property (nonatomic,retain) NSObject <AFKPageFlipperDataSource> *dataSource;
#property (nonatomic,assign) NSInteger currentPage;
#property (nonatomic, retain) UITapGestureRecognizer *tapRecognizer;
#property (nonatomic, retain) UIPanGestureRecognizer *panRecognizer;
#property (nonatomic,assign) BOOL disabled;
- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated;
#end
AFKPageFlipper.m
//
// AFKPageFlipper.m
// AFKPageFlipper
//
// Created by Marco Tabini on 10-10-12.
// Copyright 2010 AFK Studio Partnership. All rights reserved.
//
#import "AFKPageFlipper.h"
#pragma mark -
#pragma mark UIView helpers
#interface UIView(Extended)
- (UIImage *) imageByRenderingView;
#end
#implementation UIView(Extended)
- (UIImage *) imageByRenderingView {
CGFloat oldAlpha = self.alpha;
self.alpha = 1;
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.alpha = oldAlpha;
return resultingImage;
}
#end
#pragma mark -
#pragma mark Private interface
#interface AFKPageFlipper()
#property (nonatomic,retain) UIView *currentView;
#property (nonatomic,retain) UIView *nextView;
#end
#implementation AFKPageFlipper
#synthesize tapRecognizer = _tapRecognizer;
#synthesize panRecognizer = _panRecognizer;
#pragma mark -
#pragma mark Flip functionality
- (void) initFlip {
// Create screenshots of view
UIImage *currentImage = [self.currentView imageByRenderingView];
UIImage *newImage = [self.nextView imageByRenderingView];
// Hide existing views
self.currentView.alpha = 0;
self.nextView.alpha = 0;
// Create representational layers
CGRect rect = self.bounds;
rect.size.height /= 2;
backgroundAnimationLayer = [CALayer layer];
backgroundAnimationLayer.frame = self.bounds;
backgroundAnimationLayer.zPosition = -300000;
CALayer *topLayer = [CALayer layer];
topLayer.frame = rect;
topLayer.masksToBounds = YES;
topLayer.contentsGravity = kCAGravityBottom;
[backgroundAnimationLayer addSublayer:topLayer];
rect.origin.y = rect.size.height;
CALayer *bottomLayer = [CALayer layer];
bottomLayer.frame = rect;
bottomLayer.masksToBounds = YES;
bottomLayer.contentsGravity = kCAGravityTop;
[backgroundAnimationLayer addSublayer:bottomLayer];
if (flipDirection == AFKPageFlipperDirectionBottom) {
topLayer.contents = (id) [newImage CGImage];
bottomLayer.contents = (id) [currentImage CGImage];
} else {
topLayer.contents = (id) [currentImage CGImage];
bottomLayer.contents = (id) [newImage CGImage];
}
[self.layer addSublayer:backgroundAnimationLayer];
rect.origin.y = 0;
flipAnimationLayer = [CATransformLayer layer];
flipAnimationLayer.anchorPoint = CGPointMake(0.5, 1);
flipAnimationLayer.frame = rect;
[self.layer addSublayer:flipAnimationLayer];
CALayer *backLayer = [CALayer layer];
backLayer.frame = flipAnimationLayer.bounds;
backLayer.doubleSided = NO;
backLayer.masksToBounds = YES;
[flipAnimationLayer addSublayer:backLayer];
CALayer *frontLayer = [CALayer layer];
frontLayer.frame = flipAnimationLayer.bounds;
frontLayer.doubleSided = NO;
frontLayer.masksToBounds = YES;
frontLayer.transform = CATransform3DMakeRotation(M_PI, 1.0, 0, 0);
[flipAnimationLayer addSublayer:frontLayer];
// shadows
frontLayerShadow = [CALayer layer];
frontLayerShadow.frame = frontLayer.bounds;
frontLayerShadow.doubleSided = NO;
frontLayerShadow.masksToBounds = YES;
frontLayerShadow.opacity = 0;
frontLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
[frontLayer addSublayer:frontLayerShadow];
backLayerShadow = [CALayer layer];
backLayerShadow.frame = backLayer.bounds;
backLayerShadow.doubleSided = NO;
backLayerShadow.masksToBounds = YES;
backLayerShadow.opacity = 0;
backLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
[backLayer addSublayer:backLayerShadow];
leftLayerShadow = [CALayer layer];
leftLayerShadow.frame = topLayer.bounds;
leftLayerShadow.doubleSided = NO;
leftLayerShadow.masksToBounds = YES;
leftLayerShadow.opacity = 0.0;
leftLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
[topLayer addSublayer:leftLayerShadow];
rightLayerShadow = [CALayer layer];
rightLayerShadow.frame = bottomLayer.bounds;
rightLayerShadow.doubleSided = NO;
rightLayerShadow.masksToBounds = YES;
rightLayerShadow.opacity = 0.0;
rightLayerShadow.backgroundColor = [UIColor blackColor].CGColor;
[bottomLayer addSublayer:rightLayerShadow];
// shadows
if (flipDirection == AFKPageFlipperDirectionBottom) {
backLayer.contents = (id) [currentImage CGImage];
backLayer.contentsGravity = kCAGravityBottom;
frontLayer.contents = (id) [newImage CGImage];
frontLayer.contentsGravity = kCAGravityTop;
CATransform3D transform = CATransform3DMakeRotation(1.1/M_PI, 1.0, 0.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.transform = transform;
currentAngle = startFlipAngle = 0;
endFlipAngle = M_PI;
} else {
//down
backLayer.contents = (id) [newImage CGImage];
backLayer.contentsGravity = kCAGravityBottom;
frontLayer.contents = (id) [currentImage CGImage];
frontLayer.contentsGravity = kCAGravityTop;
CATransform3D transform = CATransform3DMakeRotation(M_PI/1.1, 1.0, 0.0, 0.0);
transform.m34 = 1.0f / 2500.0f;
flipAnimationLayer.transform = transform;
currentAngle = startFlipAngle = M_PI;
endFlipAngle = 0;
}
}
- (void) cleanupFlip {
[backgroundAnimationLayer removeFromSuperlayer];
[flipAnimationLayer removeFromSuperlayer];
backgroundAnimationLayer = Nil;
flipAnimationLayer = Nil;
animating = NO;
if (setNextViewOnCompletion) {
[self.currentView removeFromSuperview];
self.currentView = self.nextView;
self.nextView = Nil;
} else {
[self.nextView removeFromSuperview];
self.nextView = Nil;
}
self.currentView.alpha = 1;
}
- (void) setFlipProgress:(float) progress setDelegate:(BOOL) setDelegate animate:(BOOL) animate {
if (animate) {
animating = YES;
}
float newAngle = startFlipAngle + progress * (endFlipAngle - startFlipAngle);
float duration = animate ? 0.5 * fabs((newAngle - currentAngle) / (endFlipAngle - startFlipAngle)) : 0;
currentAngle = newAngle;
CATransform3D endTransform = CATransform3DIdentity;
endTransform.m34 = 1.0f / 2500.0f;
endTransform = CATransform3DRotate(endTransform, newAngle, 1.0, 0.0, 0.0);
[flipAnimationLayer removeAllAnimations];
// shadows
//NSLog(#"End flip angle: %.0f, \tstartflip: %.0f, \tprogress: %.2f\tduration: %.2f", endFlipAngle, startFlipAngle,progress, duration);
CGFloat newShadowOpacity = (0.5 - progress);
if(newShadowOpacity < 0) {
newShadowOpacity *= -1;
}
if (newShadowOpacity < 0.05) {
newShadowOpacity = 0;
}
// shadows
if (duration < 0.15) {
duration = 0.15;
}
[UIView animateWithDuration: duration delay: 0 options: UIViewAnimationOptionCurveLinear animations: ^(void) {
flipAnimationLayer.transform = endTransform;
if (endFlipAngle < startFlipAngle) {
if(progress < 0.5) {
rightLayerShadow.opacity = newShadowOpacity;
frontLayerShadow.opacity = (0.5 - newShadowOpacity)/2;
} else {
backLayerShadow.opacity = (0.5 - newShadowOpacity)/2;
leftLayerShadow.opacity = newShadowOpacity;
}
} else {
if(progress < 0.5) {
leftLayerShadow.opacity = newShadowOpacity;
backLayerShadow.opacity = (0.5 - newShadowOpacity)/2;
} else {
frontLayerShadow.opacity = (0.5 - newShadowOpacity)/2;
rightLayerShadow.opacity = newShadowOpacity;
}
}
// shadows
} completion: ^(BOOL completion) {
}];
if (setDelegate) {
[self performSelector:#selector(cleanupFlip) withObject:Nil afterDelay:duration];
}
}
- (void) flipPage {
[self setFlipProgress:1.0 setDelegate:YES animate:YES];
}
#pragma mark -
#pragma mark Animation management
- (void)animationDidStop:(NSString *) animationID finished:(NSNumber *) finished context:(void *) context {
[self cleanupFlip];
}
#pragma mark -
#pragma mark Properties
#synthesize currentPage;
- (BOOL) doSetCurrentPage:(NSInteger) value {
if (value == currentPage) {
return FALSE;
}
flipDirection = value < currentPage ? AFKPageFlipperDirectionBottom : AFKPageFlipperDirectionTop;
currentPage = value;
self.nextView = [self.dataSource viewForPage:value inFlipper:self];
[self addSubview:self.nextView];
return TRUE;
}
- (void) setCurrentPage:(NSInteger) value {
if (![self doSetCurrentPage:value]) {
return;
}
setNextViewOnCompletion = YES;
animating = YES;
self.nextView.alpha = 0;
[UIView beginAnimations:#"" context:Nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
self.nextView.alpha = 1;
[UIView commitAnimations];
}
- (void) setCurrentPage:(NSInteger) value animated:(BOOL) animated {
if (![self doSetCurrentPage:value]) {
return;
}
setNextViewOnCompletion = YES;
animating = YES;
if (animated) {
[self initFlip];
[self performSelector:#selector(flipPage) withObject:Nil afterDelay:0.091];
} else {
[self animationDidStop:Nil finished:[NSNumber numberWithBool:NO] context:Nil];
}
}
#synthesize dataSource;
- (void) setDataSource:(NSObject <AFKPageFlipperDataSource>*) value {
if (dataSource) {
dataSource = nil;
}
dataSource = value;
numberOfPages = [dataSource numberOfPagesForPageFlipper:self];
currentPage = 0;
self.currentPage = 1;
}
#synthesize disabled;
- (void) setDisabled:(BOOL) value {
disabled = value;
self.userInteractionEnabled = !value;
for (UIGestureRecognizer *recognizer in self.gestureRecognizers) {
recognizer.enabled = !value;
}
}
#pragma mark -
#pragma mark Touch management
- (void) tapped:(UITapGestureRecognizer *) recognizer {
if (animating || self.disabled) {
return;
}
if (recognizer.state == UIGestureRecognizerStateRecognized) {
NSInteger newPage;
if ([recognizer locationInView:self].y < (self.bounds.size.height - self.bounds.origin.y) / 2) {
newPage = MAX(1, self.currentPage - 1);
} else {
newPage = MIN(self.currentPage + 1, numberOfPages);
}
[self setCurrentPage:newPage animated:YES];
}
}
- (void) panned:(UIPanGestureRecognizer *) recognizer {
if (animating) {
return;
}
static BOOL hasFailed;
static BOOL initialized;
static NSInteger oldPage;
float translation = [recognizer translationInView:self].y;
float progress = translation / self.bounds.size.height;
if (flipDirection == AFKPageFlipperDirectionTop) {
progress = MIN(progress, 0);
} else {
progress = MAX(progress, 0);
}
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
hasFailed = FALSE;
initialized = FALSE;
animating = NO;
setNextViewOnCompletion = NO;
break;
case UIGestureRecognizerStateChanged:
if (hasFailed) {
return;
}
if (!initialized) {
oldPage = self.currentPage;
if (translation > 0) {
if (self.currentPage > 1) {
[self doSetCurrentPage:self.currentPage - 1];
} else {
hasFailed = TRUE;
return;
}
} else {
if (self.currentPage < numberOfPages) {
[self doSetCurrentPage:self.currentPage + 1];
} else {
hasFailed = TRUE;
return;
}
}
hasFailed = NO;
initialized = TRUE;
setNextViewOnCompletion = NO;
[self initFlip];
}
[self setFlipProgress:fabs(progress) setDelegate:NO animate:NO];
break;
case UIGestureRecognizerStateFailed:
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
break;
case UIGestureRecognizerStateRecognized:
if (hasFailed) {
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
return;
}
if (fabs((translation + [recognizer velocityInView:self].y / 4) / self.bounds.size.height) > 0.5) {
setNextViewOnCompletion = YES;
[self setFlipProgress:1.0 setDelegate:YES animate:YES];
} else {
[self setFlipProgress:0.0 setDelegate:YES animate:YES];
currentPage = oldPage;
}
break;
default:
break;
}
}
#pragma mark -
#pragma mark Frame management
/*
- (void) setFrame:(CGRect) value {
super.frame = value;
numberOfPages = [dataSource numberOfPagesForPageFlipper:self];
if (self.currentPage > numberOfPages) {
self.currentPage = numberOfPages;
}
}*/
#pragma mark -
#pragma mark Initialization and memory management
+ (Class) layerClass {
return [CATransformLayer class];
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[self initRecognizers];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder: aDecoder])) {
[self initRecognizers];
}
return self;
}
- (void) initRecognizers {
_tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
_panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panned:)];
[_tapRecognizer requireGestureRecognizerToFail:_panRecognizer];
[self addGestureRecognizer:_tapRecognizer];
[self addGestureRecognizer:_panRecognizer];
}
#end
Can anybody help me with the sample code for embedding a zoomable scroll view within a paging scroll view so that each page can be zoomed and panned individually?
Also the navigation structure is like tab bar controller --> navigation controller with buttons (on push of the buttons) --> view controller where in the horizontal scrollview of images has to be implemented with pagination and zooming.
Here is the link to the tutorial that I followed: How To Use UIScrollView to Scroll and Zoom Content
The code I've implemented is following:
#import "ViewCorpBrochureController.h"
#interface ViewCorpBrochureController ()
#property (nonatomic, strong) NSArray *pageImages;
#property (nonatomic, strong) NSMutableArray *pageViews;
#property (nonatomic, strong) UIImageView *imageView;
- (void)loadVisiblePages;
- (void)loadPage:(NSInteger)page;
- (void)purgePage:(NSInteger)page;
- (void)centerScrollViewContents;
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer;
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer;
#end
#implementation ViewCorpBrochureController
#synthesize scrollView = _scrollView;
#synthesize pageControl = _pageControl;
#synthesize pageImages = _pageImages;
#synthesize pageViews = _pageViews;
#synthesize imageView = _imageview;
- (void)centerScrollViewContents {
CGSize boundsSize = self.scrollView.bounds.size;
CGRect contentsFrame = self.imageView.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
contentsFrame.origin.y = 0.0f;
}
self.imageView.frame = contentsFrame;
}
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
// Get the location within the image view where we tapped
CGPoint pointInView = [recognizer locationInView:self.imageView];
// Get a zoom scale that's zoomed in slightly, capped at the maximum zoom scale specified by the scroll view
CGFloat newZoomScale = self.scrollView.zoomScale * 1.5f;
newZoomScale = MIN(newZoomScale, self.scrollView.maximumZoomScale);
// Figure out the rect we want to zoom to, then zoom to it
CGSize scrollViewSize = self.scrollView.bounds.size;
CGFloat w = scrollViewSize.width / newZoomScale;
CGFloat h = scrollViewSize.height / newZoomScale;
CGFloat x = pointInView.x - (w / 2.0f);
CGFloat y = pointInView.y - (h / 2.0f);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
}
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer {
// Zoom out slightly, capping at the minimum zoom scale specified by the scroll view
CGFloat newZoomScale = self.scrollView.zoomScale / 1.5f;
newZoomScale = MAX(newZoomScale, self.scrollView.minimumZoomScale);
[self.scrollView setZoomScale:newZoomScale animated:YES];
}
#pragma mark -
- (void)loadVisiblePages {
// First, determine which page is currently visible
CGFloat pageWidth = self.scrollView.frame.size.width;
NSInteger page = (NSInteger)floor((self.scrollView.contentOffset.x * 2.0f + pageWidth) / (pageWidth * 2.0f));
// Update the page control
self.pageControl.currentPage = page;
// Work out which pages we want to load
NSInteger firstPage = page - 1;
NSInteger lastPage = page + 1;
// Purge anything before the first page
for (NSInteger i=0; i<firstPage; i++) {
[self purgePage:i];
}
for (NSInteger i=firstPage; i<=lastPage; i++) {
[self loadPage:i];
}
for (NSInteger i=lastPage+1; i<self.pageImages.count; i++) {
[self purgePage:i];
}
}
- (void)loadPage:(NSInteger)page {
if (page < 0 || page >= self.pageImages.count) {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Load an individual page, first seeing if we've already loaded it
UIView *pageView = [self.pageViews objectAtIndex:page];
if ((NSNull*)pageView == [NSNull null]) {
CGRect frame = self.scrollView.bounds;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0.0f;
UIImageView *newPageView = [[UIImageView alloc] initWithImage:[self.pageImages objectAtIndex:page]];
newPageView.contentMode = UIViewContentModeScaleAspectFit;
newPageView.frame = frame;
[self.scrollView addSubview:newPageView];
[self.pageViews replaceObjectAtIndex:page withObject:newPageView];
}
}
- (void)purgePage:(NSInteger)page {
if (page < 0 || page >= self.pageImages.count) {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Remove a page from the scroll view and reset the container array
UIView *pageView = [self.pageViews objectAtIndex:page];
if ((NSNull*)pageView != [NSNull null]) {
[pageView removeFromSuperview];
[self.pageViews replaceObjectAtIndex:page withObject:[NSNull null]];
}
}
#pragma mark -
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"CorporateBrochure";
self.pageImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"CB1.jpg"],
[UIImage imageNamed:#"CB2.jpg"],
[UIImage imageNamed:#"CB3.jpg"],
[UIImage imageNamed:#"CB4.jpg"],
[UIImage imageNamed:#"CB5.jpg"],
[UIImage imageNamed:#"CB6.jpg"],
nil];
NSInteger pageCount = self.pageImages.count;
// Set up the page control
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = pageCount;
// Set up the array to hold the views for each page
self.pageViews = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < pageCount; ++i) {
[self.pageViews addObject:[NSNull null]];
}
/* If I use this the subview appears below the image
UIImage *image = self.imageView.image;
//UIImage *image = [UIImage imageNamed:#"CB2.jpg"];
self.imageView = [[UIImageView alloc] initWithImage:image];
self.imageView.frame = (CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=image.size};
[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = image.size;
*/
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:doubleTapRecognizer];
UITapGestureRecognizer *twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewTwoFingerTapped:)];
twoFingerTapRecognizer.numberOfTapsRequired = 1;
twoFingerTapRecognizer.numberOfTouchesRequired = 2;
[self.scrollView addGestureRecognizer:twoFingerTapRecognizer];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGSize pagesScrollViewSize = self.scrollView.frame.size;
self.scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * self.pageImages.count, pagesScrollViewSize.height);
[self loadVisiblePages];
}
- (void)viewDidUnload {
[super viewDidUnload];
self.scrollView = nil;
self.pageControl = nil;
self.pageImages = nil;
self.pageViews = nil;
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
// Return the view that we want to zoom
return self.imageView;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationMaskAll);
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// Load the pages which are now on screen
[self loadVisiblePages];
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
// The scroll view has zoomed, so you need to re-center the contents
[self centerScrollViewContents];
}
#end
Is there a specific issue you are having with Ray Wenderlich's tutorial?
Apple's documentation has a very good example of how to implement a paging scroll view, see the Photo Scroller sample code here. I would suggest starting there.
From the above link:
"PhotoScroller" demonstrates the use of embedded UIScrollViews and
CATiledLayer to create a rich user experience for displaying and
paginating photos that can be individually panned and zoomed.
Note: If your images are not overly large, you can get away with not using CATiledLayer.
EDIT 1: See my answer to this question about how to modify Apple's Photo Scroller code to make the UIPageViewController a subview of your own view controller (and not the rootViewController).
EDIT 2: See this sample project on github.
I wish to show the FBLoginView in a landscape Cocos2D layer. Originally with just having it added to the view it was thinking the view was portrait, but by adding
[loginview setTransform:CGAffineTransformMakeRotation(-M_PI / 2)];
I was able to have it rotate to landscape as I needed. Perfect. However, the problem arises when you click on it again to Log Out. The ActionSheet that appears is very warped and I can't press any buttons on it (see below).
http://www.dansinclair.co.uk/SO/FBLoginView_error.png
I also get an entry in my log when this happens;
Presenting action sheet clipped by its superview. Some controls might not respond to touches. On iPhone try -[UIActionSheet showFromTabBar:] or -[UIActionSheet showFromToolbar:] instead of -[UIActionSheet showInView:].
To my knowledge, it's not easy to customise the calls to and from the FBLoginView.
Any ideas/thoughts/advice would be great!
Ok, so I've found the solution.
Seeing as I'm using Cocos2D it was difficult (i.e. not standard) to use a UIKit element on the CCLayer. Thus, I had to implement CCUIViewWrapper and all is working fine.
Here's my code for CCUIViewWrapper to work with Cocos2D 2.X and ARC;
CCUIViewWrapper.h
#import "cocos2d.h"
#interface CCUIViewWrapper : CCSprite
{
UIView *uiItem;
float rotation;
}
#property (nonatomic, retain) UIView *uiItem;
+ (id) wrapperForUIView:(UIView*)ui;
- (id) initForUIView:(UIView*)ui;
- (void) updateUIViewTransform;
#end
CCUIViewWrapper.m
#import "CCUIViewWrapper.h"
#implementation CCUIViewWrapper
#synthesize uiItem;
+ (id) wrapperForUIView:(UIView*)ui
{
return [[self alloc] initForUIView:ui];
}
- (id) initForUIView:(UIView*)ui
{
if((self = [self init]))
{
self.uiItem = ui;
return self;
}
return nil;
}
-(void)setParent:(CCNode *)parent {
if(parent == nil) {
[uiItem removeFromSuperview];
} else if(uiItem != nil) {
[[[CCDirector sharedDirector] view] addSubview:uiItem];
}
[super setParent:parent];
}
-(void)updateUIViewTransform {
float thisAngle, pAngle;
CGAffineTransform transform = CGAffineTransformMakeTranslation(0, [[CCDirector sharedDirector] winSize].height);
for(CCNode *p = self; p != nil; p = p.parent) {
thisAngle = CC_DEGREES_TO_RADIANS(p.rotation);
if(p.ignoreAnchorPointForPosition)
transform = CGAffineTransformTranslate(transform, p.anchorPointInPoints.x, p.anchorPointInPoints.y);
if(p.parent != nil) {
pAngle = CC_DEGREES_TO_RADIANS(p.parent.rotation);
transform = CGAffineTransformTranslate(transform,
(p.position.x * cosf(pAngle))+(p.position.y * sinf(pAngle)),
(-p.position.y * cosf(pAngle))+(p.position.x * sinf(pAngle)));
}
else {
transform = CGAffineTransformTranslate(transform, p.position.x, -p.position.y);
}
transform = CGAffineTransformRotate(transform, thisAngle);
transform = CGAffineTransformScale(transform, p.scaleX, p.scaleY);
transform = CGAffineTransformTranslate(transform, -p.anchorPointInPoints.x, -p.anchorPointInPoints.y);
}
[uiItem setTransform:transform];
}
- (void) setVisible:(BOOL)v
{
[super setVisible:v];
[uiItem setHidden:!v];
}
- (void) setRotation:(float)protation
{
[super setRotation:protation];
[self updateUIViewTransform];
}
- (void) setScaleX:(float)sx
{
[super setScaleX:sx];
[self updateUIViewTransform];
}
- (void) setScaleY:(float)sy
{
[super setScaleY:sy];
[self updateUIViewTransform];
}
- (void) setOpacity:(GLubyte)opacity
{
[uiItem setAlpha:opacity/255.0];
[super setOpacity:opacity];
}
- (void) setContentSize:(CGSize)size
{
[super setContentSize:size];
uiItem.frame = CGRectMake(0, 0, self.contentSize.width, self.contentSize.height);
uiItem.bounds = CGRectMake(0, 0, self.contentSize.width, self.contentSize.height);
}
- (void) setAnchorPoint:(CGPoint)pnt
{
[super setAnchorPoint:pnt];
[self updateUIViewTransform];
}
- (void) setPosition:(CGPoint)pnt
{
[super setPosition:pnt];
[self updateUIViewTransform];
}
#end
I hope this helps someone else...