CALayer delegate callbacks in a UITableViewCell - Workflow - iphone

I'm using several CALayers to draw my content in a UITableViewCell subclass. The actual drawing is supposed to happen by calling the CALayer delegate method drawLayer:inContext:. However the delegate methods never gets called.
Here is my structure:
CustomCell: UITalbeViewCell
creates CustomCellContentView: UIView
creates CABackgroundLayer: CALayer
creates CATextLayer: CALayer
creates CALayerDelegate: NSObject
Each customCell has a customContentView, which holds all the CALayers for drawing.
Each customCell creates a caLayerDelegate for the CALayers to call. The caLayerDelegate reports back to the customCell.
CustomCell:
#define CALAYER_TEXTLAYER_NAME #"CATextLayer"
#define CALAYER_BGLAYER_NAME #"CABackgroundLayer"
...
- (id) customContentViewForCurrentCell
{
CGRect contentViewFrame = CGRectMake(-CC_LEFTRIGHT_PADDING, 0., self.contentView.bounds.size.width, self.contentView.bounds.size.height);
CustomCellContentView *cellContentView = [[[CustomCellContentView alloc] initWithFrame: contentViewFrame] autorelease];
CALayerDelegate *layerDelegate = [[CALayerDelegate alloc] initWithView: self];
cellContentView.layerDelegate = layerDelegate;
[cellContentView setupSubLayers];
[layerDelegate release];
return cellContentView;
}
#pragma mark - Initialisation
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (!self)
return nil;
self.customContentView = (CustomCellContentView *)[self customContentViewForCurrentCell];
self.customContentView.opaque = TRUE;
self.backgroundColor = [UIColor clearColor];
self.selectionStyle = UITableViewCellSelectionStyleNone;
[self.contentView addSubview: self.customContentView];
return self;
}
#pragma mark - Cell Update
- (void) refreshLayout
{
[self setNeedsDisplay];
[self.customContentView setNeedsDisplay];
}
...
#pragma mark - CALayer Delegate Methods
-(void) drawLayer: (CALayer*) layer inContext: (CGContextRef) context
{
}
-(void) drawCABackgroundLayer: (CALayer*) layer inContext: (CGContextRef) context
{
UIGraphicsPushContext(context);
CGRect contentRect = [layer bounds];
UIImage *bgImage = [[ImageCacheController sharedImageCache] imageFromCache: GENERIC_BGIMAGE_FILENAME];
[bgImage drawInRect: CGRectMake(contentRect.origin.x, contentRect.origin.y, contentRect.size.width, contentRect.size.height)];
UIGraphicsPopContext();
}
-(void) drawCATextLayer: (CALayer*) layer inContext: (CGContextRef) context
{
UIGraphicsPushContext(context);
CGContextSetShadowWithColor(context, CGSizeMake(0.f, 2.0f), 0.0f, [UIColor mainLabelShadowColor].CGColor);
[[UIColor mainLabelColor] set];
UIFont *mainFont = [UIFont fontWithName: FONT_INFOS size: CC_MAINLABEL_FONTSIZE_MAX];
[#"ArthurDent" drawAtPoint: [self mainViewPosition]
forWidth: CC_MAINLABEL_WIDTH
withFont: mainFont
minFontSize: CC_MAINLABEL_FONTSIZE_MAX
actualFontSize: NULL
lineBreakMode: UILineBreakModeTailTruncation
baselineAdjustment: UIBaselineAdjustmentAlignBaselines];
UIGraphicsPopContext();
}
CustomCellContentView:
#interface CustomCellContentView : UIView
{
CALayerDelegate *layerDelegate;
CALayer *backgroundLayer;
CALayer *textLayer;
}
#property (nonatomic, retain) CALayerDelegate *layerDelegate;
#property (nonatomic, retain) CALayer *backgroundLayer;
#property (nonatomic, retain) CALayer *textLayer;
- (void) setupSubLayers;
#end
#implementation CustomCellContentView
#synthesize textLayer, backgroundLayer, layerDelegate;
#pragma mark - Initialisation
- (id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame: frame];
if(!self)
return nil;
return self;
}
- (void) setupSubLayers
{
self.textLayer = [CALayer layer];
self.textLayer.name = CALAYER_TEXTLAYER_NAME;
self.textLayer.shadowColor = [UIColor mainLabelShadowColor].CGColor;
self.textLayer.shadowOffset = CGSizeMake(0.0f, 2.0f);
self.textLayer.shadowOpacity = 1.f;
self.textLayer.shadowRadius = 0.f;
self.textLayer.needsDisplayOnBoundsChange = TRUE;
self.textLayer.delegate = self.layerDelegate;
self.backgroundLayer = [CALayer layer];
self.backgroundLayer.name = CALAYER_BGLAYER_NAME;
self.backgroundLayer.needsDisplayOnBoundsChange = TRUE;
self.backgroundLayer.delegate = self.layerDelegate;
[self.layer addSublayer: self.textLayer];
[self.layer addSublayer: self.backgroundLayer];
[self.textLayer setNeedsDisplay];
[self.backgroundLayer setNeedsDisplay];
}
#pragma mark - SetNeedsDisplay Overwritten
- (void) setNeedsDisplay
{
[super setNeedsDisplay];
[self.layer setNeedsDisplay];
[self.textLayer setNeedsDisplay];
[self.backgroundLayer setNeedsDisplay];
}
...
CALayerDelegate:
#implementation CALayerDelegate
#pragma mark - Initialisation
-(id) initWithView: (UIView *) view
{
self = [super init];
if (!self)
return nil;
_view = view;
return self;
}
#pragma mark - CALayer Delegate Methods
-(void) drawLayer: (CALayer*) layer inContext: (CGContextRef) context
{
NSString* methodName = [NSString stringWithFormat: #"draw%#:inContext:", [layer name]];
SEL selector = NSSelectorFromString(methodName);
if (![_view respondsToSelector: selector])
selector = #selector(drawLayer:inContext:);
[_view performSelector: selector
withObject: layer
withObject: (id)context];
}
#end
I've read for UIViewController it is better to create sub-CALayers in awakeFromNib: and not in initWithFrame:, because there is some form of layer - backing happening after the initWithFrame:, which will discard the creation of the sublayers. So, you should only add sublayers after the view is loaded?
I use seperate CALayerDelegate - objects, so the CustomCell and its view are only responsible for the customContentView.layer.
Anyone knows, what I'm doing wrong?
Thanks in advance!!

From the documentation for CALayer describing the 'delegate' property: 'In iOS, if the layer is associated with a UIView object, this property must be set to the view that owns the layer'.
If I read that correctly then you need to set CustomCellContentView as the layer delegate. Move the functionality from CALayerDelegate directly into your custom view class and see if that works. That would be the standard way to do it anyway, rather than creating a separate class just to act as a delegate.

Check that your frame is not CGRectZero.
I had a similar issue to what you describe. If the frame is CGRectZero this will prevent the drawLayer:inContext from being called.

Related

Xcode Error Semantic error value may not respond to 'initWithFrame:image Name:'

I am getting an error in my project. I have tried plenty of solutions but to no avail.
I am getting an error Xcode Error Semantic error value may not respond to 'initWithFrame:image Name:'
I have no idea what this means and why i'm getting this warning. Please help.
Thanks
Link to my project and the Updated project link.
I'm getting the error in this line
GalleryButton *btnAttachment = [[GalleryButton alloc] initWithFrame:CGRectMake(startX, startY, width, height) imageName:imgName];
GalleryScrollView.h
#import <UIKit/UIKit.h>
#import "AttachmentItem.h"
#import "GalleryButton.h"
#protocol GAlleryScrollDelegate;
#interface GalleryScrollView : UIView <GalleryButtonDelegate>
{
id <GAlleryScrollDelegate> delegate;
// MAIN WINDOW WHERE YOU CAN DRAG ICONS
UIView *mainView;
UIScrollView *_scrollView;
NSMutableArray *_attachments;
NSInteger *_totalSize;
UIImageView *_recycleBin;
CGRect recycleBinFrame;
}
#property (nonatomic, retain) id <GAlleryScrollDelegate> delegate;
#property (nonatomic, retain) UIView *mainView;
#property (nonatomic, retain) NSMutableArray *attachments;
#property (nonatomic, retain) UIImageView *recycleBin;
#property (nonatomic, retain) UIImageView *imgName;
#property (nonatomic) CGRect recycleBinFrame;
- (void) addAttachment:(AttachmentItem *)attachment withImageNamed:(NSString *)imgName;
- (void) removeAttachment:(GalleryButton *)button;
- (void) reloadData;
- (id) initWithFrame:(CGRect)frame imageName:(NSString *)imageName;
#end
// EVENTS IF YOU WANT TO DISABLE SOME SCROLL ON DID PRESS AND ENABLE IT ON DROP
#protocol GAlleryScrollDelegate
- (void) didPressButton;
- (void) didDropButton;
#end
GalleryScrollView.m
#import <QuartzCore/QuartzCore.h>
#import "GalleryScrollView.h"
#import "GalleryButton.h"
#implementation GalleryScrollView
#synthesize delegate;
#synthesize mainView;
#synthesize attachments = _attachments;
#synthesize recycleBin = _recycleBin, recycleBinFrame;
int padding = 0;
#pragma mark - INIT
- (id) init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
- (id) initWithFrame:(CGRect)frame imageName:(NSString *)imageName
{
self = [super initWithFrame:frame];
if (self){
;
}
return self;
}
- (void) awakeFromNib
{
// INIT ATTACHMENT ARRAY
if (_attachments == nil){
_attachments = [[NSMutableArray alloc] init];
}
// SCROLL VIEW
UIScrollView *scrollTemp = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width-0, 450)];
_scrollView = scrollTemp;
_scrollView.backgroundColor = [UIColor clearColor];
// RECYCLE BIN
UIImageView *imageViewTemp = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"mozambique-wenge.png"]];
self.recycleBin = imageViewTemp;
self.recycleBin.frame = CGRectMake(0, 0, 320, 270);
[self addSubview:_scrollView];
[self addSubview:self.recycleBin];
[scrollTemp release];
[imageViewTemp release];
}
- (void) dealloc {
[super dealloc];
}
#pragma mark - ATTACHMENTS ADD / REMOVE
- (void) addAttachment:(AttachmentItem *)attachment withImageNamed:(NSString *)imgName
{
// SAVE ATTACHMENT
[_attachments addObject:attachment];
// RESIZE CONTENT VIEW FOR INSERTINT NEW ATTACHMENT
_scrollView.contentSize = CGSizeMake([_attachments count]*70, 70);
CGFloat startX = (70.0f * ((float)[_attachments count] - 1.0f) + padding);
CGFloat startY = 370;
CGFloat width = 64;
CGFloat height = 64;
GalleryButton *btnAttachment = [[GalleryButton alloc] initWithFrame:CGRectMake(startX, startY, width, height) imageName:imgName];
btnAttachment.tag = [_attachments count];
btnAttachment.scrollParent = _scrollView;
btnAttachment.mainView = self.mainView;
btnAttachment.delegate = self;
if (attachment.type == 1){
}else if (attachment.type == 2){
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
imageView.image=[UIImage imageNamed:#"mozambique-wenge"];
[btnAttachment addSubview:imageView];
[imageView release];
} else if (attachment.type == 3){
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
imageView.image=[UIImage imageNamed:#"recyclebin.png"];
[btnAttachment addSubview:imageView];
[imageView release];
}
[_scrollView addSubview:btnAttachment];
[btnAttachment release];
}
- (void) removeAttachment:(GalleryButton *)button
{
}
#pragma mark - RELOAD DATA
- (void) reloadData
{
}
#pragma mark - GALLERY BUTTON DELEGATE
-(void) touchDown
{
[self.delegate didPressButton];
}
-(void) touchUp
{
[self.delegate didDropButton];
_scrollView.scrollEnabled = YES;
}
-(BOOL) isInsideRecycleBin:(GalleryButton *)button touching:(BOOL)finished;
{
CGPoint newLoc = [self convertPoint:self.recycleBin.frame.origin toView:self.mainView];
CGRect binFrame = self.recycleBin.frame;
binFrame.origin = newLoc;
if (CGRectIntersectsRect(binFrame, button.frame) == TRUE){
if (finished){
[self removeAttachment:button];
}
return YES;
}
else {
return NO;
}
}
#end
GalleryButton.h
#import <UIKit/UIKit.h>
#protocol GalleryButtonDelegate;
#interface GalleryButton : UIView
{
id<GalleryButtonDelegate> delegate;
CGPoint _originalPosition;
CGPoint _originalOutsidePosition;
BOOL isInScrollview;
// PARENT VIEW WHERE THE VIEWS CAN BE DRAGGED
UIView *mainView;
// SCROLL VIEW WHERE YOU GONNA PUT THE THUMBNAILS
UIScrollView *scrollParent;
UIImageView *images;
}
#property (nonatomic, retain) id<GalleryButtonDelegate> delegate;
#property (nonatomic) CGPoint originalPosition;
#property (nonatomic, retain) UIView *mainView;
#property (nonatomic, retain) UIScrollView *scrollParent;
#property (nonatomic, retain) IBOutlet UIImageView *images;
#end
#protocol GalleryButtonDelegate
-(void) touchDown;
-(void) touchUp;
-(BOOL) isInsideRecycleBin:(GalleryButton *)button touching:(BOOL)finished;
#end
GalleryButton.m
#import <QuartzCore/QuartzCore.h>
#import "GalleryButton.h"
#import "GalleryScrollView.h"
#import "AttachmentItem.h"
#implementation GalleryButton
#synthesize delegate;
#synthesize originalPosition = _originalPosition;
#synthesize mainView, scrollParent;
#synthesize images;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self){
isInScrollview = YES;
self.backgroundColor = [UIColor blackColor];
self.layer.borderWidth = 2;
self.layer.borderColor = [UIColor blackColor].CGColor;
self.layer.masksToBounds = YES;
self.layer.cornerRadius = 5;
}
return self;
}
#pragma mark - DRAG AND DROP
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.delegate touchDown];
self.originalPosition = self.center;
self.scrollParent.scrollEnabled = NO;
if (isInScrollview == YES) {
CGPoint newLoc = CGPointZero;
newLoc = [[self superview] convertPoint:self.center toView:self.mainView];
_originalOutsidePosition = newLoc;
// [self.superview touchesCancelled:touches withEvent:event];
[self removeFromSuperview];
self.center = newLoc;
[self.mainView addSubview:self];
[self.mainView bringSubviewToFront:self];
isInScrollview = NO;
}
else {
;
}
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[UIView beginAnimations:#"stalk" context:nil];
[UIView setAnimationDuration:.001];
[UIView setAnimationBeginsFromCurrentState:YES];
UITouch *touch = [touches anyObject];
self.center = [touch locationInView: self.superview];
[UIView commitAnimations];
if ([delegate isInsideRecycleBin:self touching:NO]){
}
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ([delegate isInsideRecycleBin:self touching:YES]){
CGRect myImageRect = CGRectMake(0, 0, 320, 300);
images = [[UIImageView alloc] initWithFrame:myImageRect];
[images setImage:[UIImage imageNamed:#"light-cherry.png"]];
[self.mainView addSubview:images];
[self.images viewWithTag:1];
UIImageView * animation = [[UIImageView alloc] init];
animation.frame = CGRectMake(self.center.x - 32, self.center.y - 32, 40, 40);
animation.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed: #"iconEliminateItem1.png"],
[UIImage imageNamed: #"iconEliminateItem2.png"],
[UIImage imageNamed: #"iconEliminateItem3.png"],
[UIImage imageNamed: #"iconEliminateItem4.png"]
,nil];
[animation setAnimationRepeatCount:1];
[animation setAnimationDuration:0.35];
[animation startAnimating];
[self.mainView addSubview:animation];
[animation bringSubviewToFront:self.mainView];
[animation release];
;
[UIView beginAnimations:#"goback" context:nil];
[UIView setAnimationDuration:0.4f];
[UIView setAnimationBeginsFromCurrentState:YES];
self.center = _originalOutsidePosition;
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector: #selector(animationDidStop:finished:context:)];
// loadingView.frame = CGRectMake(rect.origin.x, rect.origin.y - 80, rect.size.width, rect.size.height);
[UIView commitAnimations];
} else{
[UIView beginAnimations:#"goback" context:nil];
[UIView setAnimationDuration:0.4f];
[UIView setAnimationBeginsFromCurrentState:YES];
self.center = _originalOutsidePosition;
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector: #selector(animationDidStop:finished:context:)];
// loadingView.frame = CGRectMake(rect.origin.x, rect.origin.y - 80, rect.size.width, rect.size.height);
[UIView commitAnimations];
}
[self.delegate touchUp];
}
-(void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
if ([animationID isEqualToString:#"goback"] && finished) {
[self removeFromSuperview];
self.center = _originalPosition;
[self.scrollParent addSubview:self];
isInScrollview = YES;
}
}
#end
The problem is that you haven't declared the custom init method:
- (id)initWithFrame:(CGRect)frame imageName:(NSString *)imageName;
In GalleryButton.h. You also haven't implemented it in GalleryButton.m. Perhaps you didn't mean to use this method at all in your code?
Either way you've referenced it and Xcode has correctly warned you that it doesn't exist (if you build and run the code you will get an Unrecognized selector sent to instance exception).
You need to place initWithFrame:image Name in GalleryButton in its header file. Always remember, if you get a warning that's says something like "May not respond to..." It's because it's not publicly mentioned in the classes header file.
Okay. After killing myself trying I found my mistake.
I Used:
- (id)initWithFrame:(CGRect)frame imageName:(NSString *)imageName;
In both the GalleryButton.h and GalleryScrollview.h and I just took it out of GalleryScrollview.h
Thanks All

UILabel not updating connected with IBOutlet

I am trying to update the UILabel of IBOutlet, however for some reason the content is not getting updated. Initially, the content is getting loaded from the model, which performs jsonResponse in async. However, I have set a delegate once the content is there to update the layout for which I am calling setNeedsDisplay.
Any help here would be appreciated.
Thanks.
The code below:
#interface pd ()
#property (nonatomic,retain) NSDictionary *pD;
#property (nonatomic, retain) pd_model *pdModel;
#end
#implementation pd
#synthesize pD = _pD, pdModel;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil :(NSDictionary*)pD {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.productDetails = [[NSDictionary alloc] initWithDictionary:pD];
self.pdModel = [[ProductDetails_Model alloc] init:pD];
self.pdModel.delegate = self;
NSLog(#"%#",[self.pD description]);
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
[scrollView setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:scrollView];
scrollView.showsHorizontalScrollIndicator = YES;
scrollView.scrollEnabled = YES;
// Do any additional setup after loading the view from its nib.
productRecCarousel = [[iCarousel alloc] initWithFrame:CGRectMake(0.0f, scrollView.contentSize.height, 320.0f, 100.0)];
productRecCarousel.delegate = self;
[scrollView addSubview:productRecCarousel];
[self addProductDescription];
NSLog(#"%#",[self.pdModel responseCode]);
[soldBy setText:[self.pdModel responseCode]];
//[soldBy setText:#"1"];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark
#pragma mark CarouselDelegate methods
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel {
return 1;
}
- (NSUInteger)numberOfPlaceholdersInCarousel:(iCarousel *)carousel {
return 1;
}
- (UIView*)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view {
return [[UIView alloc] init];
}
- (CGFloat)carouselItemWidth:(iCarousel *)carousel {
//usually this should be slightly wider than the item views
return 150;
}
- (CATransform3D)carousel:(iCarousel *)_carousel transformForItemView:(UIView *)view withOffset:(CGFloat)offset {
//implement 'flip3D' style carousel
//set opacity based on distance from camera
view.alpha = 1.0 - fminf(fmaxf(offset, 0.0), 1.0);
//do 3d transform
CATransform3D transform = CATransform3DIdentity;
transform.m34 = _carousel.perspective;
transform = CATransform3DRotate(transform, M_PI / 8.0, 0, 1.0, 0);
return CATransform3DTranslate(transform, 0.0, 0.0, offset * _carousel.itemWidth);
}
- (void)addProductDescription {
NSLog(#"%f",addProductDetails.frame.size.height);
addProductDetails.frame = CGRectMake(0.0f, productRecCarousel.frame.size.height, addProductDetails.frame.size.width, addProductDetails.frame.size.height);
[scrollView addSubview:addProductDetails];
}
- (void)updateTheLayout {
//[self.view setNeedsDisplay];
//This is where I get the delegate called once the model is being updated. I tried scrollView and self.view setNeedsDisplay and setNeedsLayout but the IBoutlet never gets updated which is in "ViewDidLoad as assigned as" : [soldBy setText:[self.pdModel responseCode]];
}
Thanks.
so you just have to call [soldBy setText:[self.pdModel responseCode]]; from updateTheLayout and not from viewDidLoad because it is not ready yet. And please make sure to update the UI elements from the main thread.
You have to call [soldBy setText:[self.pdModel responseCode]]; in your callback, not in the viewDidLoad method.

Adding Gesture Recognizer for panning a UIView

After seeing some examples, I'm trying to create a window where an Image view could be moved with the PanGestureRecognizer. I don't understand what is missing.
I created and initialized an UIView object
I also created an UIGestureRecognizer for panning, for both views.
I created the method to be selected when Gesture is recognized.
If you have an idea of what is missing, I would be thankful to read it;
Here is my code:
View Controller.h
#import <UIKit/UIKit.h>
#import "bouton.h"
#interface ATSViewController : UIViewController {
boutonHome *bouton; }
#property (retain, nonatomic) boutonHome *bouton;
-(IBAction)handlePanGesture:(UIPanGestureRecognizer*)sender;
#end
View Controller.m
- (void)viewDidLoad
{
[super viewDidLoad];
UIImage *image = [UIImage imageNamed:#"back.png"];
CGRect frame = CGRectMake(0, 0, image.size.width, image.size.height);
// Set self's frame to encompass the image
bouton.frame = frame;
bouton.boutonImage = image;
[self.view addSubview:bouton];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)];
[bouton setUserInteractionEnabled:YES];
[bouton addGestureRecognizer:panGesture];
[self.view setUserInteractionEnabled:YES];
[self.view addGestureRecognizer:panGesture];
// Do any additional setup after loading the view, typically from a nib.
}
-(IBAction)handlePanGesture:(UIPanGestureRecognizer *)sender
{
CGPoint translate = [sender translationInView:self.view];
CGRect newFrame = bouton.frame;
newFrame.origin.x += translate.x;
newFrame.origin.y += translate.y;
sender.view.frame = newFrame;
if(sender.state == UIGestureRecognizerStateEnded)
bouton.frame = newFrame;
}
BoutonHome.h
#import <Foundation/Foundation.h>
#interface boutonHome : UIView
{
UIImage *boutonImage;
}
#property (nonatomic, retain) UIImage *boutonImage;
// Initializer for this object
#end
boutonHome.m
#import "bouton.h"
#implementation boutonHome
#synthesize boutonImage;
#end

How to render UITableViewCell

I create UITabBar template. Then I add to firstViewController a tableView. Make connections (UITableViewDataSource and UITableViewDelegate).
In didselect method I need to render selected UITableViewCell and animate it to one of UITabBars.
If I render UITableViewCell row = 0 - it's ok, But if I render UITableViewCell row > 0 (1,2,...) I get black rectangle.
I can render UITableViewCell contentView, but it will render without background.
How can I get snapshot of UITableViewCell with a background?
#import <UIKit/UIKit.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UITabBarController *tabBarController;
#end
#import "AppDelegate.h"
#import "FirstViewController.h"
#import "SecondViewController.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize tabBarController = _tabBarController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UIViewController *viewController1 = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
UIViewController *viewController2 = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2, nil];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, strong) IBOutlet UITableView *tableViewAnimation;
#property (nonatomic, retain) UIImageView *thumbnail;
- (UIImage*)screenshotFromView:(UIView *)view;
#end
#import "FirstViewController.h"
#import <QuartzCore/QuartzCore.h>
#implementation FirstViewController
#synthesize tableViewAnimation = _tableViewAnimation;
#synthesize thumbnail = _thumbnail;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"First", #"First");
self.tabBarItem.image = [UIImage imageNamed:#"first"];
}
return self;
}
#pragma mark - UITableView DataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSUInteger count = 10;
return count;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *goodsListCellIdentifier = #"ListOfGoodsIndetifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:goodsListCellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:goodsListCellIdentifier];
cell.textLabel.backgroundColor = [UIColor clearColor];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.text = #"detalied view";
return cell;
}
#pragma mark - UITableView Delegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.backgroundColor = [UIColor redColor];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [_tableViewAnimation cellForRowAtIndexPath:indexPath];
CGRect imageViewFrame = cell.frame;
imageViewFrame.origin.y = 64.0 + tableView.frame.origin.y + imageViewFrame.origin.y - tableView.contentOffset.y;
// imageViewFrame.size.width = cell.contentView.frame.size.width;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:imageViewFrame];
imageView.image = [self screenshotFromView:cell];
[self.tabBarController.view addSubview:imageView];
self.thumbnail = imageView;
self.thumbnail.hidden = NO;
self.view.userInteractionEnabled = NO;
[UIView animateWithDuration:1.0
animations:^{
CGRect tabBarFrame = self.tabBarController.tabBar.frame;
CGFloat xoffset = tabBarFrame.size.width / 2;
[_thumbnail setCenter:CGPointMake(xoffset * 0.5, tabBarFrame.origin.y + 25.0)];
[_thumbnail setTransform:CGAffineTransformMakeScale(0.1, 0.1)];
[_thumbnail setAlpha:0.4];
}
completion:^(BOOL finished){
[self.thumbnail removeFromSuperview];
self.thumbnail = nil;
self.view.userInteractionEnabled = YES;
}];
}
- (UIImage*)screenshotFromView:(UIView *)view {
// Create a graphics context with the target size
// On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
// On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
CGSize imageSize = view.bounds.size;
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// -renderInContext: renders in the coordinate space of the layer,
// so we must first apply the layer's geometry to the graphics context
CGContextSaveGState(context);
// Center the context around the window's anchor point
CGContextTranslateCTM(context, [view center].x, [view center].y);
// Apply the window's transform about the anchor point
CGContextConcatCTM(context, [view transform]);
// Offset by the portion of the bounds left of and above the anchor point
CGContextTranslateCTM(context, -[view bounds].size.width * [[view layer] anchorPoint].x, -[view bounds].size.height * [[view layer] anchorPoint].y);
// Render the layer hierarchy to the current context
[[view layer] renderInContext:context];
// Restore the context
CGContextRestoreGState(context);
// Retrieve the screenshot image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
#end
The background is probably not rendered automatically by the cell but by the table instead.
Note that background is rendered depending on cell state (background / selected background).
You can take cell.backgroundView and render it before you render the cell.
[((UITableViewCell*) view).backgroundView.layer renderInContext:context]
But that's only a guess.
Maybe it's something simpler. When you tap a cell, selection animation is started. renderInContext ignores animations. Maybe there is a problem?
Just comment next lines:
// Center the context around the window's anchor point
// CGContextTranslateCTM(context, [view center].x, [view center].y);
// Apply the window's transform about the anchor point
// CGContextConcatCTM(context, [view transform]);
// Offset by the portion of the bounds left of and above the anchor point
// CGContextTranslateCTM(context, -[view bounds].size.width * [[view layer] anchorPoint].x, -[view bounds].size.height * [[view layer] anchorPoint].y);

Extend TTPhotoViewController with custom TTPhotoView

I have successfully integrated the three20 framework in my project,
and I have extended the TTPhotoViewController to add some further
functionality.
Now I need to add some subviews to the TTPhotoView loaded by the
TTPhotoViewController. In particular I would like to add that subviews
after every TTPhotoView as been loaded. These subviews represents
sensible area over the image so they should scale proportionally with
the image.
The user can tap a subview to get extra info about the image.
I don't know how to implement this behavior. Should I extend the
TTPhotoView and make sure that the TTPhotoViewController use this
extended version instead of its TTPhotoView?
Could someone point me to the right direction?
Thank you
Solved subclassing the TTPhotoView (TapDetectingPhotoView) and then adding all my subviews to that subclass.
The main problem was represented by the photo switching, because the TTPhotoViewController (in particular its inner TTScrollView) reuse the TTPhotoView during switching operation.
So for example if you add your subview to your TTPhotoView subclass and try to switch to the next photo, your subview will probably be here, because the TTPhotoView is reused.
To solve this problem I decided to add and remove all my subviews every time a photo switch occur (see TTPhotoViewController::didMoveToPhoto).
In this way I'm sure that every photoview has its subviews.
Here my implementation (only remarkable methods)
Hope these help!
PhotoViewController.h:
#import "TapDetectingPhotoView.h"
#interface PhotoGalleryController : TTPhotoViewController <TTScrollViewDelegate, TapDetectingPhotoViewDelegate> {
NSArray *images;
}
#property (nonatomic, retain) NSArray *images;
#end
PhotoViewController.m:
#import "PhotoGalleryController.h"
#implementation PhotoGalleryController
#synthesize images;
- (void)viewDidLoad { // fill self.images = ... }
- (TTPhotoView*)createPhotoView {
TapDetectingPhotoView *photoView = [[TapDetectingPhotoView alloc] init];
photoView.tappableAreaDelegate = self;
return [photoView autorelease];
}
#pragma mark -
#pragma mark TTPhotoViewController
- (void)didMoveToPhoto:(id<TTPhoto>)photo fromPhoto:(id<TTPhoto>)fromPhoto {
[super didMoveToPhoto:photo fromPhoto:fromPhoto];
TapDetectingPhotoView *previousPhotoView = (TapDetectingPhotoView *)[_scrollView pageAtIndex:fromPhoto.index];
TapDetectingPhotoView *currentPhotoView = (TapDetectingPhotoView *)[_scrollView pageAtIndex:photo.index];
// destroy the sensible areas from the previous photoview, because the photo could be reused by the TTPhotoViewController!
if (previousPhotoView)
previousPhotoView.sensibleAreas = nil;
// if sensible areas has not been already created, create new
if (currentPhotoView && currentPhotoView.sensibleAreas == nil) {
currentPhotoView.sensibleAreas = [[self.images objectAtIndex:photo.index] valueForKey:#"aMap"];
[self showSensibleAreas:YES animated:YES];
}
}
#pragma mark -
#pragma mark TappablePhotoViewDelegate
// show a detail view when a sensible area is tapped
- (void)tapDidOccurOnSensibleAreaWithId:(NSUInteger)ids {
NSLog(#"SENSIBLE AREA TAPPED ids:%d", ids);
// ..push new view controller...
}
TapDetectingPhotoView.h:
#import "SensibleAreaView.h"
#protocol TapDetectingPhotoViewDelegate;
#interface TapDetectingPhotoView : TTPhotoView <SensibleAreaViewDelegate> {
NSArray *sensibleAreas;
id <TapDetectingPhotoViewDelegate> tappableAreaDelegate;
}
#property (nonatomic, retain) NSArray *sensibleAreas;
#property (nonatomic, assign) id <TapDetectingPhotoViewDelegate> tappableAreaDelegate;
#end
#protocol TapDetectingPhotoViewDelegate <NSObject>
#required
- (void)tapDidOccurOnSensibleAreaWithId:(NSUInteger)ids;
#end
TapDetectingPhotoView.m:
#import "TapDetectingPhotoView.h"
#interface TapDetectingPhotoView (Private)
- (void)createSensibleAreas;
#end
#implementation TapDetectingPhotoView
#synthesize sensibleAreas, tappableAreaDelegate;
- (id)init {
return [self initWithSensibleAreas:nil];
}
- (id)initWithFrame:(CGRect)frame {
return [self initWithSensibleAreas:nil];
}
// designated initializer
- (id)initWithSensibleAreas:(NSArray *)areasList {
if (self = [super initWithFrame:CGRectZero]) {
self.sensibleAreas = areasList;
[self createSensibleAreas];
}
return self;
}
- (void)setSensibleAreas:(NSArray *)newSensibleAreas {
if (newSensibleAreas != self.sensibleAreas) {
// destroy previous sensible area and ensure that only sensible area's subviews are removed
for (UIView *subview in self.subviews)
if ([subview isMemberOfClass:[SensibleAreaView class]])
[subview removeFromSuperview];
[newSensibleAreas retain];
[sensibleAreas release];
sensibleAreas = newSensibleAreas;
[self createSensibleAreas];
}
}
- (void)createSensibleAreas {
SensibleAreaView *area;
NSNumber *areaID;
for (NSDictionary *sensibleArea in self.sensibleAreas) {
CGFloat x1 = [[sensibleArea objectForKey:#"nX1"] floatValue];
CGFloat y1 = [[sensibleArea objectForKey:#"nY1"] floatValue];
area = [[SensibleAreaView alloc] initWithFrame:
CGRectMake(
x1, y1,
[[sensibleArea objectForKey:#"nX2"] floatValue]-x1, [[sensibleArea objectForKey:#"nY2"] floatValue]-y1
)
];
areaID = [sensibleArea objectForKey:#"sId"];
area.ids = [areaID unsignedIntegerValue]; // sensible area internal ID
area.tag = [areaID integerValue];
area.delegate = self;
[self addSubview:area];
[area release];
}
}
// to make sure that if the zoom factor of the TTScrollView is > than 1.0 the subviews continue to respond to the tap events
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *result = nil;
for (UIView *child in self.subviews) {
CGPoint convertedPoint = [self convertPoint:point toView:child];
if ([child pointInside:convertedPoint withEvent:event]) {
result = child;
}
}
return result;
}
#pragma mark -
#pragma mark TapDetectingPhotoViewDelegate methods
- (void)tapDidOccur:(SensibleAreaView *)aView {
NSLog(#"tapDidOccur ids:%d tag:%d", aView.ids, aView.tag);
[tappableAreaDelegate tapDidOccurOnSensibleAreaWithId:aView.ids];
}
SensibleAreaView.h:
#protocol SensibleAreaViewDelegate;
#interface SensibleAreaView : UIView {
id <SensibleAreaViewDelegate> delegate;
NSUInteger ids;
UIButton *disclosureButton;
}
#property (nonatomic, assign) id <SensibleAreaViewDelegate> delegate;
#property (nonatomic, assign) NSUInteger ids;
#property (nonatomic, retain) UIButton *disclosureButton;
#end
#protocol SensibleAreaViewDelegate <NSObject>
#required
- (void)tapDidOccur:(SensibleAreaView *)aView;
#end
SensibleAreaView.m:
#import "SensibleAreaView.h"
#implementation SensibleAreaView
#synthesize delegate, ids, disclosureButton;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.userInteractionEnabled = YES;
UIColor *color = [[UIColor alloc] initWithWhite:0.4 alpha:1.0];
self.backgroundColor = color;
[color release];
UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[button addTarget:self action:#selector(buttonTouched) forControlEvents:UIControlEventTouchUpInside];
CGRect buttonFrame = button.frame;
// set the button position over the right edge of the sensible area
buttonFrame.origin.x = frame.size.width - buttonFrame.size.width + 5.0f;
buttonFrame.origin.y = frame.size.height/2 - 10.0f;
button.frame = buttonFrame;
button.autoresizingMask = UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin |UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight;
self.disclosureButton = button;
[self addSubview:button];
// notification used to make sure that the button is properly scaled together with the photoview. I do not want the button looks bigger if the photoview is zoomed, I want to preserve its default dimensions
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(zoomFactorChanged:) name:#"zoomFactorChanged" object:nil];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
if ([[touches anyObject] tapCount] == 1)
[delegate tapDidOccur:self];
}
- (void)buttonTouched {
[delegate tapDidOccur:self];
}
- (void)zoomFactorChanged:(NSNotification *)message {
NSDictionary *userInfo = [message userInfo];
CGFloat factor = [[userInfo valueForKey:#"zoomFactor"] floatValue];
BOOL withAnimation = [[userInfo valueForKey:#"useAnimation"] boolValue];
if (withAnimation) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.18];
}
disclosureButton.transform = CGAffineTransformMake(1/factor, 0.0, 0.0, 1/factor, 0.0, 0.0);
if (withAnimation)
[UIView commitAnimations];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"zoomFactorChanged" object:nil];
[disclosureButton release];
[super dealloc];
}
Some ideas:
Subclass TTPhotoView, then override createPhotoView in your TTPhotoViewController:
- (TTPhotoView*)createPhotoView {
return [[[MyPhotoView alloc] init] autorelease];
}
Try overriding a private method (yes, bad practice, but hey) setImage: in TTPhotoView subclass:
- (void)setImage:(UIImage*)image {
[super setImage:image]
// Add a subview with the frame of self.view, maybe?..
//
// Check for self.isLoaded (property of TTImageView
// which is subclassed by TTPhotoView) to check if
// the image is loaded
}
In general, look at the headers and implementations (for the private methods) of TTPhotoViewController and TTPhotoView. Set some breakpoints to figure out what does what :)
Interesting question. Facebook has a similar functionality with their tags. Their tags do not scale proportionally with the image. In fact, they do not even show the tags if you have zoomed. I don't know if this will help you, but I would look at how (if) three20 handles tags and then maybe try to extend that.