I have some side UIView that is in the screen size .
This side UIView, is placed on the left where its X is at -screen.width , so we dont see him .
Than , in this side view class view, i am adding a small square UIView, at its right , at screen.width , that means on the main view i can see on the left , this little square from the side view .
This structure is to get a side menu effect .
In this little square view, there is a UIButton , but this button is not responding to touches .
I do not want to place this side menu where it is a few pixels inside the screen, because this may cover some content .
Hence, i created this structure where the side menu is in the screen size,it is placed on
minus screen size.width
and has this little square that comes into the screen ,and when you push it, the whole side view comes in .
Why is this button is disabled ? is that because he is hang in the air, where part of it is not inside its superview ?
EDIT:
Found this great answer which is not working for me
interaction beyond bounds of uiview
I have added this to the parent view that contains the button , and it does log the touch,but not working.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGFloat radius = 200.0;
CGRect frame = CGRectMake(self.frame.size.width, 0,
radius,
radius);
if (CGRectContainsPoint(frame, point))
{
NSLog(#"log");
return self;
}
return nil;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (CGRectContainsPoint(self.bounds, point) ||
CGRectContainsPoint(menus.frame, point))
{
return YES;
}
return NO;
}
Thanks Aaron, I found that indeed this solution does works :
Interaction beyond bounds of UIView
But I found that a better way to do that side menu without covering the content of the main screen, is to set the height of the side menu to be very small, and set its x to appear a little bit inside the main scene.
Then when open it, just a moment before bringing it to cover the main scene, change its height to be in the screen height, this way, you don't cover the content when the menu is closed, but when you open it you change its height to be full height.
Related
I have an iPad app that uses a horizontal scroll view with a bunch of controls as subviews. The user has to be able to use all the controls inside the scrollview and only scroll the view if they deliberately drag a finger from outside the left or right edge of the screen.
I've implemented this by putting this code in the UIScrollView's pointInside:forEvent: method.
- (BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// scroll the view if the user's finger has dragged from off-screen.
[self setScrollEnabled:(point.x - self.contentOffset.x < 9 || point.x > self.contentOffset.x + self.frame.size.width - 9) ||
self.isDecelerating];
return [super pointInside:point withEvent:event];
}
When the iPad is in UIInterfaceOrientationLandscapeRight (home button on right side of the screen), I can drag my fingers from outside the right side or left side inwards and the UIScrollView scrolls as intended. However, when I switch orientations, dragging from the left side (home button) scrolls fine while the right side (near the camera) only works about 50% of the time.
I've tried extending the frame of the scroll view to be slightly outside the bounds of the screen, but I still only have problems with this type of gesture on that one side on UIInterfaceOrientationLandscapeLeft. Ideas?
EDIT: I added two UIScreenEdgePanGestureRecognizers for both sides of the screen — the gesture is more responsive now, but dragging from the right side of the screen while the home button is on the left is still sketchy at best. I have no idea why this would be happening for just that interface orientation.
In a what would be a (yet another) word game for iOS I have a scroll view holding an image view with game board.
And underneath I have draggable custom views representing letter tiles.
For the scroll view I've added a double tap gesture recognizer for toggling its zoomScale (between 50% and 100% image width):
This works well, but I had to disable Autolayout (because otherwise scroll view's contentOffset would jump to {0,0} when I dragged the tiles).
So I had to add the following code to ViewController.m to adjust scroll view's frame after device rotation:
- (void) adjustSubViews
{
_scrollView.frame = CGRectMake(0,
0,
self.view.bounds.size.width,
self.view.bounds.size.height - kHeight - 2 * kPadding);
}
this seems to work too - but my problem is that after first device rotation to landscape and back "something breaks" and the tiles are suddenly draggable by touching underneath them:
Why does it happen please?
Below is an Xcode screenshot (here fullscreen) showing the scroll view properties:
UPDATE: I've added size logging to the Tile.m:
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
NSLog(#"%s %#", __PRETTY_FUNCTION__, NSStringFromCGSize(self.frame.size));
[_smallImage setHidden:YES];
[_smallLetter setHidden:YES];
[_smallValue setHidden:YES];
[_bigImage setHidden:NO];
[_bigLetter setHidden:NO];
[_bigValue setHidden:NO];
[self.superview bringSubviewToFront:self];
}
and while initially it logs the expected tile size of 45x45 (set by me in Tile.xib):
-[Tile touchesBegan:withEvent:] {45, 45}
After I rotate the device to landscape and back to portrait I suddenly get the height of 160:
-[Tile touchesBegan:withEvent:] {45, 160}
The tile becomes draggable blow its image because the view containing the tile is auto-resized by it's superview (controller's view) that changes its frame during rotation.
This behaviour can be easily avoided by setting autoresizing mask of the tile view to UIViewAutoresizingNone (by deselecting all springs and struts in Interface Builder).
Note the tools to debug problems with view sizing like this one:
LLDB command po viewVariableOrAddress (calls description method of its parameter and prints the result).
-[UIView recursiveDescription] called from LLDB by the following command: po [viewVariableOrAddress recursiveDescription]. Note that his method is private and must not be called in production code (the app will be rejected otherwise).
Reveal — in a natural GUI, represents the same information that you can get at runtime in debugger console.
I am trying to develop a new custom UIView (to allow for horizontal date selection). I want to do all the UI design in XIB files.
The custom UI view contains a scrollview and then two 'week' views. The idea is that as the scrolling occurs, I will move the two 'week' views in place and reconfigure them to the right dates to create an 'infinite' scroll for date selections.
I can load the UIView, which then loads scrollview and week views (all designed in a XIB).
My DatePickerView class, derived from the UIView class does an addSubview of the scroll view (which contains the two week views). The scroll view is 320 wide and the contentSize is set to 640 wide. UserInteraction is enabled. Horizonal Scrolling is enabled.
This all works and displays on the screen. The week views each contain 7 buttons. I can press them and they get the touch. However, the scrollview does not seem to want to scroll.
I set my custom view to be a UIScrollViewDelegate. No calls occur to scrollViewDidScroll.
For each of the week views, I have a 'container' view and then the buttons. I added the following to the container view (again derived from a UIView).
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
DDLogInfo(#"Began. Next Responder: %#", self.nextResponder);
[self.nextResponder touchesBegan:touches withEvent:event];
}
(and comparable ones for the other touch events, touchesMoved, touchesEnded, touchesCancelled),
I print out the nextResponder, which is the UIScrollView, so I know that I am sending the touch to the view, but I never see the scrollview want to scroll.
Is my method of passing the touchEvents up the responder chain correct?
Is there anything else I need to configure to get the scrolling to work?
Any help is appreciated.
Charlie
If I understand correctly, you want infinite scroll with just three pages of scroll view. I achieved it with similar effects in my calendar view project.
You can checkout from here DPCalendar
In a nutshell, I created a view like
#interface DPCalendarMonthlyView : UIScrollView
And initial it like this
self.showsHorizontalScrollIndicator = NO;
self.clipsToBounds = YES;
self.contentInset = UIEdgeInsetsZero;
self.pagingEnabled = YES;
self.delegate = self;
I create three views like this
[self.pagingViews addObject:[self singleMonthViewInFrame:self.bounds]];
[self.pagingViews addObject:[self singleMonthViewInFrame:CGRectMake(self.bounds.size.width, 0, self.bounds.size.width, self.bounds.size.height)]];
[self.pagingViews addObject:[self singleMonthViewInFrame:CGRectMake(self.bounds.size.width * 2, 0, self.bounds.size.width, self.bounds.size.height)]];
Then I set the content size and also scroll it to the middle
[self setContentSize:CGSizeMake(self.bounds.size.width * 3, self.bounds.size.height)];
[self scrollRectToVisible:((UIView *)[self.pagingViews objectAtIndex:1]).frame animated:NO];
In the scrollview delegate function, i need to do
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender
{
//If scroll right
if(self.contentOffset.x > self.frame.size.width)
{
//do something if scroll right
} else if(self.contentOffset.x < self.frame.size.width)
{
//do something else if scroll left
} else {
return;
}
//scroll back to the middle
[self scrollRectToVisible:((UICollectionView *)[self.pagingViews objectAtIndex:1]).frame animated:NO];
}
Hopefully it is useful to you.
For those that follow down this path, I figured this out and it was a silly error. I forgot to turn off AutoLayout. I keep forgetting that Apple put autoLayout as an enable/disable option under the 'document'-level of a NIB (so I forget to look there).
Turned it off and it works as designed. Looks like autoLayout was causing the views to be rearranged to not need to be scrolled, or something equivalent.
So i have a UIView setup on the Ipad for a small project i am working on. I will be displaying an image or a view on that page. I wanted to know if is it possible to create an invisible border (say 1") around the view that will be unclickable ?
I was thinking of adding a button and disabling it, but i think that would not allow the image to be shown full screen.
I have already setup a recognizer, because i want a three finger swipe to go to the next image. What would be the best approach for this ?
Use a custom UIView class and override hitTest:withEvent:.
- UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGRect frame = CGRectInset(self.bounds, 25, 25);
return CGRectContainsPoint(frame, point) ? self : nil;
}
Adjust the inset to meet your needs.
I have created a relatively simple app which allows the user to drag an imageview from a 'tray' UIView at the bottom of the screen, and when the view is dropped, it adds itself to a subview of a 'target' UIView.
My problem is, when I drop the imageview into the target view, the frame is totally wrong, and the imageview jumps (seemingly randomly) to another position in the target view.
I've torn my hair out over this, and am having a massive brainfart. Is there a way to just get the coordinates of the dragged view in the superview, and then translate that into coordinates of the target view?
As you can see, i'm taking the icon out of the 'tray' view, and into the main view when the dragging begins. When it ends, I simply add the icon into the target view.
if (gesture.state == UIGestureRecognizerStateBegan){
if (icon.superview == viewTray) {
[self closeTray:nil];
[self.view addSubview:icon];
}
}
if (gesture.state == UIGestureRecognizerStateEnded) {
[viewTarget addSubview:icon];
}
An image of the basics of the xib:
use the gesture recognizer to figure out where the finger touches in the viewTarget's frame by:
CGPoint p = [gesture locationInView:viewTarget];
then set the icon's center to be that point, and you are done:
icon.center = p;
[icon removeFromSuperView];
[viewTarget addSubview:icon];
The problem is that the frame is in the bounds of its superView. So when you drop it into a new superView its not the same. You need to convert it.
This code will convert its from from its superView coordinate system to its targetView coordinate system. You should probably do this right after [viewTarget addSubview:icon];
[viewTarget convertRect:icon.frame fromView:icon.superView];