xcode UITapGestureRecognizer on scrollview not calling until second tap - xcode

I have the following code to dismiss the keyboard if the user taps the background. It works fine if the scrollview is in the PointZero position, but if the user scrolls the view and then selects the textview, it doesn't call the "dismissKeyboard' method until the 2nd background tap.
On the first tap (for some reason) moves the scrollview offset to align with the scrollview frame to the screen bottom. The second tap will dismiss the keyboard and run the code below. I know it has to do with the scrollview. Any help would be appreciated.
Thanks
- (void)viewDidLoad {
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissKeyboard)];
tapGesture.cancelsTouchesInView = NO;
[_scrollView addGestureRecognizer:tapGesture];
}
-(void)dismissKeyboard {
[self.view endEditing:YES];
}
- (void)keyboardWasShown:(NSNotification *)notification {
scrollViewRect = _scrollView.contentOffset.y;
NSDictionary* info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
keyboardSize.height += 10;
CGFloat viewBottom = CGRectGetMaxY(self.scrollView.frame);
if ([_itemNotes isFirstResponder]) {
CGFloat notesBottom = CGRectGetMaxY(_itemNotes.frame);
viewBottom -= notesBottom;
if (viewBottom < keyboardSize.height) {
keyboardSize.height -= viewBottom;
CGPoint scrollPoint = CGPointMake(0.0, keyboardSize.height);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
else {
[self.scrollView setContentOffset:CGPointZero animated:YES];
}
}
else {
[self.scrollView setContentOffset:CGPointZero animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification *)notification {
CGPoint scrollPoint = CGPointMake(0.0, scrollViewRect);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
EDIT:
So I figured out a solution but it seems like there must be a better way to handle this. The problem was because I was setting the contentOffset of the scrollView so that the contentSize was beyond the screen boundaries. Thus the first tap was moving the scrollView contentOffset back within the screen boundaries and the second was performing the tap gesture. I will post my solution below hoping that someone has a better answer.

I would recommend setting
_scrollView.layer.borderColor = [UIColor redColor].CGColor;
_scrollView.layer.borderWidth = 1;
This will show you exactly where your scrollview boundaries are, which may not be where you think they are, or may be covered by something else. Also, when I open the keyboard, I generally set the scrollview frame bottom to the top of the keyboard. Otherwise, you may have content below the keyboard you can't get to. Not sure if this is exactly related to your issues.

I am assuming there must be a better solution to this but I was able to solve the issue by extending the contentSize when the keyboard is displayed and then shrinking it back down when the keyboard is hidden.
Set a float (scrollViewHeight) to hold the original content size for the reset.
//add this right before setting the content offset
scrollViewHeight = _scrollView.contentSize.height;
_scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width , scrollViewHeight + keyboardSize.height);
//add this right before reseting the content offset
_scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width , scrollViewHeight);
It really seems like there must be a better way that I'm not aware of. I will have to go back through the documentation to see if there is another way.

Related

Label on UIButton not laid out correctly with autolayout

I have an app which I'm updating to auto layout and size classes and I'm getting some weird behaviour with the label on a button.
The button should be a circle and have a label in the centre. I'm implementing my own subclass so I can reuse it.
Here's the storyboard:
and the code for the class which extends UIButton:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self setBackgroundImage:[UIImage imageNamed:#"selected-green"] forState:UIControlStateHighlighted ];
self.layer.borderColor = [UIColor tlbWhiteColor].CGColor;
self.layer.borderWidth = 10;
}
return self;
}
-(void) layoutSubviews {
self.layer.cornerRadius = self.frame.size.width / 2;
}
With this the appears as expected but there is no label. On debugging I see that the frame of the label has 0 height and width. So I extended layoutSubviews:
-(void) layoutSubviews {
self.layer.cornerRadius = self.frame.size.width / 2;
if (self.titleLabel.frame.size.width == 0) {
[self.titleLabel sizeToFit];
[self setNeedsLayout];
[self layoutIfNeeded];
}
}
The label then appears, but it's in the wrong place:
The only extra info I can offer is that in Reveal the button has weird height and width constraints added:
The titleInsets are all at 0.
Help hugely appreciated.
I don't think you need the layoutSubviews override. I think it's a hack and it's hiding your real issue.
What are the constraints on the 'Go' label? How are you centering it in the UIButton? Also what is the height constraint on the label?
My suggestion would be to put both the UIButton and the Go label inside a container view and centre the label inside the container view. The container view should have the same height and width as the UIButton inside it.
Also are you changing the frames of the views programmatically somewhere in the app? Those weird constraints you talk about in reveal point to that.
As per your requirements:
- (void)viewDidLoad {
[super viewDidLoad];
view_1.layer.cornerRadius = 10; //view_1 is white color
view_1.layer.masksToBounds = YES;
view_2.layer.cornerRadius = _view_2.frame.size.width/2;//view_2 is green color
view_2.layer.masksToBounds = YES;
}
output
Ok, this was a real school boy error.
When the docs said 'The implementation of this method is empty before iOS 6' I for some reason took this to mean there's no need to call super on layoutSubviews.
So the fix was:
-(void) layoutSubviews {
[super layoutSubviews]; // <<<<< THIS WAS MISSING
self.layer.cornerRadius = self.frame.size.width / 2;
self.layer.masksToBounds = YES;
}

user interaction enabled subviw in scrollview

I have a ScrollView that contains a ContainerView. The ContainerView contains another View that the user is supposed to be able to pan around in.
The scrollView scrolls vertical only, the "view inside the containerView" is panable in all directions.
Here is what I have
self.scrollView.contentSize = CGSizeMake(1024,1440);
self.modelController = [self.storyboard instantiateViewControllerWithIdentifier:#"LCProduct3DViewController"];
self.modelController.meshIdentifier = self.meshIdentifier;
[self addChildViewController:self.modelController];
self.modelController.view.frame = self.threeDView.bounds;
[self.threeDView addSubview:self.modelController.view];
What happens is that the touch events inside the modelController's view and the ones outside the modelControler's view but inside the scrollview bounds seems to be getting the the way of each other.
I played around with
self.scrollView.canCancelContentTouches = NO;
self.scrollView.delaysContentTouches = NO;
but havent found a working solution yet.
Any Ideas ?
Thanks in advance
as can be found in other places:
The trick is to subclass the scrollView and override the hitTest method like this:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView* result = [super hitTest:point withEvent:event];
if ([result isKindOfClass:[GLKView class]]) {
self.scrollEnabled = NO;
} else {
self.scrollEnabled = YES;
}
return result;
}
this way - if the hittest for the innver view is positive, scrolling for the scrollview is disabled and only the innver view will get the event.

Zoom & Pan in UIImageView inside UIScrollView

I have a UIScrollView filled with UIImageView. The UIScrollView are paging enabled to let the users to "flip" those images. Each UIImageView has UIPinchGestureRecognizer for pinch zoom, and UIPanGestureRecognizer for to pan that image when zoomed in.
As probably you have noticed, what I'd like to achieve is just like what iBooks does in its application.
However, I have difficult times to get this work.
In my "BookPageViewController", I have set up UIScrollView, then fill them with images from the folder based on data (page numbers, file names, etc) from sqlite.
_pageScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
NSUInteger pageCount = [pages count];
for (int i = 0; i < pageCount; i++) {
CGFloat x = i * self.view.frame.size.width;
// Page Settings
UIImageView *pageView = [[UIImageView alloc] initWithFrame:CGRectMake(x, 0, self.view.frame.size.width, self.view.frame.size.height)];
pageView.backgroundColor = [UIColor greenColor];
pageView.image = [pages objectAtIndex:i];
pageView.userInteractionEnabled = YES;
pageView.tag = i;
// Gesture Reocgnisers
UIPinchGestureRecognizer *pinchGr = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(pinchImage:)];
pinchGr.delegate = self;
[pageView addGestureRecognizer:pinchGr];
UIPanGestureRecognizer *panGr = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panImage:)];
[pageView addGestureRecognizer:panGr];
// Finally add this page to the ScrollView
[_pageScrollView addSubview:pageView];
}
_pageScrollView.contentSize = CGSizeMake(self.view.frame.size.width * pageCount, self.view.frame.size.height);
_pageScrollView.pagingEnabled = YES;
_pageScrollView.delegate = self;
[self.view addSubview:_pageScrollView];
And with the help of another good questions here in Stackoverflow, I have put those:
- (void) pinchImage:(UIPinchGestureRecognizer*)pgr {
[self adjustAnchorPointForGestureRecognizer:pgr];
if ([pgr state] == UIGestureRecognizerStateBegan || [pgr state] == UIGestureRecognizerStateChanged) {
if ([pgr scale]) {
NSLog(#"SCALE: %f", [pgr scale]);
[pgr view].transform = CGAffineTransformScale([[pgr view] transform], [pgr scale], [pgr scale]);
[pgr setScale:1];
} else {
NSLog(#"[PAMPHLET PAGE]: The image cannot be scaled.");
}
}
}
My problem is, when I zoom in the one of the UIImageView with Pinch Gesture, the image exceeds (go over) to the next image and hides it. I believe that there should be the way to "limit" the zoom in/out and the size of UIImageView (not UIImage itself), but I don't know where to go from here. I have also put a code to limit the scale something like:
([pgr scale] > 1.0f && [pgr scale] < 1.014719) || ([pgr scale] < 1.0f && [pgr scale] > 0.98f)
but it didn't work...
I know it's not hard for ios professionals, but I'm quite new to objective-c, and this is my first time to develop real application. If this is not a good practice, I also would like to know any ideas to achieve just how to do this like ibooks do (e.g. put UIImageView in UIScrollView, then put the UIScrollView to another UIScrollView)...
Sorry for another beginner question, but I really need help.
Thanks in advance.
Without trying it out myself, my first guess would be that the transform on the UIImageView also transforms its frame. One way to solve that, would be to put the UIImageView in another UIView, and put that UIView in the UIScrollView. Your gesture recognizers and the transform would still be on the UIImageView. Make sure the UIView's clipsToBounds property is set to YES.

mapView is not Zooming to Coordinates

I've looked through existing questions and believe I'm following the rules, but some reason my map is not zooming. I have put in a breakpoint to make sure my coordinates are making it through and everything seems to be OK there.
Below is my code.
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureView];
mapView = [[MKMapView alloc]initWithFrame:CGRectMake(20, 20, 260, 169)];
mapView.delegate = self;
mapView.mapType = MKMapTypeStandard;
[self.view addSubview:mapView];
}
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
Bar *maps = self.detailItem;
MKCoordinateSpan span;
span.latitudeDelta = .02;
span.longitudeDelta = .02;
CLLocationDegrees latDegree = [maps.barLat doubleValue];
CLLocationDegrees longDegree = [maps.barLong doubleValue];
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(latDegree, longDegree);
MKCoordinateRegion region;
region.center = location;
region.span = span;
[mapView setRegion:region animated:TRUE];
}
I'm not entirely clear what you're trying to do in the mapView:regionWillChangeAnimated: callback ... that gets called when the region is going to change (e.g. on user pan or zoom), and you are responding by commanding another region change. Maybe that's just for debugging?
I think your question is about why your map won't zoom, right?
If you want to be sure your map supports zoom, add this code:
mapView = [[MKMapView alloc]initWithFrame:CGRectMake(20, 20, 260, 169)];
mapView.delegate = self;
mapView.mapType = MKMapTypeStandard;
mapView.zoomEnabled = YES;
mapView.multipleTouchEnabled = YES;
however, I'm wondering if what you're seeing is just that your map is pretty small. It's less than half the screen size. On the simulator, I found it difficult to perform the pinch gesture (which causes the zoom), and even on my real device, it wasn't easy to get my two fingers into that small view area. But, on both platforms, I definitely did see that code allowing zoom (iOS 5.0).
Can you increase the frame size of your map, and make sure it wasn't just that it's hard to pinch within a small area?

CGGradient isn't visible (not using interface builder) and UIButtons can't be triggered

I have created a view that contains a CGGradient:
// Bar ContextRef
CGRect bar = CGRectMake(0, screenHeight-staffAlignment, screenWidth, barWidth);
CGContextRef barContext = UIGraphicsGetCurrentContext();
CGContextSaveGState(barContext);
CGContextClipToRect(barContext,bar);
// Bar GradientRef
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat components[16] = { 1.0,1.0,1.0,0.0, 0.0,0.0,0.0,1.0, 0.0,0.0,0.0,1.0, 1.0,1.0,1.0,0.0};
CGFloat locations[4] = {0.95,0.85,0.15,0.05};
size_t count = 4;
CGGradientRef gradientRef = CGGradientCreateWithColorComponents(colorSpace, components, locations, count);
// Draw Bar
CGPoint startPoint = {0.0,0.0};
CGPoint endPoint = {screenWidth,0.0};
CGContextDrawLinearGradient(barContext, gradientRef, startPoint, endPoint, 0);
CGContextRestoreGState(barContext);
This code is called in the drawRect method of the UIView. I then use a UIViewController to access the created view.
- (void)loadView {
MainPageView *mpView = [[MainPageView alloc] initWithFrame:[window bounds]];
[self setView:mpView];
[mpView release];
}
and displayed on the screen through the appDelegate:
mpViewController = [[MainPageViewController alloc] init];
[window addSubview:[mpViewController view]];
[window makeKeyAndVisible];
The UIView contains more objects, such as UIButtons, that are visible. I am assuming because they are added as a subview. But I can't work out how to add the CGGradient as a subview? Does it need to be? Is there another reason CGGradient is not visible?
I also don't get the functionality on the UIButtons. I guess that is because of where I have added the UIButtons to the view. Do the buttons need to be added in the UIViewController or the appDelegate to have functionality. Sorry to ask what would seem like simple questions but I am trying to accomplish the programming without the Interface Builder and material on that is scarce. If anyone could point me in the right direction on both these problems I would really appreciate it.
Thanks!
The functionality on the buttons was lost because the frame was too large but the buttons were still visible because the background was clearColor

Resources