The Problem:
After I finish a pan gesture, the cell under the initiating tap no longer responds to selection. didSelectItemAtIndexPath will fire when tapping other cells, but not the cell that was under the pan gesture.
How can I get that cell to respond to touch input again? Alternatively, how can I check if that cell is stuck responding to an event?
Background:
I have a UICollectionView with a bunch of cells. Tapping a cell in this collection view fires didSelectItemAtIndexPath.
The collection view has a pan gesture recognizer. Panning creates an image of the view and translates it around the screen. The collection view is left underneath the other views for the duration of the pan gesture.
Theories:
It's possible that the selection is never completing due to the pan. Maybe there's a touch event hanging around in the UI. If I could clear it, maybe the cell would respond again. I tried setting [panHandler setCancelsTouchesInView:YES], but it didn't change anything.
Update 1:
In the comments, a couple people suggested that the image may be hanging around and blocking the touch. In the completion block passed to animateWithDuration, I have the following lines:
[self.regionScreenshot removeFromSuperview];
self.regionScreenshot = nil;
I verified that these lines are executed.
Another factor that leads me to believe that that is not the issue is that, of all the cells, only ones that have been used to drag are affected. Surrounding cells still respond.
Update 2: Code & Gif
- (IBAction)handlePanFrom:(UIPanGestureRecognizer*)recognizer
{
CGPoint translation = [recognizer translationInView:recognizer.view];
float transparencyLevel = 0.85;
if (recognizer.state == UIGestureRecognizerStateBegan)
{
SFCYRegion* centerRegion = self.region;
//North
if (centerRegion.regionToNorth != nil)
{
self.region = centerRegion.regionToNorth;
[self reloadRegion];
self.regionToNorthScreenshot = [self.collectionView snapshotViewAfterScreenUpdates:YES];
}
else
{
self.regionToNorthScreenshot = [[UIView alloc] init];
self.regionToNorthScreenshot.backgroundColor = [UIColor darkGrayColor];
}
[self.view.superview addSubview:self.regionToNorthScreenshot];
self.regionToNorthScreenshot.alpha = transparencyLevel;
self.regionToNorthScreenshot.frame = northFrame;
//East
if (centerRegion.regionToEast != nil)
{
self.region = centerRegion.regionToEast;
[self reloadRegion];
self.regionToEastScreenshot = [self.collectionView snapshotViewAfterScreenUpdates:YES];
}
else
{
self.regionToEastScreenshot = [[UIView alloc] init];
self.regionToEastScreenshot.backgroundColor = [UIColor darkGrayColor];
}
[self.view.superview addSubview:self.regionToEastScreenshot];
self.regionToEastScreenshot.alpha = transparencyLevel;
self.regionToEastScreenshot.frame = eastFrame;
//South
if (centerRegion.regionToSouth != nil)
{
self.region = centerRegion.regionToSouth;
[self reloadRegion];
self.regionToSouthScreenshot = [self.collectionView snapshotViewAfterScreenUpdates:YES];
}
else
{
self.regionToSouthScreenshot = [[UIView alloc] init];
self.regionToSouthScreenshot.backgroundColor = [UIColor darkGrayColor];
}
[self.view.superview addSubview:self.regionToSouthScreenshot];
self.regionToSouthScreenshot.alpha = transparencyLevel;
self.regionToSouthScreenshot.frame = southFrame;
//West
if (centerRegion.regionToWest != nil)
{
self.region = centerRegion.regionToWest;
[self reloadRegion];
self.regionToWestScreenshot = [self.collectionView snapshotViewAfterScreenUpdates:YES];
}
else
{
self.regionToWestScreenshot = [[UIView alloc] init];
self.regionToWestScreenshot.backgroundColor = [UIColor darkGrayColor];
}
[self.view.superview addSubview:self.regionToWestScreenshot];
self.regionToWestScreenshot.alpha = transparencyLevel;
self.regionToWestScreenshot.frame = westFrame;
//Northeast
if (centerRegion.regionToNorth != nil && centerRegion.regionToNorth.regionToEast != nil)
{
self.region = centerRegion.regionToNorth.regionToEast;
[self reloadRegion];
self.regionToNortheastScreenshot = [self.collectionView snapshotViewAfterScreenUpdates:YES];
}
else
{
self.regionToNortheastScreenshot = [[UIView alloc] init];
self.regionToNortheastScreenshot.backgroundColor = [UIColor darkGrayColor];
}
[self.view.superview addSubview:self.regionToNortheastScreenshot];
self.regionToNortheastScreenshot.alpha = transparencyLevel;
self.regionToNortheastScreenshot.frame = northeastFrame;
//Southeast
if (centerRegion.regionToSouth != nil && centerRegion.regionToSouth.regionToEast != nil)
{
self.region = centerRegion.regionToSouth.regionToEast;
[self reloadRegion];
self.regionToSoutheastScreenshot = [self.collectionView snapshotViewAfterScreenUpdates:YES];
}
else
{
self.regionToSoutheastScreenshot = [[UIView alloc] init];
self.regionToSoutheastScreenshot.backgroundColor = [UIColor darkGrayColor];
}
[self.view.superview addSubview:self.regionToSoutheastScreenshot];
self.regionToSoutheastScreenshot.alpha = transparencyLevel;
self.regionToSoutheastScreenshot.frame = southeastFrame;
//Southwest
if (centerRegion.regionToSouth != nil && centerRegion.regionToSouth.regionToWest != nil)
{
self.region = centerRegion.regionToSouth.regionToWest;
[self reloadRegion];
self.regionToSouthwestScreenshot = [self.collectionView snapshotViewAfterScreenUpdates:YES];
}
else
{
self.regionToSouthwestScreenshot = [[UIView alloc] init];
self.regionToSouthwestScreenshot.backgroundColor = [UIColor darkGrayColor];
}
[self.view.superview addSubview:self.regionToSouthwestScreenshot];
self.regionToSouthwestScreenshot.alpha = transparencyLevel;
self.regionToSouthwestScreenshot.frame = southwestFrame;
//Northwest
if (centerRegion.regionToNorth != nil && centerRegion.regionToNorth.regionToWest != nil)
{
self.region = centerRegion.regionToNorth.regionToWest;
[self reloadRegion];
self.regionToNorthwestScreenshot = [self.collectionView snapshotViewAfterScreenUpdates:YES];
}
else
{
self.regionToNorthwestScreenshot = [[UIView alloc] init];
self.regionToNorthwestScreenshot.backgroundColor = [UIColor darkGrayColor];
}
[self.view.superview addSubview:self.regionToNorthwestScreenshot];
self.regionToNorthwestScreenshot.alpha = transparencyLevel;
self.regionToNorthwestScreenshot.frame = northwestFrame;
//Self
self.region = centerRegion;
[self reloadRegion];
self.regionScreenshot = [self.collectionView snapshotViewAfterScreenUpdates:YES];
self.regionScreenshot.frame = mainFrame;
self.regionScreenshot.center = mainFrameCenter;
[self.view.superview addSubview:self.regionScreenshot];
self.collectionView.alpha = 0;
}
else if (recognizer.state == UIGestureRecognizerStateChanged)
{
//Track the movement:
self.regionScreenshot.center = [self movePoint:mainFrameCenter
byX:translation.x andY:translation.y];
self.regionToNorthScreenshot.center = [self movePoint:northFrameCenter
byX:translation.x andY:translation.y];
self.regionToEastScreenshot.center = [self movePoint:eastFrameCenter
byX:translation.x andY:translation.y];
self.regionToSouthScreenshot.center = [self movePoint:southFrameCenter
byX:translation.x andY:translation.y];
self.regionToWestScreenshot.center = [self movePoint:westFrameCenter byX:translation.x andY:translation.y];
self.regionToNortheastScreenshot.center = [self movePoint:northeastFrameCenter
byX:translation.x andY:translation.y];
self.regionToSoutheastScreenshot.center = [self movePoint:southeastFrameCenter
byX:translation.x andY:translation.y];
self.regionToSouthwestScreenshot.center = [self movePoint:southwestFrameCenter
byX:translation.x andY:translation.y];
self.regionToNorthwestScreenshot.center = [self movePoint:northwestFrameCenter
byX:translation.x andY:translation.y];
//[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
}
else if (recognizer.state == UIGestureRecognizerStateEnded)
{
SFCYRegion* newRegion;
UIView* viewToMoveToCenter;
CGSize slideVector;
NSInteger movedByX = 0;
NSInteger movedByY = 0;
float thresholdPercentage = 0.4;
BOOL isOverThresholdToNorth = translation.y > (mainFrame.size.height * thresholdPercentage);
BOOL isOverThresholdToSouth = -translation.y > (mainFrame.size.height * thresholdPercentage);
BOOL isOverThresholdToEast = -translation.x > (mainFrame.size.width * thresholdPercentage);
BOOL isOverThresholdToWest = translation.x > (mainFrame.size.width * thresholdPercentage);
if (isOverThresholdToNorth && self.region.regionToNorth != nil)
{
movedByY = -1;
if (isOverThresholdToEast && self.region.regionToNorth.regionToEast != nil)
{
//Northeast
newRegion = self.region.regionToNorth.regionToEast;
viewToMoveToCenter = self.regionToNortheastScreenshot;
movedByX = 1;
}
else if (isOverThresholdToWest && self.region.regionToNorth.regionToWest != nil)
{
//Northwest
newRegion = self.region.regionToNorth.regionToWest;
viewToMoveToCenter = self.regionToNorthwestScreenshot;
movedByX = -1;
}
else
{
//North
newRegion = self.region.regionToNorth;
viewToMoveToCenter = self.regionToNorthScreenshot;
}
}
else if (isOverThresholdToSouth && self.region.regionToSouth != nil)
{
movedByY = 1;
if (isOverThresholdToEast && self.region.regionToSouth.regionToEast != nil)
{
//Southeast
newRegion = self.region.regionToSouth.regionToEast;
viewToMoveToCenter = self.regionToSoutheastScreenshot;
movedByX = 1;
}
else if (isOverThresholdToWest && self.region.regionToSouth.regionToWest != nil)
{
//Southwest
newRegion = self.region.regionToSouth.regionToWest;
viewToMoveToCenter = self.regionToSouthwestScreenshot;
movedByX = -1;
}
else
{
//South
newRegion = self.region.regionToSouth;
viewToMoveToCenter = self.regionToSouthScreenshot;
}
}
else if (isOverThresholdToEast && self.region.regionToEast != nil)
{
//East
newRegion = self.region.regionToEast;
viewToMoveToCenter = self.regionToEastScreenshot;
movedByX = 1;
}
else if (isOverThresholdToWest && self.region.regionToWest != nil)
{
//West
newRegion = self.region.regionToWest;
viewToMoveToCenter = self.regionToWestScreenshot;
movedByX = -1;
}
else
{
//None, so return to start:
newRegion = self.region;
viewToMoveToCenter = self.regionScreenshot;
}
slideVector = CGSizeMake(mainFrameCenter.x - viewToMoveToCenter.center.x,
mainFrameCenter.y - viewToMoveToCenter.center.y);
[UIView animateWithDuration:0.2
animations:^
{
viewToMoveToCenter.alpha = 1;
if (![self.regionScreenshot isEqual:viewToMoveToCenter])
{
self.regionScreenshot.alpha = transparencyLevel;
}
self.regionScreenshot.center = [self movePoint:self.regionScreenshot.center
byX:slideVector.width andY:slideVector.height];
self.regionToNorthScreenshot.center = [self movePoint:self.regionToNorthScreenshot.center
byX:slideVector.width andY:slideVector.height];
self.regionToEastScreenshot.center = [self movePoint:self.regionToEastScreenshot.center
byX:slideVector.width andY:slideVector.height];
self.regionToSouthScreenshot.center = [self movePoint:self.regionToSouthScreenshot.center
byX:slideVector.width andY:slideVector.height];
self.regionToWestScreenshot.center = [self movePoint:self.regionToWestScreenshot.center
byX:slideVector.width andY:slideVector.height];
self.regionToNortheastScreenshot.center = [self movePoint:self.regionToNortheastScreenshot.center
byX:slideVector.width andY:slideVector.height];
self.regionToSoutheastScreenshot.center = [self movePoint:self.regionToSoutheastScreenshot.center
byX:slideVector.width andY:slideVector.height];
self.regionToSouthwestScreenshot.center = [self movePoint:self.regionToSouthwestScreenshot.center
byX:slideVector.width andY:slideVector.height];
self.regionToNorthwestScreenshot.center = [self movePoint:self.regionToNorthwestScreenshot.center
byX:slideVector.width andY:slideVector.height];
}
completion:^(BOOL finished)
{
if (finished)
{
//Remove the old views:
self.collectionView.alpha = 1;
[self.regionScreenshot removeFromSuperview];
[self.regionToNorthScreenshot removeFromSuperview];
[self.regionToEastScreenshot removeFromSuperview];
[self.regionToSouthScreenshot removeFromSuperview];
[self.regionToWestScreenshot removeFromSuperview];
[self.regionToNortheastScreenshot removeFromSuperview];
[self.regionToSoutheastScreenshot removeFromSuperview];
[self.regionToSouthwestScreenshot removeFromSuperview];
[self.regionToNorthwestScreenshot removeFromSuperview];
self.regionScreenshot = nil;
self.regionToNorthScreenshot = nil;
self.regionToEastScreenshot = nil;
self.regionToSouthScreenshot = nil;
self.regionToWestScreenshot = nil;
self.regionToNortheastScreenshot = nil;
self.regionToSoutheastScreenshot = nil;
self.regionToSouthwestScreenshot = nil;
self.regionToNorthwestScreenshot = nil;
}
}];
if (![self.regionScreenshot isEqual:viewToMoveToCenter])
{
self.region = newRegion;
[self reloadRegion];
[self movedRegionByX:movedByX andY:movedByY];
}
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
}
}
Here is an image demonstrating the issue. I perform a selection on the "Suburb" cell, then pan, and then am no longer able to perform a selection on that cell, but can still select others:
Update 3: All Gestures
This occurs when I do swipe gestures and pinch gestures as well. If I add in two-finger swipe gestures, both cells under the origin points are affected.
Make sure that regionScreenshot and any other view's you add to the view hierarchy have userInteractionEnabled set to NO.
Also set your cell and its content's clipsToBounds set to YES. Sometimes views look OK but actually they are "much smaller than they appear".
When I was panning, I called [self.collectionView reloadData] in the process of generating the screenshots to use for the adjacent regions. Reloading the cells while a gesture was active seemed to interfere with that individual cell's ability to handle gestures.
As a workaround, I instead added a second, hidden UICollectionView to the controller and load the data into that view and take screenshots of it instead of the main view. The main collection view never has to reload the data until the gestures are complete and a new region is selected.
UIView* aScreenshot = [self.secondaryCollectionView snapshotViewAfterScreenUpdates:YES];
Related
This is about a web browser.
I have a custom Class that handles the webpages.
SNBrowserView
SNBrowserViewPage
SNBrowserViewPages have two objects.
WKWebView
UIImageView // Snapshot of the WKWebView
A function either sustains or recovers a page for memory management.
(Testing) Whenever a page is selected I call a recovery function.
Selection:
- (void)browserView:(SNBrowserView *)browserView didSelectPage:(SNBrowserViewPage *)page
{
if (page.sustained) {
[page recoverAnimated:NO];
}
}
Sustain:
- (void)sustain
{
_sustained = YES;
if (_webView) {
_webView = nil;
[_webView removeFromSuperview];
}
_snapshotView = [[UIImageView alloc] init];
_snapshotView.frame = CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height);
_snapshotView.image = _snapshot;
[self addSubview:_snapshotView];
}
Recover:
- (void)recoverAnimated:(BOOL)animated
{
_sustained = NO;
_webView = [[WKWebView alloc] init];
_webView.frame = CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height);
_webView.navigationDelegate = self;
_webView.UIDelegate = self;
[self addSubview:_webView];
[self sendSubviewToBack:_webView];
[self loadURL:_initialURL]; // Start loading as early as possible.
if (animated) {
[UIView animateWithDuration:0.3
animations:^{
_snapshotView.alpha = 0.0;
}
completion:^(BOOL finished){
_snapshotView = nil;
[_snapshotView removeFromSuperview];
}];
}
else {
_snapshotView = nil;
[_snapshotView removeFromSuperview];
}
}
When I try to recover a page the snapshotView is not set to nil nor is it removed from the superview.
How is that even possible?
Even this won't work:
- (void)recoverAnimated:(BOOL)animated
{
_snapshotView = nil;
[_snapshotView removeFromSuperview];
}
The snapshotView is a subview, removeFromSuperview should always work, why is there MORE to it?
I suggest you try replace all of your
_view = nil;
[_view removeFromSuperview];
with
[_view removeFromSuperview];
_view = nil;
because what you are doing is setting the _view to nil and then removing nil from superview.
I have a pickerview that is getting its information from a separate AFPickerView file. I'm getting the error "Implicit conversion of 'int' to 'UIColor*' is disallowed with ARC". Any suggestions on an easy fix for this?
Here is the code that it effects:
[visibleViews minusSet:recycledViews];
// add missing pages
for (int index = firstNeededViewIndex; index <= lastNeededViewIndex; index++)
{
if (![self isDisplayingViewForIndex:index])
{
UILabel *label = (UILabel *)[self dequeueRecycledView];
if (label == nil)
{
label = [[UILabel alloc] initWithFrame:CGRectMake(_rowIndent, 0, self.frame.size.width - _rowIndent, 39.0)];
label.backgroundColor = [UIColor clearColor];
label.font = self.rowFont;
label.textColor = RGBACOLOR(0.0, 0.0, 0.0, 0.75);
}
[self configureView:label atIndex:index];
[contentView addSubview:label];
[visibleViews addObject:label];
}
}
}
Here is the full code:
#import "AFPickerView.h"
#implementation AFPickerView
#pragma mark - Synthesization
#synthesize dataSource;
#synthesize delegate;
#synthesize selectedRow = currentRow;
#synthesize rowFont = _rowFont;
#synthesize rowIndent = _rowIndent;
#pragma mark - Custom getters/setters
- (void)setSelectedRow:(int)selectedRow
{
if (selectedRow >= rowsCount)
return;
currentRow = selectedRow;
[contentView setContentOffset:CGPointMake(0.0, 39.0 * currentRow) animated:NO];
}
- (void)setRowFont:(UIFont *)rowFont
{
_rowFont = rowFont;
for (UILabel *aLabel in visibleViews)
{
aLabel.font = _rowFont;
}
for (UILabel *aLabel in recycledViews)
{
aLabel.font = _rowFont;
}
}
- (void)setRowIndent:(CGFloat)rowIndent
{
_rowIndent = rowIndent;
for (UILabel *aLabel in visibleViews)
{
CGRect frame = aLabel.frame;
frame.origin.x = _rowIndent;
frame.size.width = self.frame.size.width - _rowIndent;
aLabel.frame = frame;
}
for (UILabel *aLabel in recycledViews)
{
CGRect frame = aLabel.frame;
frame.origin.x = _rowIndent;
frame.size.width = self.frame.size.width - _rowIndent;
aLabel.frame = frame;
}
}
#pragma mark - Initialization
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// setup
[self setup];
// backgound
UIImageView *bacground = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"pickerBackground.png"]];
[self addSubview:bacground];
// content
contentView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, frame.size.width, frame.size.height)];
contentView.showsHorizontalScrollIndicator = NO;
contentView.showsVerticalScrollIndicator = NO;
contentView.delegate = self;
[self addSubview:contentView];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTap:)];
[contentView addGestureRecognizer:tapRecognizer];
// shadows
UIImageView *shadows = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"pickerShadows.png"]];
[self addSubview:shadows];
// glass
UIImage *glassImage = [UIImage imageNamed:#"pickerGlass.png"];
glassImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 76.0, glassImage.size.width, glassImage.size.height)];
glassImageView.image = glassImage;
[self addSubview:glassImageView];
}
return self;
}
- (void)setup
{
_rowFont = [UIFont boldSystemFontOfSize:24.0];
_rowIndent = 30.0;
currentRow = 0;
rowsCount = 0;
visibleViews = [[NSMutableSet alloc] init];
recycledViews = [[NSMutableSet alloc] init];
}
#pragma mark - Buisness
- (void)reloadData
{
// empry views
currentRow = 0;
rowsCount = 0;
for (UIView *aView in visibleViews)
[aView removeFromSuperview];
for (UIView *aView in recycledViews)
[aView removeFromSuperview];
visibleViews = [[NSMutableSet alloc] init];
recycledViews = [[NSMutableSet alloc] init];
rowsCount = [dataSource numberOfRowsInPickerView:self];
[contentView setContentOffset:CGPointMake(0.0, 0.0) animated:NO];
contentView.contentSize = CGSizeMake(contentView.frame.size.width, 39.0 * rowsCount + 4 * 39.0);
[self tileViews];
}
- (void)determineCurrentRow
{
CGFloat delta = contentView.contentOffset.y;
int position = round(delta / 39.0);
currentRow = position;
[contentView setContentOffset:CGPointMake(0.0, 39.0 * position) animated:YES];
[delegate pickerView:self didSelectRow:currentRow];
}
- (void)didTap:(id)sender
{
UITapGestureRecognizer *tapRecognizer = (UITapGestureRecognizer *)sender;
CGPoint point = [tapRecognizer locationInView:self];
int steps = floor(point.y / 39) - 2;
[self makeSteps:steps];
}
- (void)makeSteps:(int)steps
{
if (steps == 0 || steps > 2 || steps < -2)
return;
[contentView setContentOffset:CGPointMake(0.0, 39.0 * currentRow) animated:NO];
int newRow = currentRow + steps;
if (newRow < 0 || newRow >= rowsCount)
{
if (steps == -2)
[self makeSteps:-1];
else if (steps == 2)
[self makeSteps:1];
return;
}
currentRow = currentRow + steps;
[contentView setContentOffset:CGPointMake(0.0, 39.0 * currentRow) animated:YES];
[delegate pickerView:self didSelectRow:currentRow];
}
#pragma mark - recycle queue
- (UIView *)dequeueRecycledView
{
UIView *aView = [recycledViews anyObject];
if (aView)
[recycledViews removeObject:aView];
return aView;
}
- (BOOL)isDisplayingViewForIndex:(NSUInteger)index
{
BOOL foundPage = NO;
for (UIView *aView in visibleViews)
{
int viewIndex = aView.frame.origin.y / 39.0 - 2;
if (viewIndex == index)
{
foundPage = YES;
break;
}
}
return foundPage;
}
- (void)tileViews
{
// Calculate which pages are visible
CGRect visibleBounds = contentView.bounds;
int firstNeededViewIndex = floorf(CGRectGetMinY(visibleBounds) / 39.0) - 2;
int lastNeededViewIndex = floorf((CGRectGetMaxY(visibleBounds) / 39.0)) - 2;
firstNeededViewIndex = MAX(firstNeededViewIndex, 0);
lastNeededViewIndex = MIN(lastNeededViewIndex, rowsCount - 1);
// Recycle no-longer-visible pages
for (UIView *aView in visibleViews)
{
int viewIndex = aView.frame.origin.y / 39 - 2;
if (viewIndex < firstNeededViewIndex || viewIndex > lastNeededViewIndex)
{
[recycledViews addObject:aView];
[aView removeFromSuperview];
}
}
[visibleViews minusSet:recycledViews];
// add missing pages
for (int index = firstNeededViewIndex; index <= lastNeededViewIndex; index++)
{
if (![self isDisplayingViewForIndex:index])
{
UILabel *label = (UILabel *)[self dequeueRecycledView];
if (label == nil)
{
label = [[UILabel alloc] initWithFrame:CGRectMake(_rowIndent, 0, self.frame.size.width - _rowIndent, 39.0)];
label.backgroundColor = [UIColor clearColor];
label.font = self.rowFont;
label.textColor = RGBACOLOR(0.0, 0.0, 0.0, 0.75);
}
[self configureView:label atIndex:index];
[contentView addSubview:label];
[visibleViews addObject:label];
}
}
}
- (void)configureView:(UIView *)view atIndex:(NSUInteger)index
{
UILabel *label = (UILabel *)view;
label.text = [dataSource pickerView:self titleForRow:index];
CGRect frame = label.frame;
frame.origin.y = 39.0 * index + 78.0;
label.frame = frame;
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[self tileViews];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (!decelerate)
[self determineCurrentRow];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self determineCurrentRow];
}
#end
This should work with ARC. Replace RGBACOLOR with colorWithRed:green:blue:alpha
label.textColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.75f];
I have the following code that successfully hides the bottom toolbar when NOT at the top of the web view as shown in attached images. (below)
What I am trying to do is hide the toolbar completely and then expand the web view to take up the extra space (similar to how Safari does this). Any help would be great!
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.url != nil)
{
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[StoreWebViewController checkAndPrependProtocolForUrl:self.url]]]];
self.webView.delegate = self;
}
for (id subview in self.webView.subviews)
{
if ([[subview class] isSubclassOfClass: [UIScrollView class]])
{
UIScrollView * s = (UIScrollView*)subview;
originalDelegate = s.delegate;
s.delegate = self;
break;
}
}
}
- (void)scrollViewDidScroll :(UIScrollView *)scrollView
{
if (scrollView.contentOffset.y == 0) //Show toolbar when at top
{
[self.toolbar setHidden:NO];
}
else
{
[self.toolbar setHidden:YES];
}
[originalDelegate scrollViewDidScroll: scrollView];
}
I have made this in an app, using text area, so i think its the same by replacing textView with webView.
Initialize directions & flags
#implementation YourViewController{
BOOL tap;
BOOL hideNav;
BOOL mustShowNav;
}
#synthesize webView;
typedef enum ScrollDirection {
ScrollDirectionNone,
ScrollDirectionRight,
ScrollDirectionLeft,
ScrollDirectionUp,
ScrollDirectionDown,
} ScrollDirection;
viewDidLoad
- (void)viewDidLoad
{
webView.delegate = self;
hideNav = NO;
mustShowNav = NO;
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(webViewTapped:)];
gestureRecognizer.delegate = self;
[self.webView addGestureRecognizer:gestureRecognizer];
}
The scroll
- (void)scrollViewDidScroll:(UIScrollView*)scrollView
{
ScrollDirection scrollDirection;
if (lastContentOffset.y < scrollView.contentOffset.y)
scrollDirection = ScrollDirectionDown;
else
scrollDirection = ScrollDirectionUp;
float endScrolling = scrollView.contentOffset.y + scrollView.frame.size.height;
if (scrollDirection == ScrollDirectionDown && scrollView.contentOffset.y > 50 && !mustShowNav) {
hideNav = YES;
tap = 0;
} else {
hideNav = NO;
}
if (scrollDirection == ScrollDirectionUp && mustShowNav){
hideNav = NO;
mustShowNav = NO;
}
if (scrollDirection == ScrollDirectionDown && endScrolling > scrollView.contentSize.height - 50 && !mustShowNav) {
mustShowNav = YES;
}
[[self navigationController] setToolbarHidden:hideNav animated:YES];
lastContentOffset = scrollView.contentOffset;
[originalDelegate scrollViewDidScroll: scrollView];
}
The tap gesture
- (void)webViewTapped:(id)sender
{
if(!tap){
hideNav = NO;
tap = 1;
} else {
hideNav = YES;
tap = 0;
}
[[self navigationController] setToolbarHidden:hideNav animated:YES];
}
Hope this helps you.
The best solution would be to let your Javascript trigger those events and the hosting view controller should handle them.
When the user pans their finger over the specific cell, the cell changes color depending on the x-axis of the user. How would I go about changing from a pan gesture to a swipe. I would like to build it so that instead of panning, swiping on the cell reveals the color behind it. The cell should then snap back into place when the user lifts their finger. Any tips?
#synthesize _panRecognizer;
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier])
{
NSInteger emoteY = floor((self.frame.size.height - 32) / 2);
_panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(respondToPanGesture:)];
[_panRecognizer setDelegate:self];
[_panRecognizer setMinimumNumberOfTouches:1];
[_panRecognizer setMaximumNumberOfTouches:1];
[self addGestureRecognizer:_panRecognizer];
// [[self textLabel] setFont:[UIFont boldSystemFontOfSize:18.0]];
[[self textLabel] setFont:[UIFont systemFontOfSize:24.0]];
[self setSelectionStyle:UITableViewCellSelectionStyleNone];
_border = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, self.frame.size.height)];
[self addSubview:_border];
_rightEmote = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.width - 42, emoteY, 32, 32)];
[self addSubview:_rightEmote];
_leftEmote = [[UIImageView alloc] initWithFrame:CGRectMake(-32, emoteY, 32, 32)];
[self addSubview:_leftEmote];
}
return self;
}
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
CGRect borderFrame = _border.frame,
rightEmoteFrame = _rightEmote.frame,
leftEmoteFrame = _leftEmote.frame;
NSInteger emoteY = floor((self.frame.size.height - 32) / 2);
borderFrame.size.height = self.frame.size.height;
rightEmoteFrame.origin.y = emoteY;
leftEmoteFrame.origin.y = emoteY;
[_border setFrame:borderFrame];
[_rightEmote setFrame:rightEmoteFrame];
[_leftEmote setFrame:leftEmoteFrame];
}
- (DDFactor *)factor
{
return _factor;
}
- (void)setFactor:(DDFactor *)factor
{
_factor = factor;
}
- (void)respondToPanGesture:(UIGestureRecognizer *)recognizer
{
// NSLog(#"%lu", [recognizer state]);
NSInteger rstate = [recognizer state];
CGFloat touchX = 0.0;
if ([recognizer numberOfTouches] == 1)
{
touchX = [recognizer locationOfTouch:0 inView:self].x;
if (rstate == UIGestureRecognizerStateBegan)
{
/*
animate to color under touch
*/
CGRect labelFrame = [self textLabel].frame;
labelFrame.origin.x += 42;
CGRect rightEmoteFrame = _rightEmote.frame;
rightEmoteFrame.origin.x += 42;
CGRect leftEmoteFrame = _leftEmote.frame;
leftEmoteFrame.origin.x += 42;
[UIView animateWithDuration:0.3 animations:^{
[_border setAlpha:0.0];
[[self textLabel] setTextColor:[UIColor whiteColor]];
[[self textLabel] setFrame:labelFrame];
[_rightEmote setFrame:rightEmoteFrame];
[_leftEmote setFrame:leftEmoteFrame];
}];
[self animateEmoticonsWithColor:NO duration:0.3];
}
else if (rstate == UIGestureRecognizerStateChanged)
{
/*
alter color
trigger emote animation if necessary
*/
if ([self responseForTouchPosition:touchX] != _responseValue)
{
[self setResponseValue:[self responseForTouchPosition:touchX]];
[_border setBackgroundColor:[UIColor colorForResponse:_responseValue]];
[self animateToBackgroundColor:[UIColor colorForResponse:_responseValue]];
[self animateEmoticonsWithColor:NO duration:0.2];
}
}
}
else if (([recognizer numberOfTouches] == 0) && (rstate == 3))
{
CGRect labelFrame = [self textLabel].frame;
labelFrame.origin.x -= 42;
CGRect rightEmoteFrame = _rightEmote.frame;
rightEmoteFrame.origin.x -= 42;
CGRect leftEmoteFrame = _leftEmote.frame;
leftEmoteFrame.origin.x -= 42;
[UIView animateWithDuration:0.3 animations:^{
[_border setAlpha:1.0];
[_rightEmote setFrame:rightEmoteFrame];
[_leftEmote setFrame:leftEmoteFrame];
[[self textLabel] setFrame:labelFrame];
[[self textLabel] setTextColor:[UIColor colorForResponse:_responseValue]];
[self setBackgroundColor:[[UIColor colorForResponse:_responseValue] colorWithAlphaComponent:0.1]];
}];
[self animateEmoticonsWithColor:YES duration:0.3];
}
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (DDResponseValue)responseForTouchPosition:(CGFloat)x
{
DDResponseValue response;
if (x <= 70) response = DDResponseGrin;
else if (x <= 120) response = DDResponseSmile;
else if (x <= 170) response = DDResponseSad;
else if (x <= 220) response = DDResponseNervous;
else if (x <= 270) response = DDResponseRefusal;
else response = DDResponseNeutral;
return response;
}
You can use the UISwipeGestureRecognizer state and can do as what you want. Have a look on the following code,
- (void)swipeGestureMethod:(UISwipeGestureRecognizer *)recognizer {
CGPoint point = [recognizer locationInView:[recognizer view]];
if (recognizer.state == UIGestureRecognizerStateBegan) {
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
}
}
I'm adding a new subview
self.editView = [[EditView alloc] initWithFrame:CGRectMake(0,0,320,296) WithImageView:self.mainImageView.image];
self.mainImageView.hidden=YES;
[self.view addSubview:self.editView];
The New View is:
- (id)initWithFrame:(CGRect)frame WithImageView: (UIImage *) image
{
self = [super initWithFrame:frame];
if (self) {
self.panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
self.rotationRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(handleRotation:)];
self.pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handlePinch:)];
self.tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
self.panRecognizer.cancelsTouchesInView = NO;
self.panRecognizer.delegate = self;
[self.frameView addGestureRecognizer:self.panRecognizer];
self.rotationRecognizer.cancelsTouchesInView = NO;
self.rotationRecognizer.delegate = self;
[self.frameView addGestureRecognizer:self.rotationRecognizer];
self.pinchRecognizer.cancelsTouchesInView = NO;
self.pinchRecognizer.delegate = self;
[self.frameView addGestureRecognizer:self.pinchRecognizer];
self.tapRecognizer.numberOfTapsRequired = 2;
[self.frameView addGestureRecognizer:self.tapRecognizer];
[self updateCropRect];
[self reset:NO];
self.retainedImage = image;
self.sourceImage = image;
self.imageView.image = self.previewImage;
if(self.previewImage != self.sourceImage) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
CGImageRef hiresCGImage = NULL;
CGFloat aspect = self.sourceImage.size.height/self.sourceImage.size.width;
CGSize size;
if(aspect >= 1.0) { //square or portrait
size = CGSizeMake(kMaxUIImageSize*aspect,kMaxUIImageSize);
} else { // landscape
size = CGSizeMake(kMaxUIImageSize,kMaxUIImageSize*aspect);
}
hiresCGImage = [self newScaledImage:self.sourceImage.CGImage withOrientation:self.sourceImage.imageOrientation toSize:size withQuality:kCGInterpolationDefault];
// dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = [UIImage imageWithCGImage:hiresCGImage scale:1.0 orientation:UIImageOrientationUp];
CGImageRelease(hiresCGImage);
// });
});
}
}
return self;
}
Also there are custom setter methods for images.
- (UIImage *)previewImage
{
if(_previewImage == nil && _sourceImage != nil) {
if(self.sourceImage.size.height > kMaxUIImageSize || self.sourceImage.size.width > kMaxUIImageSize) {
CGFloat aspect = self.sourceImage.size.height/self.sourceImage.size.width;
CGSize size;
if(aspect >= 1.0) { //square or portrait
size = CGSizeMake(kPreviewImageSize,kPreviewImageSize*aspect);
} else { // landscape
size = CGSizeMake(kPreviewImageSize,kPreviewImageSize*aspect);
}
_previewImage = [self scaledImage:self.sourceImage toSize:size withQuality:kCGInterpolationLow];
} else {
//self.previewImage = [self.sourceImage retain];
_previewImage = self.retainedImage;
}
}
return _previewImage;
}
I cannot load the image in the subview. I understand the properties wont be set on init. But how do I set them. I tried using another property retainedImage, and assigning it to preview image in its setter method, but not working.
This is a custom library found here
The properties for an object will be set to nil when the object is created. If you want to change the properties to other values, you can set them in any methods.
There are many possible reasons why you didn't see the image.
self.imageView is nil
self.imageView.image is nil
the size of self.imageView is 0
self.imageView is hidden
self.imageView is not attached to any views
self.imageView is occluded by other views
From your description, it is hard to tell which one is the reason behind it.