Is there a way to make some parts of UITableView not scrollable? - objective-c

I'm using a paper fold animation To show some content from the top of the screen, over the tableView.
Right now i have a button that triggers this folding action.
I would like instead to trigger this action by swiping from top to bottom. but my TableView is full screen, so i figured if there is a way to make on UITableViewCell, or a part of the table, not scrollable, for it to trigger the Fold swipe instead.
I tried using the UISwipeGestureRecognizer But it doesn't work so well...

There are several ways to stop UITableView from scrolling. UITableView is a subclass of UIScroll view so you can use the UIScroll view methods to stop the table view being scrollable. So in your tableViewController you can call [[self tableView] setScrollEnabled:NO]. You should also be able to stop any user interaction on the table view by calling [[self tableView] setUserInteractionEnabled:NO] (a method on UIView).
You could subclass the UITableView and override the setContentOffset: method (another scrollView method), and only call [super setContentOffset:] when you actually want the table view to scroll (it's possible that you may need to override other methods as well.
You can also get hold of the pan gesture recogniser that the scrollView is using, this allows you to see exactly what messages the scrollView is getting for scrolling and you may be able to adjust the scroll offset accordingly [[self tableView] panGestureRecognizer]. Once you have the panGestureRecognizer you should be able to set it to wait for your swipeGestureRecognizer to fail using [UIPanGestureRecognizer requireGestureRecognizerToFail:. Which will mean that the pan gesture recogniser the scroll view uses will only start working if the swipe fails, you may need to make it so the swipeGestureRecognizer fails if it doesn't start near the edge of the screen.

Related

Custom UIScrollView Gesture

I have a collection view cell which contains an UIScrollview in which all of the content sits. I want to have a gesture that allows users to swipe down when the cell is at 0,0 and then that fades away into the list view of the collection view.
However, I still want the users to be able to scroll down into the rest of the cell content.
Should this be contained in the same gesture? If so, how do I override a scrollview's gesture?
Thanks.
Yea just try implement this method from UIScrollViewDelegate:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
Get scrollView and calculate gestures and make behaviour. And look other methods for make best visual effect :)

Pull to reveal hidden navBar-like menu in iOS? as seen in GetPocket App

I've been driving myself mad over this one. It might be one of those things where I need to take a step back and figure out the simplest way to implement this. Can't find anything on this either. I google-fu'd the heck out of this one.
In the Pocket App when you pull down a menu reveals itself just like the searchBar does. In this instance the faux bar when you pull it past its halfway point if you release it shows springs into place. If it is release before the halfway point the bar will snap back and hide. As shown here
In my case I've been trying to replicate this with no luck. In my case I have a UITextView inside of a UIViewController view. I think I have it all wrong.
I can get it to work with a UIScrollView hidden by initiating with it offscreen and then when I press a button the UIScrollView reveals itself. The problem is that this method covers everything so I'll have to resize and relayout a bunch of views. Is this in a UITableView possibly? I want it to be a pull action though and just want to put some TextStrings/Labels in this bar.
Thank you in advance.
Feels like you need a UIScrollView (or UITableView) and to put the menu you want to reveal at the top (in the table view header, for example) and then get the delegate callbacks for scrolling.
UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
You might also need to watch for some of the dragging delegate callbacks
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
You can initially hide the menu by setting the content offset to the height of the menu, then catch the delegate callbacks for scrolling and, if the scrolling has reached beyond half the size of the menu set the content offset with animation.
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
If I understand your goal correctly you don't need the scroll view.
What I would do is have an simple UIView containing all the stuff you need and place it outside of the visible area. This will be your pull-down view. Then add an UIPanGestureRecognizer to the view in your UIViewController and use it to track the movement of your finger and update the frame of the pull-down view accordingly. Then in this update method you simply check if the position has passed some threshold and if so you let the pull-down view snap to its final position (using animations of course).
If you are unfamiliar with the UIPanGestureRecognizer there is a really good tutorial here:
http://www.raywenderlich.com/6567/uigesturerecognizer-tutorial-in-ios-5-pinches-pans-and-more

Handle tap event by subview of UIScrollView while scrolling

I have custom UIScrollView subclass with some content views inside. In some of them I have UITapGestureRecogniser. All works fine when scroll view is not scrolling. But when it scrolling content views does not receive tap action. What is the simplest solution to handle tap action by subview while scroll view is scrolling?
Details:
MyScrollView scrolls horizontally. It contains a lot of content views (e.g. MyContentView). Each MyContentView has width about one third of MyScrollView width. So there are about 3-4 visible MyContentView elements at a moment. The main behavior of MyScrollView is to 1)make sure that after scrolling one of MyContentView elements will be at center of screen and 2)to scroll to center of MyContentView if user taps on it. So the main answer I hope to get is how to "properly" implement handling of tap action in MyContentView while MyScrollView is decelerating.
I found some same questions and answers but none of them satisfied me. The best was to implement gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: of UITapGestureRecogniser delegate. But in this case I sometimes (when I tap, make smaaaal drag and release finger so tap is steel recognizable(lets called it quasi tap)) have both tap and scroll events and it leads to bugs for me even if scroll view is not scrolling when I begin tap. When user make quasi tap my application tries to scroll to tapped MyContentView element and than immediately handle normal scrolling. It seems even more terrible, due to some other functionality start to perform after handling tap (it must not perform when normal scrolling).
I need solution where scroll view wait enough to decide it is not tap event and only then make scroll. Otherwise if tap event had recognized scroll must not happen.
You can go with the custom delegates methods as well, using #protocol. Implement those delegate methods in view controller where your UIScrollView has been added.
like in MyContentView:
In touchesBegan method,
[self.delegate contentViewTapped:self];
Now in ContainerView class where scroll view is added, implement that method:
- (void)contentViewTapped:(MyContentView *)myContentView {
NSLog (#"ContentView no: %d", myContentView.tag); // if tag has been set while adding this view to scrollview.
}
Go through the examples for #protocol.
Hope this is what you required.
Enjoy Coding :)
This is built into UIScrollView - take a look at the delaysContentTouches and canCancelContentTouches properties. This should alleviate the problem when dragging a small bit after a tap.
This is all system built-in behaviour. I would suggest sticking with what Apple has provided for the feel of your interface (how it reacts to small drags, for instance) so that your app doesn't feel out of place on a user's phone.
EDIT:
Alternatively, you could disable scrolling of your scroll view in you gesture recognizer and re-enable it once it's ended/cancelled.
Further Edit:
I don't understand - I've created a sample project that illustrates how to intercept touches in a subview of a scroll view using gesture recognizer delegate methods. Play close attention to the "Cancellable Content Touches" and "Delays Content Touches" properties of the scroll view. They're both YES for very important reasons.
You scroll view should be delaying content touches until it has determined if the user is attempting a tap, pseudo-tap (as you put it), or a pan for the scroll view. Apple has already written the functionality you're trying to build; UIScrollView will already do what you want.
The problem is that the system doesn't want a scroll view's subviews intercepting tap events while the scroll view is scrolling. To this end, it cancels touch events if it determines that the user is actually trying to pan. Setting "Delays Content Touches" enables this behaviour. Ensure it's turned on and you should be fine.

presentPopoverFromRect is displaying a popover sideways on rotation

-I have a UIView.
-This UIView has a UIButton that when clicked makes a UIAlertView appear.
-Within this UIAlertView I have another UIButton that when clicked calls buttonClicked:
-Within this buttonClicked: method, I call presentPopoverFromRect with a custom view inside. (hourKeyboard is the custom view)
-(void)buttonClicked:(id)sender
{
if(self.hourKeyboard==nil)
{
self.hourKeyboard = [[HourKeyboardViewController alloc] init];
self.hourKeyboardPopover = [[UIPopoverController alloc] initWithContentViewController:self.hourKeyboard];
}
[self.hourKeyboardPopover presentPopoverFromRect:[sender bounds] inView:sender permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
}
In normal portrait mode, this works great. The popover spawns just to the right of the button, with the arrow correctly pointing left to the button.
There's 2 problems that arrises:
1) While this popover is visible, when you rotate the screen the popover rotates slightly incorrectly (it doesn't reposition it's own x and y position)
2) If the popover is not being shown. If you rotate the screen, then call "buttonClicked", the popover will appear, however, its being shown sideways above the button with the arrow pointing "down" towards the button (technically left in relation to the sideways popover view). If you dismiss it, rotate the screen, then call "buttonClicked", the popover now appears upside down with the button pointing "right" to the button (again, technically left in relation to the sideways popover view)! Repeat to make it sideways again, then right-side up again.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
My thinking:
1) I believe I can just reposition the x and y, however, I've also read that you should dismiss the popover and present it over again on a rotation. I'll see if I can get the first one working, however I'm more concerned about the second problem.
2) I have no idea how to fix this rotation issue. It seems that when you rotate to landscape without the popover being visible. And then you call presentPopoverFromRect, the popover is created with the iPad thinking it's still in portrait view by mistake. That's the behavior it's giving, however, I'm not sure how to make the iPad not make this mistake.
-=-=-=-=-=-=-=-=-=
Thanks again for any help you can provide!
-=-=-
Slight Update:
1) It was easy to just dismiss the popover from the main view on rotation. And this seems to be the general way everyone deals with this issue.
2) Trying out various things such as changing the frame, using CGAffineTransformMakeRotation, and others...but no luck thus far
-=-=-
Another Update:
2) After a lot of testing, it seems to be a direct issue with UIAlertView. If I place the view within UIAlertView (currently doing), the AlertView doesn't tell the popover that the screen is rotated...thus creating the issue
It looks like the only way to fix this is to drop the UIAlertView completely. Instead of showing the UIAlertView, I'll disable the various background views manually (like Alert View was doing) and then show a custom UIView that looks darn similar to the AlertView. From there, I should be able to show the popover without any issues. I'll let yea know how it turns out.
-=-=-=-=-=-=-=-
Final Solution:
I ended up just creating my own view, and having that view imitate a UIAlertView. Then when I spawned the popover, I placed it in the root view controller. Worked much MUCH better, but required more work since I had to manually create my own View instead of the premade UIAlertView. Either way, apparently UIAlertView fails at telling a UIPopoverover subview what rotation it is in.
dismiss the popover in willRotateToInterfaceOrientation and show it again in didRotateFromInterfaceOrientation.
It works with no problems.
EDIT:
Sorry, I misunderstood your second problem.
If some part of your view hierarchy is displayed with bad orientation, one of your controllers is probably missing shouldAutorotateToInterfaceOrientation method.

UIScrollView on UIWebView and UITapGestureRecognizer conflicts

I have a UITapGestureRecognizer on a UIViewController, which has a UIScrollView and UIWebView inside. It recognizes the tap gesture only after I scroll the UIWebView. How could I prevent this ?. Basically I want the tap gesture to be detected, when I am not scrolling the web view. I looked around and the closest I found is this:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
return YES;
}
but not sure how can I use this to disable the tap while scrolling. Any idea?
Another thing I want to do is to disable the UITapGestureRecognizer, when a link on the UIWebView is clicked (shouldStartLoadWebRequest is called). I checked that the tap gesture recognizer is called, before the shouldStartLoadWebRequest is called. Basically when clicking on a link on a UIWebView, it shouldn't trigger the action invoked by the UITapGestureRecongnizer. Any idea on how to do this?
So Apple's documentation strongly recommends you don't nest a UIWebView inside a scroll view:
Important: You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behavior can result because touch events for the two objects can be mixed up and wrongly handled.
It is possible on iOS 5 and above to get direct access to the underlying scroll view on a UIWebView (using the scrollView) property - playing around with this might help you.