Animating UIImageView flip from middle - ios

I'm trying for a while to build an ebook, flipping page (from left to right) and zooming.
So far I've tested a few projects but none of them was good enough to do both.
I've decided to build one of my own, so I started with basic flip animation like this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.scrollView.delegate = self;
self.scrollView.maximumZoomScale = 5.0f;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tapGesture.delegate = self;
tapGesture.numberOfTapsRequired = 1;
tapGesture.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:tapGesture];
}
#pragma mark - UIScrollView Delegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
#pragma mark - UIGestureRecognizer Delegate
- (void)handleTap:(UITapGestureRecognizer *)tapGestur
{
NSLog(#"%s", __PRETTY_FUNCTION__);
if (tapGestur.state == UIGestureRecognizerStateEnded) {
[UIView transitionWithView:self.imageView duration:1 options:UIViewAnimationOptionTransitionFlipFromRight|UIViewAnimationOptionCurveEaseInOut animations:^{
if (!flipped) {
self.imageView.image = [UIImage imageNamed:#"2-IPAD-P.jpg"];
flipped = YES;
}
else {
self.imageView.image = [UIImage imageNamed:#"1-IPAD-P.jpg"];
}
} completion:^(BOOL finished) {
//
}];
}
}
What I'm trying to do now is transition flipping page (left to right) from the middle of the image.
Any idea how can I do this using UIVIew? Or maybe better options?

You should also give a look to CATansition. Have a look at this example.
Also, have you looked at UIViewAnimationOptions's UIViewAnimationOptionTransitionCurlUp and UIViewAnimationOptionTransitionCurlDown for page animations ?

Related

Obj-C - Dismiss keyboard by tapping anywhere on screen from UITextView?

When my user taps on my UITextView (NOT uitextfield...), an animation occurs. However after the UITextView begins editing, no other UITapGesture seems to be recognized (e.g. if I add a tapGesture to my UIView to dismiss this animation, it doesn't execute at all). I've been trying to fix this for what feels like forever. Help is appreciated, I'm stumped.
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.replyField.delegate = self;
[self.replyField setUserInteractionEnabled:YES];
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(textViewTapped)];
[self.view addGestureRecognizer:gestureRecognizer];
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
-(void)textViewTapped {
NSLog(#"DISMISS PLEASE!");
[self animateTextView:NO];
}
- (void)textViewDidBeginEditing:(UITextView *)textView
{
[self animateTextView: YES];
self.replyField.gestureRecognizers = nil;
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
[self animateTextView:NO];
}
- (void) animateTextView:(BOOL) up
{
const int movementDistance = 206;
const float movementDuration = 0.3f;
int movement= movement = (up ? -movementDistance : movementDistance);
NSLog(#"%d",movement);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.inputView.frame, 0, movement);
[UIView commitAnimations];
}
- (void)textViewDidChange:(UITextView *)textView
{
}
Your problem is not related to gesture recognizer.
Problem caused by line:
self.view.frame = CGRectOffset(self.inputView.frame, 0, movement);
You set frame for self.view from self.inputView with offset.
And when self.view size is changed, its subviews (including text view) may have positions out of superview bound. And UIView doesn't respond touch event if touch point is out of superview bounds.
You can easily check this by setting background color for views and show they frames in log:
...
- (void) animateTextView:(BOOL) up
{
NSLog(#"self.view state1:%#", self.view);
NSLog(#"self.replyField state1:%#", self.replyField);
self.view.backgroundColor = [UIColor redColor];
self.replyField.backgroundColor = [UIColor blueColor];
const int movementDistance = 206;
const float movementDuration = 0.3f;
int movement= movement = (up ? -movementDistance : movementDistance);
NSLog(#"%d",movement);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
//self.view.frame = CGRectOffset(self.inputView.frame, 0, movement);
self.replyField.frame = CGRectOffset(self.replyField.frame, 0, movement);
[UIView setAnimationDidStopSelector:#selector(afterAnimationStops)];
[UIView commitAnimations];
}
- (void)afterAnimationStops {
NSLog(#"self.view state2:%#", self.view);
NSLog(#"self.replyField state2:%#", self.replyField);
}
...
Also what is self.inputView? is it properly set?
BTW: What goal of the code? Do you want to move text view to avoid overlapping by keyboard? Then consider placing it in UIScrollView.
e.g see discussion here: How to make a UITextField move up when keyboard is present?.
I have encountered similar issues use follow below code to dismiss keyboard by tapping anywhere on screen view. Hope will helpful to you.
Objective c
- (void)viewDidLoad
{
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:gestureRecognizer];
gestureRecognizer.cancelsTouchesInView = NO;
}
- (void)dismissKeyboard
{
[self.view endEditing:YES];
}
Swift 4
func viewDidLoad() {
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
view.addGestureRecognizer(gestureRecognizer)
gestureRecognizer.cancelsTouchesInView = false
}
func dismissKeyboard() {
view.endEditing(true)
}
In ViewDidLoad :
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:tap];
Create one method :
-(void)dismissKeyboard {
[textView resignFirstResponder];
}
Not clearly understand what you want BTW let me explain.
First of all if you want to hide your keyboard you can do it by single line code.
Objective C
[self.view endEditing:YES];
Swift
view.endEditing(true)
Now in your code, you added tap gesture on self.view it means if you touch main view anywhere then your gesture method will be call. If you want that my keyboard will be hide when I touch on main view then in tap gesture's method you should write above code ([self.view endEditing:YES];) But make sure keyboard will be open if you tap on your textView.
But If you want keyboard should not open when I click on UITextView then there are many ways but follow below
Objective C
UIView* dummyInputView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
myTextView.inputView = dummyInputView;
Swift
let dummyInputView = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
myTextView.inputView = dummyInputView
Here keyboard will not be open but cursor will be blinking if you want to hide cursor then you can do by below code
Objective C
myTextView.tintColor = [UIColor clearColor];
Swift
myTextView.tintColor = UIColor.clear
If you have any issue then do comment.
In viewController you can do this
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.view endEditing:Yes];
}

Slide Drawer is opening and closing on both left and right gestures iOS object c

I have been trying to develop an iOS app which have a slide drawer, similar to the navigation drawer in Android... Everything working fine except, the slide drawer can open and close using both left and right swipe gestures...
I need it only close in Left swipe gesture and open using right swipe gesture...
Adding the code below... Please consider that am very new in iOS development..
#import "mainViewController.h"
#interface mainViewController ()
#end
#implementation mainViewController
#synthesize menuDrawerWidth, menuDrawerX,recognizer_open, recognizer_close;
- (void)viewDidLoad {
[super viewDidLoad];
menuDrawer = [[[NSBundle mainBundle] loadNibNamed:#"SlideDrawer" owner:self options:nil]objectAtIndex:0];
menuDrawerWidth= 250 ;
int statusbarHeight= [UIApplication sharedApplication].statusBarFrame.size.height;
menuDrawerX= self.view.frame.origin.x- menuDrawerWidth;
menuDrawer.frame= CGRectMake(menuDrawerX, menuDrawer.frame.origin.y+statusbarHeight, menuDrawer.frame.size.width, menuDrawer.frame.size.height-statusbarHeight);
// menuDrawer.backgroundColor=[UIColor colorWithRed:0.18 green:0.09 blue:0.29 alpha:1.0];
recognizer_close= [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipes:)];
recognizer_open= [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipes:)];
recognizer_close.direction= UISwipeGestureRecognizerDirectionLeft;
recognizer_open.direction= UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:recognizer_close];
[self.view addGestureRecognizer:recognizer_open];
[self.view addSubview:menuDrawer];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
-(void) handleSwipes:(UIGestureRecognizer *) sender{
[self drawerAnimation];
}
-(void) drawerAnimation{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:-10];
CGFloat new_x = 0;
if(menuDrawer.frame.origin.x<self.view.frame.origin.x)
{
new_x= menuDrawer.frame.origin.x+menuDrawerWidth;
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:menuDrawer.bounds];
menuDrawer.layer.masksToBounds = NO;
menuDrawer.layer.shadowColor = [UIColor blackColor].CGColor;
menuDrawer.layer.shadowOffset = CGSizeMake(0.0f, 1.0f);
menuDrawer.layer.shadowOpacity = 0.5f;
menuDrawer.layer.shadowPath = shadowPath.CGPath;
}
else{
new_x= menuDrawer.frame.origin.x-menuDrawerWidth;
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:menuDrawer.bounds];
menuDrawer.layer.masksToBounds = NO;
menuDrawer.layer.shadowColor = [UIColor blackColor].CGColor;
menuDrawer.layer.shadowOffset = CGSizeMake(0.0f, 1.0f);
menuDrawer.layer.shadowOpacity = 0.0f;
menuDrawer.layer.shadowPath = shadowPath.CGPath;
}
menuDrawer.frame= CGRectMake(new_x, menuDrawer.frame.origin.y, menuDrawer.frame.size.width, menuDrawer.frame.size.height);
[UIView commitAnimations];
}
#end
You should take a look at Mutual Mobile's MMDrawerController. It's an amazing drawer library with a lot of features.
If you just need a drawer in your app, I'd recommend to use it.
If you want to learn how to do it yourself, why not having a look at their code ?

Move UIImageView from A to B

I need to move an UIImageView from x,y to x,y1.
This is what i have. I tried lots of different codes in -(void)animation, most using animateWithDuration but none seems to work. It just doesn't do anything. I must be missing something. Is .h correct? How would you make the image move? I then have to loop that, how can i do that? It's my first time with animate, I can't figure out what to do, it has to be simple. Thanks in Advance.
in .h I connected it this way
#import <UIKit/UIKit.h>
#interface ImageBeaconViewController : UIViewController {
__weak IBOutlet UIImageView *blueView;
}
#end
the in .m I've got
- (void)viewDidLoad
{
[self animation];
[super viewDidLoad];
}
....
- (void)animation{
[UIView animateWithDuration:3 animations:^{
blueView.alpha = 1;
blueView.center = CGPointMake(blueView.center.x , blueView.center.y +??);
}];
}
??= I still have to decide how much I want it to move
This way the app crashes as soon as I get to the screen with the animation
Add Method Like This:
- (void) animateToPoint:(CGPoint)point {
[UIView animateWithDuration:3 animations:^{
blueView.alpha = 1;
blueView.center = point;
}];
}
Call like this:
- (void) viewDidAppear:(BOOL)animated {
int distanceToSlide = 40; // will slide down 40px (use -40 to go up)
CGPoint targetPoint = CGPointMake(blueView.center.x, blueView.center.y + distanceToSlide);
[self animateToPoint:targetPoint];
}
NOTE
Notice that I put your animation call in viewDidAppear if you animate in viewDidLoad you won't see anything because your view isn't displayed yet.
If it's still not moving, make sure "useAutolayout" is not selected check this:
Just because I already built it, try this for tap to animate:
In your viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
tap.numberOfTapsRequired = 1;
[tap addTarget:self action:#selector(handleTap:)];
[self.view addGestureRecognizer:tap];
}
Then, use these:
- (void) handleTap:(UITapGestureRecognizer *)tap {
int distanceToSlide = 40; // will slide down 40px (use -40 to go up)
CGPoint targetPoint = CGPointMake(blueView.center.x, blueView.center.y + distanceToSlide);
[self animateToPoint:targetPoint];
}
- (void) animateToPoint:(CGPoint)point {
[UIView animateWithDuration:3 animations:^{
blueView.alpha = 1;
blueView.center = point;
}];
}
Here's a way to loop it:
#implementation ImageBeaconViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
pointA = CGPointMake(40, 40);
pointB = CGPointMake(250, 350);
blueView.center = pointA;
}
- (void) viewDidAppear:(BOOL)animated {
[self animate];
}
- (void) animate {
if (CGPointEqualToPoint(_blueView.center, pointA)) {
[self animateToPoint:pointB];
}
else {
[self animateToPoint:pointA];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) animateToPoint:(CGPoint)point {
[UIView animateWithDuration:1.0 animations:^{
_blueView.alpha = 1;
_blueView.center = point;
} completion:^(BOOL finished) {
if (finished) {
[self animate];
}
}];
}
#end
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
blueView.alpha = 1;
blueView.center = CGPointMake(blueView.center.x , blueView.center.y + 100);
} completion:^(BOOL finished) {
//if you need any thing after animation is done
}];
this is them code for animation i have

Add image to subclassed UIImageView

I am trying to add a tap gesture from a subclassed UIImageView and then control the tap from the View Controller. I am not getting any compiling errors but "addSubview" is not displaying any image. How can make the UIImageView to be displayed?
If I try to control the tap and pan gestures from the subclassed UIImageVIew I have no problems but I would like to control these functions from the View Controller
Relevant code looks like this.
UIImageView subclass    
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
//self.userInteractionEnabled = YES;
previewController = [[PreviewController alloc]init];
[previewController self];
[self addSubview:character];
// Tap Initialization code
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:(PreviewController *)self.previewController action:#selector(addCharacter:)];
[self addGestureRecognizer:tap];
self.userInteractionEnabled = YES;
}
return self;
}
View Controller
- (void)addCharacter:(UITapGestureRecognizer *)t
{
NSLog(#"add character");
imageNSArray = [NSMutableArray array];
uiImg = [UIImage imageNamed:#"homer.png"];
CGPoint loc = [t locationInView:self.view];
character = [[UIImageView alloc] initWithImage:uiImg];
character.center = loc;
[imageNSArray addObject:character];
//Locate the imageNSArray on frameImageView area only.
[self.view addSubview:character];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panCharacter:)];
[self.character addGestureRecognizer:pan];
character.userInteractionEnabled = YES;
}
put a view behind your subclassed imageView. Apply the gestureRecognition to the new View.
You can control your tap gesture from new view. Let me know if i am not clear, or if more info needed.
I think what you want is.. .you want to show a image when user taps on screen.
Do following steps:
1. drag & drop a tap gesture recognizer on default view of your view controller.
2. connect (ctrl +) gestureRecognizer with ViewController.m
Take a look at this code. (Just modified your code)
- (IBAction)onTap:(UITapGestureRecognizer *)sender {
NSLog(#"add character");
UIImage * uiImg = [UIImage imageNamed:#"sel.png"];
CGPoint loc = [sender locationInView:self.view];
UIImageView * character = [[UIImageView alloc] initWithImage:uiImg];
character.center = loc;
[self.view addSubview:character];
}
Let me know if this is not what you want…
Edit
better go for this code… No need to do above steps.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onTap:)];
[self.view addGestureRecognizer:tap];
self.view.userInteractionEnabled = YES;
}
- (void)onTap:(UITapGestureRecognizer *)sender
{
NSLog(#"add character");
UIImage * uiImg = [UIImage imageNamed:#"sel.png"];
CGPoint loc = [sender locationInView:self.view];
UIImageView * character = [[UIImageView alloc] initWithImage:uiImg];
character.center = loc;
[self.view addSubview:character];
}

How to make table scrolling work: UIControl within UITableView within UIControl

Here's the setup: I have UIControls in table cells that allow sliding from right to left for a delete function (like Clear)
The table cells live inside a UITableView.
The TableView lives inside another UIControl that allows swiping from left to right or right to left in order to change days. When this happens a new TableView gets created to the right or left of the main one, and the new one is pulled in from left or right until a threshold is met and an animation takes over and then replaces the old with the new.
In some conditions all of these interactions actually work correctly. The issue is that after a couple of interactions (table slides seem to be problematic) it becomes difficult / impossible to scroll the table.
Here is the code for the TableViewSlider (the top level UIControl that contains the TableViews). Any ideas would be greatly appreciated!
#implementation OSETableViewSlider
- (void) initialize {
self.backgroundColor = [UIColor backgroundFlatColor];
self.mainTableView = [self createUITableView];
self.mainTableView.frame = CGRectMake(0,0, self.frame.size.width, self.frame.size.height);
self.mainTableView.hidden = NO;
[self addSubview:self.mainTableView];
self.transitionInProgress = NO;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self initialize];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self) {
[self initialize];
}
return self;
}
- (UITableView *)createUITableView {
UITableView *newTable = [[UITableView alloc] init];
newTable.hidden = YES;
newTable.separatorStyle = UITableViewCellSeparatorStyleNone;
newTable.scrollEnabled = YES;
newTable.bounces = YES;
newTable.dataSource = self;
newTable.delegate = self;
newTable.backgroundColor = [UIColor backgroundFlatColor];
return newTable;
}
- (void)setFrame:(CGRect)frame {
super.frame = frame;
self.mainTableView.frame = CGRectMake(0,0, frame.size.width, frame.size.height);
}
- (void)transitionToDate:(NSDate *)date fromRight:(BOOL)rightToLeft {
[self.viewController beginTransitionToDate:date];
self.transitionTableView = [self createUITableView];
self.transitionTableView.frame = CGRectMake( rightToLeft ? self.frame.size.width : (-1 * self.frame.size.width), 0,
self.frame.size.width, self.frame.size.height );
self.transitionTableView.hidden = NO;
[self addSubview:self.transitionTableView];
self.transitionInProgress = YES;
[UIView animateWithDuration:0.2f animations:^{
self.transitionTableView.frame = CGRectMake(0,0, self.frame.size.width, self.frame.size.height);
self.mainTableView.frame = CGRectMake( rightToLeft ? (-1 * self.frame.size.width) : self.frame.size.width, 0,
self.frame.size.width, self.frame.size.height);
} completion:^(BOOL completed){
[self.viewController endTransitionDidChange:YES];
[self.mainTableView removeFromSuperview];
self.mainTableView = self.transitionTableView;
self.transitionInProgress = NO;
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.viewController activityCountForTransition:(tableView!=self.mainTableView)];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self.viewController cellNumber:indexPath.item forTransition:(tableView!=self.mainTableView)];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self.viewController cellNumber:indexPath.item forTransition:(tableView!=self.mainTableView)].frame.size.height;
}
- (void)layoutSubviews {
if(!self.transitionInProgress) {
[self.mainTableView setFrame:CGRectMake(0,0, self.frame.size.width, self.frame.size.height)];
}
}
- (BOOL) beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
NSLog(#"begin track");
self.locationInView = [touch locationInView:self.superview];
self.fingerTracking = YES;
self.fingerMoved = NO;
self.transitionTableView = nil;
return YES;
}
- (CGFloat) calcOffsetForTouch:(UITouch *)touch {
CGFloat fingerOffset = [touch locationInView:self.superview].x - self.locationInView.x;
return fingerOffset + self.startingOffset;
}
- (BOOL) continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
self.fingerMoved = YES;
CGFloat offset = [self calcOffsetForTouch:touch];
//NSLog(#"offset is: %f flarb: %f", offset, fabs(offset));
if(offset < 0 && (!self.transitioningLeft || [OSEUtils isNull:self.transitionTableView])) {
[self.viewController beginTransitionToDate:[OSEUtils daysOffset:1 fromDate:self.viewController.selectedDate withTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]]];
[self.transitionTableView removeFromSuperview];
self.transitionTableView = [self createUITableView];
self.transitioningLeft = YES;
NSLog(#"going left");
[self addSubview:self.transitionTableView];
[self.transitionTableView reloadData];
}
if(offset > 0 && (self.transitioningLeft || [OSEUtils isNull:self.transitionTableView])) {
[self.viewController beginTransitionToDate:[OSEUtils daysOffset:-1 fromDate:self.viewController.selectedDate withTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]]];
[self.transitionTableView removeFromSuperview];
self.transitionTableView = [self createUITableView];
self.transitioningLeft = NO;
NSLog(#"going right");
[self addSubview:self.transitionTableView];
[self.transitionTableView reloadData];
}
CGFloat mult = self.transitioningLeft ? 1 : -1;
if(fabs(offset) > (self.frame.size.width / 3.0f)) {
if(self.transitioningLeft) {
offset = -1 * self.frame.size.width;
} else {
offset = self.frame.size.width;
}
[UIView animateWithDuration:0.2f animations:^{
self.mainTableView.frame = CGRectMake(offset, 0, self.frame.size.width, self.frame.size.height);
self.transitionTableView.frame = CGRectMake(offset + (mult * self.frame.size.width), 0,
self.frame.size.width, self.frame.size.height);
} completion:^(BOOL finished) {
[self.mainTableView removeFromSuperview];
if(self.transitioningLeft) {
[self.viewController.daySelector jumpToNext];
} else {
[self.viewController.daySelector jumpToPrevious];
}
self.mainTableView = self.transitionTableView;
self.transitionTableView = nil;
[self.viewController endTransitionDidChange:YES];
}];
return NO;
}
self.mainTableView.frame = CGRectMake(offset, 0, self.frame.size.width, self.frame.size.height);
self.transitionTableView.frame = CGRectMake(offset + (mult * self.frame.size.width), 0, self.frame.size.width, self.frame.size.height);
self.transitionTableView.hidden = NO;
return YES;
}
- (void) endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
NSLog(#"end tableslide track");
[UIView animateWithDuration:0.2f animations:^{
self.mainTableView.frame = CGRectMake(0,0,self.frame.size.width, self.frame.size.height);
self.transitionTableView.frame = CGRectMake(((self.transitioningLeft ? 1 : -1 ) * self.frame.size.width), 0, self.frame.size.width, self.frame.size.height);
} completion:^(BOOL completion){
[self.transitionTableView removeFromSuperview];
self.transitionTableView = nil;
[self.viewController endTransitionDidChange:NO];
}];
self.fingerTracking = NO;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *tableHitTest = [self.mainTableView hitTest:point withEvent:event];
if([tableHitTest isKindOfClass:[OSECellPanelControl class]]) {
return tableHitTest;
} else {
return self;
}
}
#end
Why are you creating a new UITableView when the user swipes left or right? It would be much more efficient to use the built-in deleteRowsAtIndexPath: and insertRowsAtIndexPath: methods to animate the rows sliding in and out without having to slide in and out entire UITableView's.
You can take a look at this for a reference on animating the tableview content changes as you swipe across dates.
I don't know about your current set up, but when I tried this exact same thing with swipeable UITableViewCell's and it's superview being swipe able, I had issues with getting the interaction that I wanted. If this is contributing to your problem, you can look at detecting where the user swipes. If the user swipes within a specific offset from the left and right edge of the UITableViewCell, then it swipes the cell, anything outside of that offset (center of the cell) would have the UITableView itself swiped. You would then animate out the deleting and insertion of the new rows.
Also, by re-instancing a new UITableView each time, you are forcing your UITableViewCell's to be re-instanced each time as well. By using the delete and insert methods, you can continue to use your existing cells via the dequeueReusableCellWithIdentifier method. Each time you re-instance the UITableView, your cells are set to nil and discarded requiring the new tableview to create complete new instances of your cells. This is extremely inefficient.
You're getting in deep with touch tracking and hit detection. Use a higher level API instead - gesture recognisers. With gestures you can set precedence (this one has to fail before this one can begin).
So, you want a couple of custom gestures on the container view (which no longer needs to be a control). These fail if the initial touch point isn't at the edge of the view (within some bound). These trigger the overall table view change.
And the table cells have swipe gestures that each require the custom ones to fail. These trigger your cell deletion logic.
Your idea share a few similarities with how the iOS7 unlock screen and calendar is built. In the unlock screen, you can swipe on notifications, move the notification list up and down, and swipe to unlock. In the calendar app, you can swipe to change the current day, or swipe the header to change the week. Thus I recommend watching the WWDC 2013 Session video #217 "Exploring Scroll Views on iOS 7" that explain and demonstrate how they have done it, and how to replicate the functionality.
Basically, what you could do (and what they did for the unlock screen and calendar screen) is: Either (A) nest UIScrollViews in a UITableView OR (B) use a swipe/pan gesture delegate on every cells, and (C) nest that UITableView along with every other UITableViews representing days in a UIScrollView with paging control enabled.
(A) will allow you to expose a delete button in cells. You could either put the delete button under the UIScrollView that sits in the cell in a way that sliding the content uncover it.
(B) will allow you to track a gesture in cells, cancel it if required, and play with the cell's contentView to display a delete button.
(C) will allow you to swipe from one UITableView to another quite easily, and with paging enable you will get a free "snap into place or bounce back" animation PLUS you can easily reuse two UITableViews instead of allocating one per day.
Personally, I would go with (B) and (C).
Turns out there was a problem with my hitTest method. Replacing [self.mainTableView hitTest...] with [super hitTest...] solved my problem. I think I was messing up some coordinates that was causing this method to return nil when it should have returned a view. I'm not 100% sure exactly what is wrong with it, but it does work better now.

Resources