I am using scrollToRowAtIndexPath: to scroll UITableView to top. Then I handle the animation in scrollViewDidEndScrollingAnimation:. After it scrolls, it should perform an action. My problem is, that when it doesn't scroll - e.g. the table view is already on top before I call the method - the action doesn't get called in scrollViewDidEndScrollingAnimation:. How do I find out whether the UITableView is already on top?
if (yourTableView.contentOffset.y > 0)
{
// yourTableView is not on top.
}
else
{
// yourTableView is already on top.
}
Use UITableView contentOffset property. If contentOffset.y is 0 then table is at top else it is not.
Hope it helps you.
You need to use the UIScrollViewDelegate function - (void)scrollViewDidEndDecelerating:(UIScrollView *)aScrollView and work out if the content offset is set to 0 on the y axis for that scrollview. If its 0 then you have reached the top.
while you're scrolling up tableView(_:cellForRowAt) gets called to display rows and when the indexPath.row == 0 that's when you're at the top of the tableview
Related
I am trying to set search bar for table, so can anyone tell me that, is it possible to have uisearchabr when table scroll up / pull up and for other case search bar doesn't get appear on screen. Is it possible? If yes, then how?
Thank you in advance
You can implement scrollViewDelegate scrollViewDidScroll and check the contentOffset to show/hide search bar. Since table view is a subclass of UIScrollView, you can depend on this delegate method to decide when to show/hide it.
Basically when the contentOffset represents the scrolled up position, you can show this search bar and when the contentOffset value starts changing, you can hide the search bar. Use hidden property of the search bar to show/hide it. You can also use removeFromSuperView/addSubview methods.
You can use below code or concept.Concept is in scroll view delegate function you will check the content offset and display the set the search bar object in table view header.
Code :
//MARK: -UIScrollViewDelegate
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
if (scrollView.contentOffset.y <= - 65.0f) {
[self.tableView setTableHeaderView:self.searchBar];
}
}
Make delegate of UIScrollViewDelegate and implement its delegate method,
in this when you start scroll the table then unhide your searchBar and change frame of tableView.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[mySearchBar setHidden:NO];
[quotesTableView setFrame:CGRectMake(0, 44, 320, 416)];
}
I am having a very difficult time understanding this. I have a UITableView which frameHeight is set at 200. Although I've set numberOfRows to 0, this UITableView still scrolls, any idea why? My understanding is that it will only scroll if the contentView height is bigger than the frameHeight right? But in the case of the numberOfRows of 0, the contentView should be 0?
First Solution use UITableViewStyleGrouped style
Because by default it is set to UITableViewStylePlain which shows empty cells too after displaying filled cells.
Second Indirect Way
if (table.contentSize.height < table.frame.size.height) {
table.scrollEnabled = NO;
}
else {
table.scrollEnabled = YES;
}
You can run this code after calling reloadData on the table, and it calculates the right sizes and appears to work.
Edit 1
Another way is to disable Bounce -> Bounces/ Bounce Vertically
Hope this will solve your problem
I have the UIScrollView with pagingEnabled set to YES, and programmatically scroll its content to bottom:
CGPoint contentOffset = scrollView.contentOffset;
contentOffset.y = scrollView.contentSize.height - scrollView.frame.size.height;
[scrollView setContentOffset:contentOffset animated:YES];
it scrolls successfully, but after that, on single tap its content scrolls up to offset that it has just before it scrolls down. That happens only when I programmaticaly scroll scrollView's content to bottom and then tap. When I scroll to any other offset and then tap, nothing is happened.
That's definitely not what I'd like to get. How that should be fixed?
Much thanks in advance!
Timur.
This small hack prevents the UIScrollView from scrolling when tapped. Looks like this is happening when the scroll view has paging enabled.
In your UIScrollView delegate add this method:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
scrollView.pagingEnabled = self.scrollView.contentOffset.x < (self.scrollView.contentSize.width - self.scrollView.frame.size.width);
}
This disables the paging when the scroll view reaches the right end in horizontal scrolling (my use case, you can adapt it to other directions easily).
I just figured out what causes this problem, and how to avoid it. If you having pagingEnabled set to YES on a scroll view, you must set the contentOffset to be a multiple of the scroll view's visible size (i.e. you should be on a paging boundary).
Concrete example:
If your scroll view was (say) 460 pixels high with a content area of 920, you would need to set the content offset to EITHER 0 or 460 if you want to avoid the "scroll to beginning on tap" problem.
As a bonus, the end result will probably look better since your scroll view will be aligned with the paging boundaries. :)
The following workaround did help (assume that one extends UIScrollView with a category, so 'self' refers to its instance):
-(BOOL) scrolledToBottom
{
return (self.contentSize.height <= self.frame.size.height) ||
(self.contentOffset.y == self.contentSize.height - self.frame.size.height);
}
Then, one should sometimes turn pagingEnabled off, just at the position where scroll view reaches its bottom. In the delegate (pagingEnabled is initialy on of course, since the problem occurs only when it is enabled):
-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.pagingEnabled == YES)
{
if ([scrollView scrolledToBottom] == YES)
scrollView.pagingEnabled = NO;
}
else
{
if ([scrollView scrolledToBottom] == NO)
scrollView.pagingEnabled = YES;
}
}
This seems to be a bug:
UIScrollView doesn't remember the position
I have tested this on iOS 4.2 (Simulator) and the issue remains.
When scrolling a ScrollView I would suggest using
[scrollView scrollRectToVisible:CGRectMake(0,0,1,1) animated:YES];
Where the rect is the position you're after. (In this case the rect would be the top of the scrollview).
Changing the content offset is not the correct way of scrolling a scrollview.
Setup: I have a UITableView, each UITableViewCell has a UIScrollView. What I am trying to do is to get all of the UIScrollViews to scroll together, such that when you scroll one of them all of the UIScrollViews appear to scroll simultaneously.
What I've done is subclass UITableView so that it has an array of all of the UIScrollViews within its table cells. I then forwarded TouchesBegan, TouchesMoved, TouchesCancelled, and TouchesEnded from the UITableView to all of the UIScrollViews in the array.
This doesn't appear to work. The UIScrollViews do not scroll! The only way I've managed to get this to work is to call the setContentOffset: method on the scrollviews. However, this is a pretty bad solution since it doesn't give you the swiping and deceleration features of the UIScrollView.
Any ideas on why my touches methods aren't getting to the UIScrollViews? Or a better way to implement this?
Ok, got it working. Thanks for the tips Ricki!
2 things to add to Ricki's solution, if you want to avoid an infinite loop, you have to check to see whether the scrollView's tracking or dragged properties are set. This will insure that only the ScrollView that is actually being dragged is calling the delegate.
- (void)scrollViewDidScroll:(UIScrollView *) theScrollView {
if (theScrollView.dragging || theScrollView.tracking)
[self.delegate scrolling:[theScrollView contentOffSet]];
}
Also, in the scrolling method of the delegate, I set animated to NO, this got rid of the delay between the initial swipe and the other scrollviews getting updated.
I did something "similar" where I had 4 scrollViews incased inside a parent view.
I placed a scrollView inside a UIView, this UIView was passed a delegate from its parentView, that was the view who kept track of all the scrollViews. The UIView containing a scrollVIew implemented the UIScrollViewDelegate and this method;
- (void)scrollViewDidScroll:(UIScrollView *) theScrollView {
[self.delegate scrolling:[self.scrollView contentOffSet]];
}
Now the parent view did this on all the scrollViews:
- (void) scrolling:(CGFloat) offset {
for(UIScrollView *s in self) {
[s setContentOffset:offset animated:YES];
}
}
It is of course a bit of a strain on the CPU, but scrolling several views will be that under any circumstances :/
Hope this was something in the direction of what you needed, and that it made any sense.
Added:
I took me 8 different paths and a lot of mass chaos before I made it work. I dropped the touchedBegan approach early, there is just no way to write something that comes close to Apples swipe, flick, scroll algorithms.
I don't know if the tableview and scrollview will "steal" each others touch events, but as I can read from your description you made that part work.
A follow up idea to ease the CPU usage. add each scrollview to a cell, set its tag=14, now when scrolling asked for all visible cells only, ask for viewWithTag=14, set the contentOffset on this. Save the content offset globally so you can assign it to cells being scrolled onto the screen in cellForRowAtIndexPath.
So set the offSet to a global property, in cellForRowAtIndexPath find the view with tag = 14, set its offset. This way you don't even need a reference to the scrollViews only the delegate.
If you have differently sized UIScrollViews and are using paging, this works great:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)_scrollView {
#pragma unused(_scrollView)
categoryPageControlIsChangingPage = NO;
for (UIImageView *iv in [categoryScrollView subviews]) {
iv.alpha = (iv.tag != categoryPageControl.currentPage+1)?0.5f:1.0f;
ILogPlus(#"%i %i", iv.tag, categoryPageControl.currentPage+1);
}
[self scrolling:_scrollView];
}
- (void)scrolling:(UIScrollView *)sv {
CGFloat offsetX = sv.contentOffset.x;
CGFloat ratio = offsetX/sv.contentSize.width;
if ([sv isEqual:categoryScrollView]) {
[categoryScrollViewLarge setContentOffset:CGPointMake(ratio*categoryScrollViewLarge.contentSize.width, 0) animated:YES];
}else {
[categoryScrollView setContentOffset:CGPointMake(ratio*categoryScrollView.contentSize.width, 0) animated:YES];
}
}
I would like to completely reset the scroll position of a UITableView, so that every time I open it, it is displaying the top-most items. In other words, I would like to scroll the table view to the top every time it is opened.
I tried using the following piece of code, but it looks like I misunderstood the documentation:
- (void)viewWillAppear:(BOOL)animated {
[tableView scrollToNearestSelectedRowAtScrollPosition:UITableViewScrollPositionTop animated:NO];
}
Is this the wrong approach here?
August got the UITableView-specific method. Another way to do it is:
[tableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
This method is defined in UIScrollView, the parent class to UITableView. The above example tells it to scroll to the 1x1 box at 0,0 - the top left corner, in other words.
The method you're using scrolls to (as the method name implies) the nearest selected row. In many cases, this won't be the top row. Instead, you want to use either
scrollToRowAtIndexPath:atScrollPosition:animated:
or
selectRowAtIndexPath:animated:scrollPosition:
where you use the index path of the row you want to scroll to. The second method actually selects a row, the first method simply scrolls to it.
What I ended up doing was this:
Swift 4
self.tableView.contentOffset = CGPoint.zero
DispatchQueue.main.async {
self.tableView.reloadData()
}
Swift:
self.tableView.scrollRectToVisible(CGRect.zero, animated: false)