FBLoginView orientation - ios

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...

Related

add two custom sprites into a scene,why do these sprites' methods will effect each other,anyone can find the mistake in my code? thanks

this is custom sprite class named BackGround
#import "BackGround.h"
// -----------------------------------------------------------------
id move02;
double roadX;
#implementation BackGround
+ (instancetype)initWithPicture: (NSString *) pic
{
return [[self alloc] init:pic];
}
-(id) init: (NSString *) pic
{
if(self = [super init])
{
CCSpriteFrameCache* spriteFrameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
CCSpriteFrame * bgSpriteFrame = [spriteFrameCache spriteFrameByName:pic];
self = [BackGround spriteWithSpriteFrame:bgSpriteFrame];
self.anchorPoint = ccp(0, 0);
roadX = -(self.contentSize.width-1)*2;
self.position = ccp((-roadX/2), 0);
id move01 = [CCActionMoveBy actionWithDuration:10.0f position:ccp(roadX,0.0)];
move02 = [CCActionRepeatForever actionWithAction:move01];
[self runAction:move02];
}
return self;
}
-(void)bgWhenRun
{
[self stopAllActions];
id move = [CCActionSpeed actionWithAction:move02 speed:2];
[self runAction:move];
}
-(void)bgWhenWalk
{
[self stopAllActions];
[self runAction:move02];
}
// -----------------------------------------------------------------
#end
this is scene class code
#import "_256Deathes.h"
#import "IntroScene.h"
#import "BackGround.h"
#import "cocos2d.h"
#import "Person.h"
// -----------------------------------------------------------------
Person * personA;
#implementation _256Deathes
{
}
- (instancetype)init
{
if ((self = [super init]))
{
NSAssert(self, #"Whoops");
self.userInteractionEnabled = YES;
CCSpriteFrameCache* spriteFrameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
[spriteFrameCache addSpriteFramesWithFile:#"256Deathes.plist"];
BackGround * bgSprite01 = [BackGround initWithPicture:#"earthA.png"];
bgSprite01.position = ccp(0, 0);
[self addChild:bgSprite01 z:0 name:#"bgSpriteA"];
BackGround * bgSprite02 = [BackGround initWithPicture:#"earthA.png"];
[self addChild:bgSprite02 z:1 name:#"bgSpriteB"];
}
return self;
}
- (void)onEnter
{
// always call super onEnter first
[super onEnter];
[self schedule:#selector(updateSprite) interval:0.02];
}
-(void)touchBegan:(CCTouch *)touch withEvent:(CCTouchEvent *)event
{
BackGround *spriteA = (BackGround *)[self getChildByName:#"bgSpriteA" recursively:NO];
BackGround *spriteB = (BackGround *)[self getChildByName:#"bgSpriteB" recursively:NO];
[spriteA bgWhenRun];
[spriteB bgWhenRun];
}
-(void)touchEnded:(CCTouch *)touch withEvent:(CCTouchEvent *)event
{
BackGround *sprite;
for(sprite in [self children])
{
if([sprite.name containsString:#"bgSprite"])
{
[sprite bgWhenWalk];
}
}
}
-(void) updateSprite
{
[self updateBackGround01];
}
-(void) updateBackGround01
{
BackGround *sprite;
for(sprite in [self children])
{
if([sprite.name containsString:#"bgSprite"])
{
double nextX = sprite.contentSize.width-3;
if(sprite.position.x <= (-nextX))
{
sprite.position = ccp(nextX, 0);
}
}
}
}
// -----------------------------------------------------------------
#end
when i touch begin or touch end, spriteA will stop moving, after i tried some times, i found [self stopAllActions] in methods named bgWhenRun and bgWhenWalk can make spriteA and spriteB effect each other.
anyone can find out the mistakes in the code then tell me?i have tried many times,now i really have no idea. thank you!
These two sprites effect each other because both are using same instance of variables id move02 and double roadX as being global ones. Declare them within scope of BackGround class i.e. as member variables of that class.

crash when using a category

I create a class category of UITouch this my code :
- (id)initInView:(UIView *)view;
{
CGRect frame = view.frame;
CGPoint centerPoint = CGPointMake(frame.size.width * 0.5f, frame.size.height * 0.5f);
return [self initAtPoint:centerPoint inView:view];
}
- (id)initAtPoint:(CGPoint)point inWindow:(UIWindow *)window;
{
self = [super init];
if (self == nil) {
return nil;
}
// Create a fake tap touch
_tapCount = 1;
_locationInWindow = point;
_previousLocationInWindow = _locationInWindow;
UIView *hitTestView = [window hitTest:_locationInWindow withEvent:nil];
_window = [window retain];
_view = [hitTestView retain];
if ([self respondsToSelector:#selector(setGestureView:)]) {
[self setGestureView:hitTestView];
}
_phase = UITouchPhaseBegan;
_touchFlags._firstTouchForView = 1;
_touchFlags._isTap = 1;
_timestamp = [[NSProcessInfo processInfo] systemUptime];
return self;
}
- (id)initAtPoint:(CGPoint)point inView:(UIView *)view;
{
return [self initAtPoint:[view.window convertPoint:point fromView:view] inWindow:view.window];
}
- (void)setPhase:(UITouchPhase)phase;
{
_phase = phase;
_timestamp = [[NSProcessInfo processInfo] systemUptime];
}
but when I call it I get this crash -[UITouch initAtPoint:inView:]: unrecognized selector sent to instance
How can I fix that?
You say you created a category, but did not include the definition of your category.
It should look something like this:
//UITouch+customInitMethods.h
#interface UITouch (customInitMethods)
- (id)initInView:(UIView *)view;
- (id)initAtPoint:(CGPoint)point inWindow:(UIWindow *)window;
- (id)initAtPoint:(CGPoint)point inView:(UIView *)view;
#end
And then your implementation:
#import "UITouch+customInitMethods.h"
#implementation UITouch (customInitMethod)
//Your method implementations go here.
#end
Make sure that the target checkbox on the .m file of your category file is set to include the category in your application target.
Then you would need to #import UITouch+customInitMethods.h in any file that wanted to use your custom init methods.

How to call a method of the viewController to display something new from within a subview? (iOS)

I am following this tutorial http://guti.in/articles/creating-tinder-like-animations/ but am having a hard time figuring out:
UPDATED:
how to notify a dragged item left or right to tell the view controller display something new, how do I call a method in the view controller based on the view within it being dragged? They are two separate files viewController.m and this one...
How do I say based on this gesture
GGDraggableView.m
#import "GGDraggableView.h"
#import "GGOverlayView.h"
#interface GGDraggableView ()
#property(nonatomic, strong) UIPanGestureRecognizer *panGestureRecognizer;
#property(nonatomic) CGPoint originalPoint;
#property(nonatomic, strong) GGOverlayView *overlayView;
#end
#implementation GGDraggableView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (!self) return nil;
self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(dragged:)];
[self addGestureRecognizer:self.panGestureRecognizer];
self.overlayView = [[GGOverlayView alloc] initWithFrame:self.bounds];
self.overlayView.alpha = 0;
[self addSubview:self.overlayView];
return self;
}
- (void)dragged:(UIPanGestureRecognizer *)gestureRecognizer
{
CGFloat xDistance = [gestureRecognizer translationInView:self].x;
CGFloat yDistance = [gestureRecognizer translationInView:self].y;
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan:{
self.originalPoint = self.center;
break;
};
case UIGestureRecognizerStateChanged:{
CGFloat rotationStrength = MIN(xDistance / 320, 1);
CGFloat rotationAngel = (CGFloat) (2*M_PI/16 * rotationStrength);
CGFloat scaleStrength = 1 - fabsf(rotationStrength) / 4;
CGFloat scale = MAX(scaleStrength, 0.93);
CGAffineTransform transform = CGAffineTransformMakeRotation(rotationAngel);
CGAffineTransform scaleTransform = CGAffineTransformScale(transform, scale, scale);
self.transform = scaleTransform;
self.center = CGPointMake(self.originalPoint.x + xDistance, self.originalPoint.y + yDistance);
[self updateOverlay:xDistance];
break;
};
case UIGestureRecognizerStateEnded: {
[self resetViewPositionAndTransformations];
break;
};
case UIGestureRecognizerStatePossible:break;
case UIGestureRecognizerStateCancelled:break;
case UIGestureRecognizerStateFailed:break;
}
}
A way I call the method currently is like so but I feel like alloc, init a new controller is incorrect since this is a view within the view controller:
This is my attempt within the GGDraggableView.m file:
RandomViewController *rVC = [[RandomViewController alloc]init];
[rVC displayNewInfo];
My apologies I made a big mistake with my last response.
What you'll want to use here is a delegate in order to update your ViewController.
GCDraggableView.h
#protocol GCDraggableViewDelegate <NSObject>
-(void)GCDraggableView:(id)draggableView dragged:(UIPanGestureRecognizer*) gestureRecognizer;
#end
#interface GGDraggableView ()
#property (weak) id<GCDraggableViewDelegate> delegate;
#end
GCDraggableView.m
- (void)dragged:(UIPanGestureRecognizer *)gestureRecognizer
{
//Call protocol method on delegate in order to update ViewController
[self.delegate GCDraggableView:self dragged:gestureRecognizer];
}
ViewController.m
-(void)viewDidLoad
{
GCDraggableView *subview.delegate = self;
}
-(void)GCDraggableView:(id)draggableView dragged:(UIPanGestureRecognizer*) gestureRecognizer
{
//Whatever you want done when your subview is panned
}
You can not use the properties of viewcontroller inside view. So, if you want to add image into your imageview, you should create the memebr variable for that imageview inside GGDraggableView. Add this GGDraggableView to viewcontroller.view in viewDidLoad or through XIB/storyboard. Now assign the image to imageView by referring it like gdraggbleview.imageview.image = pics[0];

ios Custom userInterface rotation

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 :)

Bringing PopUpView in front of a cell

So, I added a popupview to my uisliders. I got the code for the custom sliders with the popup from a guy who had already done that. The popup is showing, but the only problem is that the popup is showing inside the cell in which the slider is at, so it gets cut off at the end of the cell.
How can I bring the popupview in front of the cell ?
(I have multiple sliders each one in a different cell)
#import "MNEValueTrackingSlider.h"
#import "ToothTableViewController.h"
#pragma mark - Private UIView subclass rendering the popup showing slider value
#interface MNESliderValuePopupView : UIView {
MNEValueTrackingSlider *trackingSlider;
ToothTableViewController *toothViewController;
}
#property (nonatomic) float value;
#property (nonatomic, retain) UIFont *font;
#property (nonatomic, retain) NSString *text;
#end
#import "MNEValueTrackingSlider.h"
#import "ToothTableViewController.h"
#implementation MNESliderValuePopupView
#synthesize value=_value;
#synthesize font=_font;
#synthesize text = _text;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.font = [UIFont boldSystemFontOfSize:18];
}
return self;
}
- (void)dealloc {
self.text = nil;
self.font = nil;
[super dealloc];
}
- (void)drawRect:(CGRect)rect {
// Set the fill color
[[UIColor colorWithWhite:0 alpha:0.8] setFill];
// Create the path for the rounded rectanble
CGRect roundedRect = CGRectMake(self.bounds.origin.x , self.bounds.origin.y, self.bounds.size.width, self.bounds.size.height * 0.8);
UIBezierPath *roundedRectPath = [UIBezierPath bezierPathWithRoundedRect:roundedRect cornerRadius:6.0];
// Create the arrow path
UIBezierPath *arrowPath = [UIBezierPath bezierPath];
CGFloat midX = CGRectGetMidX(self.bounds);
CGPoint p0 = CGPointMake(midX, CGRectGetMaxY(self.bounds));
[arrowPath moveToPoint:p0];
[arrowPath addLineToPoint:CGPointMake((midX - 10.0), CGRectGetMaxY(roundedRect))];
[arrowPath addLineToPoint:CGPointMake((midX + 10.0), CGRectGetMaxY(roundedRect))];
[arrowPath closePath];
// Attach the arrow path to the buble
[roundedRectPath appendPath:arrowPath];
[roundedRectPath fill];
// Draw the text
if (self.text) {
[[UIColor colorWithWhite:1 alpha:0.8] set];
CGSize s = [_text sizeWithFont:self.font];
CGFloat yOffset = (roundedRect.size.height - s.height) / 2;
CGRect textRect = CGRectMake(roundedRect.origin.x, yOffset, roundedRect.size.width, s.height);
[_text drawInRect:textRect
withFont:self.font
lineBreakMode:UILineBreakModeWordWrap
alignment:UITextAlignmentCenter];
}
}
- (void)setValue:(float)aValue {
_value = aValue;
self.text = [NSString stringWithFormat:#"%4.2f", _value];
[self setNeedsDisplay];
}
#end
#pragma mark - MNEValueTrackingSlider implementations
#import "ToothTableViewController.h"
#implementation MNEValueTrackingSlider
#synthesize thumbRect;
#synthesize sliderButtonPoint;
#pragma mark - Private methods
- (void)_constructSlider {
valuePopupView = [[MNESliderValuePopupView alloc] initWithFrame:CGRectZero];
valuePopupView.backgroundColor = [UIColor clearColor];
valuePopupView.alpha = 0.0;
toothViewController = [[ToothTableViewController alloc] init];
[self addSubview:valuePopupView];
}
- (void)_fadePopupViewInAndOut:(BOOL)aFadeIn {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
if (aFadeIn) {
valuePopupView.alpha = 1.0;
} else {
valuePopupView.alpha = 0.0;
}
[UIView commitAnimations];
}
- (void)_positionAndUpdatePopupView {
CGRect _thumbRect = self.thumbRect;
CGRect popupRect = CGRectOffset(_thumbRect, 0, -(_thumbRect.size.height * 1.5));
valuePopupView.frame = CGRectInset(popupRect, -20, -10);
valuePopupView.value = (NSInteger)self.value;
}
#pragma mark - Memory management
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self _constructSlider];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self _constructSlider];
}
return self;
}
- (void)dealloc {
[valuePopupView release];
[super dealloc];
}
#pragma mark - UIControl touch event tracking
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
// Fade in and update the popup view
CGPoint touchPoint = [touch locationInView:self];
// Check if the knob is touched. Only in this case show the popup-view
if(CGRectContainsPoint(self.thumbRect, touchPoint)) {
[self _positionAndUpdatePopupView];
[self _fadePopupViewInAndOut:YES];
}
return [super beginTrackingWithTouch:touch withEvent:event];
}
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
// Update the popup view as slider knob is being moved
[self _positionAndUpdatePopupView];
return [super continueTrackingWithTouch:touch withEvent:event];
}
- (void)cancelTrackingWithEvent:(UIEvent *)event {
[super cancelTrackingWithEvent:event];
}
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
// Fade out the popoup view
[self _fadePopupViewInAndOut:NO];
[super endTrackingWithTouch:touch withEvent:event];
}
#pragma mark - Custom property accessors
- (CGRect)thumbRect {
CGRect trackRect = [self trackRectForBounds:self.bounds];
CGRect thumbR = [self thumbRectForBounds:self.bounds
trackRect:trackRect
value:self.value];
return thumbR;
}
#end
Ok so I gave up, I cant figure it out. That is the code for the slider and its popupview. If anyone feels like reading the whole thing I could use the help :P
You could try to add the popupview as a subview of the UITableView.
Then to move it along with the slider, you would have to calculate the point by getting the slider's position relative to your tableview.
This can be achieved by using the UIView's convertPoint:toView: method, for example:
CGPoint sliderButtonRelativePoint = [slider.superview convertPoint:sliderButtonPoint toView:tableView];
where slider.superview would be your UITableViewCell, sliderButtonPoint would be the middle-top point of the slider's round button (for example), and tableView would be, well... your UITableView.
You might have to play around with this a little, and you may find there are strange behaviours when scrolling the tableview, but that's what I would try first.

Resources