Pull up keyboard while scrolling UITableView and scroll along with the tableView - ios

To add panning behaviour to the keyboard, I am using DAKeyboardControl. It works just fine (after modifications) for panning behaviour to close the keyboard.
How do I make the keyboard appear if the user tries to scroll downwards (finger pan in up direction) at the end of the UITableView. To be more specific, I am looking for behaviour similar to the Facebook Messages app, where, if you scroll up the keyboard appears, with panning, and scrolls with the table.
EDIT: It seems I am not clear about what I want. I wish to move the keyboard along with the scrollview (UITableView). In the following image, I am panning the keyboard along with the table. The table is already scrolled to it's bottom, and if I try to scroll down any further the keyboard begins to appear. My finger, meanwhile, is in the middle of the table.

This effect can be achieved by setting the scrollview's keyboardDismissMode to UIScrollViewKeyboardDismissModeInteractive and then in scrollViewDidScroll: calling [textField becomeFirstResponder];. Because you are in the middle of scroll, the keyboard will respect the keyboardDismissMode property and interactively appear. Drop the DAKeyboardControl; it's outdated.

You need an element such as UITextView and then, detect the scroll (for example, using any of the UIScrollView delegate methods). Once scrolled, call becomeFirstResponder on the text view.

add this in .h fileā€¦
#property (nonatomic, assign) CGFloat lastContentOffset
And add this in your .m file...
- (void)scrollViewDidScroll:(UIScrollView *)sender
{
if (self.lastContentOffset > scrollView.contentOffset.y)
{
[textField becomeFirstResponder];
}
else if (self.lastContentOffset < scrollView.contentOffset.y)
{
[textField resignFirstResponder];
}
self.lastContentOffset = scrollView.contentOffset.y;
}
Hope this will do the trick, haven't tested it...

Related

Drag UIView containg UIScrollView

I have been having trouble trying to drag a UIView that has a UIScrollView within it. The UIView drags up from the bottom of the screen, when it has hit the top you can use the UIScrollView inside it. When the user scrolls to the top of the UIScrollView, the UIView should drag down with their finger.
It should work like the Google Maps popup when you click a pin.
Update: I can get the dragging up of the UIView working (touchesMoved) that changes the Y value. The main functionality I am having problems with is the dragging down of the UIView when the user touches inside the UIScrollView. I want the user to still be able to drag the scrollview as normal until it reaches the top at which point the dragging action should then move the parent UIView down off the screen.
Update 2: This is the code you suggested to turn off the user interaction:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if(scrollView.contentOffset.y<=0){
scrollView.userInteractionEnabled = NO;
}else{
scrollView.userInteractionEnabled = YES;
}
}
Most apps handle this scenario by using the UIScrollViewDelegate method scrollViewDidScroll.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// check the scrollView.contentOffset.y to see if you
// have reached the top of the scroll view.
}
You can then check the contentOffset and continue with your dragging code the way you want.

Custom UIView widget with UIScrollView not scrolling

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.

Cross Directional UIScrollViews - Can I Modify the Scrolling Behaviour?

Here's how the scroll views work: One scroll view is paging enabled in the horizontal direction. Each 'page' of this scroll view contains a vertically scrolling UITableView. Without modification, this works OK, but not perfectly.
The behaviour that's not right: When the user scrolls up and down on the table view, but then wants to flick over to the next page quickly, the horizontal flick/swipe will not work initially - it will not work until the table view is stationary (even if the swipe is very clearly horizontal).
How it should work: If the swipe is clearly horizontal, I'd like the page to change even if the table view is still scrolling/bouncing, as this is what the user will expect too.
How can I change this behaviour - what's the easiest or best way?
NOTE For various reasons, a UIPageViewController as stated in some answers will not work. How can I do this with cross directional UIScrollViews (/one is a table view, but you get the idea)? I've been banging my head against a wall for hours - if you think you can do this then I'll more than happily award a bounty.
According to my understanding of the question, it is only while the tableView is scrolling we want to change the default behaviour. All the other behaviour will be the same.
SubClass UITableView. UITableViews are subClass of UIScrollViews. On the UITableView subClass implement one UIScrollView's UIGestureRecognizer's delegate method
- (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer
{
//Edit 1
//return self.isDecelerating;
//return self.isDecelerating | self.bounces; //If we want to simultaneous gesture on bounce and scrolling
//Edit 2
return self.isDecelerating || self.contentOffset.y < 0 || self.contentOffset.y > MAX(0, self.contentSize.height - self.bounds.size.height); // #Jordan edited - we don't need to always enable simultaneous gesture for bounce enabled tableViews
}
As we only want to change the default gesture behaviour while the tableView is decelerating.
Now change all 'UITableView's class to your newly created tableViewSubClass and run the project, swipe should work while tableView is scrolling. :]
But the swipe looks a little too sensitive while tableView is scrolling. Let's make the swipe a little restrictive.
SubClass UIScrollView. On the UIScrollView subclass implement another UIGestureRecognizer's delegate method gestureRecognizerShouldBegin:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
CGPoint velocity = [(UIPanGestureRecognizer *)gestureRecognizer velocityInView:self];
if (abs(velocity.y) * 2 < abs(velocity.x)) {
return YES;
}
}
return NO;
}
We want to make the "swipe is clearly horizontal". Above code only permits gesture begin if the gesture velocity on x axis is double than on y axis. [Feel free to increase the hard coded value "2" if your like. The higher the value the swipe needs to be more horizontal.]
Now change the `UiScrollView' class (which has multiple TableViews) to your ScrollViewSubClass. Run the project. :]
I've made a project on gitHub https://github.com/rishi420/SwipeWhileScroll
Although apple doesn't like this method too much:
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.
I've found a great way to accomplish this.
This is a complete solution for the problem. In order to scroll the UIScrollView while your UITableView is scrolling you'll need to disable the interaction you have it.
- (void)viewDidLoad
{
[super viewDidLoad];
_myScrollView.contentSize = CGSizeMake(2000, 0);
data = [[NSMutableArray alloc]init];
for(int i=0;i<30;i++)
{
[data addObject:[NSString stringWithFormat:#"%d",i]];
}
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[self.view addGestureRecognizer:tap];
}
- (void)handleTap:(UITapGestureRecognizer *)recognizer
{
[_myTableView setContentOffset:_myTableView.contentOffset animated:NO];
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
scrollView.userInteractionEnabled = NO;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
scrollView.userInteractionEnabled = YES;
}
To sum up the code above, if the UITableView is scrolling, set userInteractionEnabled to NO so the UIScrollView will detect the swipe. If the UITableView is scrolling and the user taps on the screen, userInteractionEnabled will be set to YES.
Instead of using UIScrollView as a container for these multiple table views, try using a UIPageViewController.
You can even integrate this into your existing view controller setup as a child view controller (directly replacing the UIScrollView).
In addition, you'll likely want to implement the required methods from UIPageViewControllerDataSource and possibly one or more of the methods from UIPageViewControllerDelegate.
Did you try the methods : directionalLockEnabled of both your table and scroll and set them up to horizontal for one and vertical for the other ?
Edit :
1)
What you want to do is very complicate since the touch wait some time (like 0.1s) to know what your movement will be. And if your table is moving, it will take your touch immediately whatever it is (because it's suppose to be reactive movement on it).
I don't see any other solution for you but to override touch movement from scratch to detect immediately the kind of mouvement you want (like if the movement will be horizontal) but it will be more than hard to do it good.
2)
Another solution I can advise you is to make your table have left and right margin, where you can touch the parent scroll (pages thing so) and then even if your table is scrolling, if you touch here, only your paging scroll will be touched. It's simpler, but could not fit with your design maybe...
Use UIPageViewController and in the -viewDidLoad method (or any other method what best suits your needs or design) get UIPageViewController's UIScrollView subview and assign a delegate to it. Keep in mind that, its delegate property won't be nil. So optionally, you can assign it to another reference, and then assign your object, which conforms to UIScrollViewDelegate, to it. For example:
id<UIScrollViewDelegate> originalPageScrollViewDelegate = ((UIScrollView *)[pageViewController.view.subviews objectAtIndex:0]).delegate;
[((UIScrollView *)[pageViewController.view.subviews objectAtIndex:0]) setDelegate:self];
So that you can implement UIScrollViewDelegate methods with ease. And your UIPageViewController will call your delegate's -scrollViewDidScroll: method.
By the way, you may be obliged to keep original delegate, and respond to delegate methods with that object. You can see an example implementation in ViewPagerController class on my UI control project here
I faced the same thing recently. My UIScrollview was on paging mode and every page contained a UITableView and like you described it worked but not as you'd expected it to work. This is how solved it.
First I disabled the scrolling of the UIScrollview
Then I added a UISwipeGestureRecognizer to the actual UITableView for left and right swipes.
The action for those swipes were:
[scroll setContentOffset:CGPointMake(currentPointX + 320, PointY) animated:YES];
//Or
[scroll setContentOffset:CGPointMake(currentPointX - 320 , PointY) animated:YES];
This works flawlessly, the only down side is that if the user drags his finger on the UITableVIew that will be considered as a swipe. He won't be able to see half of screen A and half of screen B on the same screen.
You could subclass your scroll view and your table views, and add this gesture recognizer delegate method to each of them...
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:
(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
I can't be sure this is exactly what you are after, but it may come close.

How to reset my UIScrollView's position after returning from a modal transition?

I have a simple view containing a long view with many buttons, with the whole thing being in a UIScrollView. The scroller works well, and I can scroll to the bottom and click a button. Every button triggers a modal seque to another view. That new view is then dismissed by user interaction, causing the original UIScrollView's view to load again.
Here's the problem: If I click on a button toward the top of the UIScrollView, I enter the modal segue, dismiss the new view, and return to the UIScrollView's view without a problem. But, if I click on one of the buttons toward the bottom of the UIScrollView, when I return seque out and then transition back, my scrolling is all messed up. I can only see the area beneath my scroller, and can't scroll back up to the top anymore!
I'm pretty sure there must be some way to reset the UIScrollView's starting and ending points upon ViewWillAppear, but I can't figure it out. Any help is appreciated!
Also, FYI, I simply added the UIScrollView through interface builder, and haven't implemented or synthesized it anywhere yet.
try this code:
- (void)viewWillAppear:(BOOL)animated
{
[yourscrollview setContentOffset:CGPointZero animated:YES];
}
Please note: the bug this question and answer is about appears to be fixed in iOS 7. The rest of this answer is only relevant to iOS 6 (and probably earlier).
The behaviour being exhibited here is a bug in the UIScrollView class. As noted by the OP, after returning from a modally presented UIViewController to a scene containing a UIScrollView, the UIScrollView takes whatever point it's currently scrolled to and starts behaving as though that is its origin. That means that if you'd scrolled down your scroll view before modally presenting another View Controller, you can't scroll back up upon returning to the scene with the scroll view.
The same thing happens when you remove the Scroll View from the view hierarchy and re-add it, even without changing its window.
You can work around this by setting the contentOffset of the scroll view back to {0,0} before it gets displayed again after dismissing the modal View Controller. If you actually want to preserve the point the user had scrolled to before they triggered the modal, then after the UIScrollView is redisplayed you can set the contentOffset back to whatever it was before you reset it.
Here's a UIScrollView subclass that fixes the bug without resetting the scroll view to the top whenever you return from a modal:
#interface NonBuggedScrollView : UIScrollView
#end
#implementation NonBuggedScrollView {
CGPoint oldOffset;
}
-(void)willMoveToWindow:(UIWindow *)newWindow {
oldOffset = self.contentOffset;
self.contentOffset = CGPointMake(0,0);
}
-(void)willMoveToSuperview:(UIView *)newSuperview {
oldOffset = self.contentOffset;
self.contentOffset = CGPointMake(0,0);
}
-(void)didMoveToWindow {
self.contentOffset = oldOffset;
}
-(void)didMoveToSuperview {
self.contentOffset = oldOffset;
}
#end
If you'd rather do this in a UIViewController than in a UIScrollView subclass, change the content offset in the viewWillAppear: and viewDidAppear methods.
If you don't want to preserve where the user's scroll position when they return from a modal, and just want to scroll the UIScrollView back to the top, as the OP asked for, then all you need is the even simpler:
#interface NonBuggedScrollView : UIScrollView
#end
#implementation NonBuggedScrollView
-(void)willMoveToWindow:(UIWindow *)newWindow {
self.contentOffset = CGPointMake(0,0);
}
-(void)willMoveToSuperview:(UIView *)newSuperview {
self.contentOffset = CGPointMake(0,0);
}
#end
First, thanks for the approved answer above. Someone mentioned that it was no longer applicable but I have a scrolling view inside of table view cell and it needs to be reset when the cell is reused.
Here is the solution in Swift.
#IBOutlet var scrollView: UIScrollView!
// many lines of code later inside a function of some sort...
scrollView.setContentOffset(CGPointMake(0.0, 0.0), animated: false)
To solve this problem i use this code:
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.scrollview scrollRectToVisible:CGRectMake(0, 0, 1, 1)
animated:NO];
}
You can change the starting and ending points by calling scrollRectToVisible:animated:. But I'm not sure if this fixes your problem.
Use below code snippet to restore the scroll position for a UIScrollview
Declare "scrollPosition" variable as CGPoint type.
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
//get the current offset
scrollPosition = scrollView.contentOffset;
//set current view to the beginning point
self.scrollView.contentOffset = CGPointZero;
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
//retrieve the previous offset
self.scrollView.contentOffset = scrollPosition;
}

Hiding a toolbar element when UITableView scrolls (similar to Facebook's app?)

How I can achieve this effect?
This isn't immediately noticeable from your screenshots, but I believe you want the that header toolbar to slide up as the user scrolls, right? (I'd suggest clarifying on that part)
You can do this a few ways, and in all of them you will have to implement your own scrolling logic, meaning how much the header toolbar slides up depending on where you have scrolled. That said, here's how to do it:
1. If you're using UITableView, I assume you've got your view controller set as its delegate. Since UITableView is a subclass of UIScrollView already, just add the UIScrollViewDelegate to your view controller. That will give us scroll events as they happen. You'll want to do your logic in scrollViewDidScroll:.
2.. If you're simply using UIScrollView, just set your view controller as its delegate, implement UIScrollViewDelegate, and do your logic in scrollViewDidScroll:.
That said, your code might look something like this:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint scrollPos = scrollView.contentOffset;
if(scrollPos.y >= 40 /* or CGRectGetHeight(yourToolbar.frame) */){
// Fully hide your toolbar
} else {
// Slide it up incrementally, etc.
}
}
Anyway, hope I helped.
If you have properly set the delegate, your table will call scrollViewDidScroll: when scrolled.
So in your controller, you can add something like :
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y >0) //means that the user began to scroll down the table
{
[UIView animateWithDuration:0.4 animations:^{
//animations you want to perform
}];
}
}
Here i implemented code for UIView Hide / Show when tableview scrolling. When tableview scrolling down then UIView is hidden and when scrolling up then UIView show. I hope it's working for you...!
Step 1:- Make one property in .h file
#property (nonatomic) CGFloat previousContentOffset;
Step 2:- Write down this code in scrollViewDidScroll Method.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat currentContentOffset = scrollView.contentOffset.y;
if (currentContentOffset > self.previousContentOffset) {
// scrolling towards the bottom
[self.subButtonView setHidden:YES];
} else if (currentContentOffset < self.previousContentOffset) {
// scrolling towards the top
[self.subButtonView setHidden:NO];
}
self.previousContentOffset = currentContentOffset;
}
I create simple class for this effect:
UIHidingView is an iOS class that displays UIView element on top UITableView which is hiding when Table View is scrolling.
This will answer your question :
iPhone: Hide UITableView search bar by default
same concept, different control. You can put a UIView on top row of tableview or any other relevant control such as button.
Good luck.

Resources