UIScrollView contentOffset resets when tapped - swift

I have a UIScrollView which resets its contentOffset anytime the view is tapped.
This is demonstrated here: https://gyazo.com/c8fb5c0fa5b6d53acf45c809dcb5b251
What I've tried:
Disabling keyboard dismissal
Checking for isDragging on scrollViewDidScroll and resetting the content offset if it was false. This resulted in jumpy behavior.
Reading this question, it did not help.
You can test it yourself here: https://github.com/willbishop/scrollviewwizardry
Any ideas?

You could turn off paging if that is not the intended result you're looking for:
scrollView.isPagingEnabled = false
If paging is something you're looking to use:
Paging is meant for snapping between multiple views in a ScrollView, usually where each subview is the size of the ScrollView's bounds. Since you've enabled paging with vertical scrolling, and your ScrollView content height is less than two "pages" height, unexpected results from paging are occurring.
For your situation, if you set each view in your ScrollView equal to the ScrollView's height, and your ScrollView contentSize height is equal to its subview's combined height, then paging works properly and the content offset doesn't reset when tapped:
scrollView.frame.size.height = view.frame.height
subView1.frame.size.height = view.frame.height
subView2.frame.size.height = view.frame.height
scrollView.contentSize.height = subView1.frame.height + subView2.frame.height
It will snap between showing your two views when the ScrollView stops being scrolled. You could also have your "peekAmount" where the second view shows at the bottom while still scrolled to the top "page", as long as the height of your ScrollView contentSize is equal to twice the ScrollView bounds height (i.e. two pages).

Related

Objective C: How can use two scrollview, one inside the another?

I have two scrollview, one scrollview inside another and I want to scroll my main scrollview content first after that only I want to scroll subview scroll
CGFloat scrollOffset = texscrl.contentOffset.y;
if (scrollOffset == 0)
{
//This condition will be true when scrollview will reach to bottom
self.ArtistScroll.scrollEnabled=YES;
texscrl.scrollEnabled=YES;
}else
{
self.ArtistScroll.scrollEnabled=NO;
texscrl.scrollEnabled=YES;
}
Here, am using content offset for this.. so can anyone help me?
You can achieve the affect you describe by using a third scrollview to actually handle the touch gestures and manually set the contentOffset of the other scrollviews.
Here is how to achieve this for vertically scrolling content, which is I think what you are describing. In the code, outerScrollView is the main scrollview, innerScrollView is the sub-scrollview that is contained by the outer scrollview, and trackingScrollView is the third scrollview that only handles the touches - it has no content.
Create the three scrollviews such that trackingScrollView exactly covers outerScrollView. (I assume you will do this in XIB or storyboard so there is no code.)
Once your scrollviews have content (and whenever their contentSize or bounds change) you should set the contentSize of trackingScrollView:
self.trackingScrollView.contentSize = CGSizeMake(self.trackingScrollView.bounds.size.width,
self.outerScrollView.contentSize.height + self.innerScrollView.contentSize.height - self.innerScrollView.bounds.height);
This makes the content height be that of outerScrollView plus the scrollable distance of innerScrollView, allowing trackingScrollView to scroll over the total travel distance of both scrollviews.
Delegate for trackingScrollView and implement scrollViewDidScroll:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.trackingScrollView) {
CGFloat const offsetY = self.trackingScrollView.contentOffset.y;
// Calculate the maximum outer scroll offset
CGFloat const maxOuterOffsetY = self.outerScrollView.contentSize.height - self.outerScrollView.bounds.height;
// Calculate the maximum inner scroll offset
CGFloat const maxInnerOffsetY = self.innerScrollView.contentSize.height - self.innerScrollView.bounds.height;
if (offsetY < maxOuterOffsetY) {
// Scroll is within the outer scroll area or the top bounce zone
self.outerScrollView.contentOffset = CGPointMake(0, offsetY);
self.innerScrollView.contentOffset = CGPointZero;
} else if (offsetY < maxOuterOffsetY + maxInnerOffsetY) {
// Scroll is within the inner scroll area
self.outerScrollView.contentOffset = CGPointMake(0, maxOuterOffsetY);
self.innerScrollView.contentOffset = CGPointMake(0, offsetY - maxOuterOffsetY);
} else {
// Scroll is within the bottom bounce zone
self.outerScrollView.contentOffset = CGPointMake(0, offsetY - maxInnerOffsetY);
self.innerScrollView.contentOffset = CGPointMake(0, maxInnerOffsetY);
}
} else {
// Handle other scrollviews as required
}
}
Effectively we divide the tracking scroll area into two parts. The top part controls the scrolling within the outer scrollview and the bottom part controls the inner scrollview.
Assuming bounce is turned on (which generally it should be) we also need to handle scrolling outside the scroll area. We want the bounce to show on the outer scrollview so the top bounce is handled implicitly by the first conditional. However, the bottom bounce has to be handled explicitly by the third conditional, otherwise we would see the bounce on the inner scrollview.
To be clear, in this solution the two content scrollviews (outerScrollView and innerScrollView) do not receive any touches at all; all input is going to trackingScrollView. If you want input to subviews of the content scrollviews you will need a more advanced solution. I believe this can be done by putting trackingScrollView behind outerScrollView, removing outerScrollView's gesture recognizers and replacing them with those from trackingScrollView. I have seen this technique presented in an old WWDC session on UIScrollView but I have not tried it myself.

How is scrollView page size calculated?

I have a scrollView embedded inside a UIView with a top constraint to the navigation bar.
My question is, how is the page size calculated of a UIScrollView (where scrollView.pagingEnabled = true)? I want each page to be the size of the aforementioned UIView, but it turns out that it's slightly bigger.
I set the contentSize as so in the view controller:
self.scrollView.contentSize = CGSizeMake(UIScreen.mainScreen().bounds.width, (UIScreen.mainScreen().bounds.height - 64) * 4)
I figured each page size would be UIScreen.mainScreen().bounds.height - 64 (because of the navigation bar and clock) but, like I said, the page turns out to be slightly bigger.
Try to set the height of the scrollView same as UIView that hosts it self.scrollView.superview.
Edit:
The 'slightly bigger' is probably the 64 points offset. You need to set self.automaticallyAdjustsScrollViewInsets = false inside yout viewController that hosts the scrollView to fix it

UIScrollView setting page size dynamically

I've done quite a bit of looking into this but have been unsuccessful in finding a solution.
I have a UIScrollView called scrollView inside of UIView. I'm using this scrollView in pagingEnabled mode. Inside of scrollView, I have 3 different views created programmatically. Inside of these 3 different views, I have a bunch of stuff(UILabel,UITextView etc.). All of the views contents are dynamic and determined at runtime.So, i really don't know scrollView.contentSize. If i give the content size of this scrollView,Sometimes ,I have a screen with white blank at the botton of the screen when user scrolls down. My question is : Can i set the content size dynamically for each single page of this scrollView? For example,for page 1 :
self.scrollViewNews.contentSize = CGSizeMake(constant1,constant2);
And set something else for page 2 as well .
I think you're confused about contentSize vs the paging size. The paging size is always the size of the scrollView bounds, and isn't a property you can otherwise set. That is, when you swipe left/right it will "page" by the width of the scrollview.
contentSize is the size of the virtual bounding box of all the subviews within the scrollview. This only serves to limit how far the scrollview will scroll, and for paging, how many times it will page, i.e. contentSize.width / bounds.size.width.
Assuming the scrollView isn't zoomed in/out (zoomScale = 1.0) then you need to position and size your subviews on the virtual 'page boundaries'. They can take up the full page boundary (be sized to match scrollview.bounds) or be inset. If you have some content that is larger/smaller then you'll have to decide if you want to change the scale of that content or size it up/down within the page bounds.
Yes you can dynamically set the content size of the scrollView. Also You can use this method:
self.scrollViewNews.contentSize = CGSizeMake(constant1,constant2);
Nothing wrong this method. You are seeing the blank space at bottom because you ares setting the height of the scrollView's contentSize to a larger value than it's content. That's the issue. Adjust the height according to the contents, blank space will go.
please, try to get the actual content size from the current content.
the content must be some inherited class from the UIView and it has a frame.size.width and frame.size.height property.
you can use those to set the contentSize of your UIScrollview for the current content at that time when you add the content to the UIScrollView.
Yeah. You can set the scrollView size dynamically through
scroller.contentSize = CGSizeMake(1650, 2000);
You can use the following property to set the dynamic frame for each pages
[urScrollView scrollRectToVisible:frame animated:YES];
if you are using pagingEnabled = YES for your scrollview
The below UIScrollViewDelegate delegate will adjust the page and content
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
int page = urScrollView.contentOffset.x/urScrollView.frame.size.width;
pageControl.currentPage = page;
}
//pager action
- (IBAction)changePage:(id)sender{
int page = pageControl.currentPage;
CGRect frame = urScrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
[urScrollView scrollRectToVisible:frame animated:YES];
}
please look at this sample app
Try out this.
- (void)viewDidLoad
{
float sizeOfContent = 0;
UIView *lLast = [scrollView.subviews lastObject];
NSInteger wd = lLast.frame.origin.y;
NSInteger ht = lLast.frame.size.height;
sizeOfContent = wd+ht;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, sizeOfContent);
}

how to force horizontal scrolling only on UITextView

I have a UITextView with contentSize property set to a very large value 1000, the width of the UITextView is 200, scrolling is enabled, horizontal bouncing is enabled and vertical bouncing is disabled but this UITextView still scrolls vertically on large text.
how can I force horizontal scrolling on it ?
p.s. the solution here does't work.
UITextView is a subclass of - UIScrollView. So you can use ScrollViewDelegate method for disabling this.
Use -
- (void)scrollViewDidScroll:(id)aScrollView
{
[aScrollView setContentOffset:CGPointMake([scrollView contentOffset].x, 0.0)];
}
I have tried many different ways to have a horizontal scrolling functionality to work as expected, but non of these methods worked for me, so I created a UIScrollView and added a UILabel as a subview in the scroll view, and setting the frame's width of the label dynamically according to its text and the text font, after that I set the contentSize of the scroll view to be equal to the label's new frame width & height. Using this method you will have a horizontal scrolling text.
Using JAHelia's advice, this worked for me:
self.dishNameLabel.text = #"Here is some very longgggg text to test with";
float width = ceil([self.dishNameLabel.text sizeWithAttributes:#{NSFontAttributeName: [UIFont fontWithName:#"WorkSans-SemiBold" size:15.0]}].width);
self.dishNameLabel.frame = CGRectMake(self.dishNameLabel.frame.origin.x, self.dishNameLabel.frame.origin.y, width, self.dishNameLabel.frame.size.height);
self.dishNameScrollView.contentSize = CGSizeMake( width, self.dishNameScrollView.frame.size.height);
Of course, you will have to replace your text and font with your respective specs

UIScrollView contentSize Issue - With example code

Within my view I create a scrollview with a width of 320 and a height of 70.
Responding to the user touching a button, I expand the scrollview, so it is 380(h) x 320(w) in size.
The code for this is shown below:
CGRect scrollviewFrame = CGRectMake(0, 30, 320, 380);
[scrollView setFrame:scrollviewFrame];
[self layoutScrollImages:YES];
CGSize srect = CGSizeMake([scrollView bounds].size.width, (kNumImages * (kScrollObjHeight + 10)));
[scrollView setContentSize:srect];
The constants mentioned in the above snippet are defined as:
const CGFloat kScrollObjHeight = 80;
const NSUInteger kNumImages = 100;
As I debug this project, I can see that srect is 320 (w) x 8000 (h) in size; however my issue is the scrollable area (where the user can actually touch to scroll the scrollview) remains the same as when it was it's original size.
I'm obviously missing something, does anyone know what it is?
have created a sample project to illustrate the issue I am having, it is available here: http://dl.dropbox.com/u/9930498/ScrollViewTest.zip
The problem in your sample is you have a very odd structure for loading your views. As such the view you're adding to the DetailScrollView instance is the root view of the DetailScrollView.xib, not the scrollview itself, which I believe is what you were expecting.
Fastest way to fix your problem is to adjust the root view in DetailScrollView.xib to autoresize width and height.
A UIView cannot respond to touches that are outside of the bounds of its superview. In your example, it appears that you expand the scroll view, but the scroll view's parent is still only 100 units high.
You should imagine the scrollView as a window, where by the window I mean the frame of the scrollView, which is also the coordinates that the scrollView detects your touches. By setting the contentView as 320 (w) x 8000 (h) you only change the content of the scroll view, which is the complete area behind that window.
By expanding content view, the scrollView can scroll a broader area, but in order to detect touches in a bigger rect, you should change frame of the scroll view.