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-indeleteRowsAtIndexPath:
andinsertRowsAtIndexPath:
methods to animate the rows sliding in and out without having to slide in and out entireUITableView
'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.