I set the content size so vertical scrolling becomes activated but I only want the user to be able to scroll north and not south. Any ideas on how I can accomplish this?
//set high greater than view
myScroll.contentSize = CGSize(width: myView.view.frame.width,height: myView.view.frame.height + 190)
I only want the ability to scroll up and disable the ability to scroll down which is the default direction of the scrollview
You can set the content offset of the scroll view to the bottom. i.e.,
myScroll.contentOffset = CGPoint(x: 0,y: 190) // Here 190 is the same value that represent the height increase done in contentSize.
Make a subclass of UIScrollView. Override layoutSubviews to make sure contentOffset.y only increases, except when it's beyond the end of the content range, so the scroll view can bounce at the bottom.
class MyScrollView: UIScrollView {
var priorOffset = CGPoint.zero
override func layoutSubviews() {
var offset = contentOffset
if offset.y < priorOffset.y {
let yMax = contentSize.height - bounds.height
if offset.y < yMax {
offset.y = priorOffset.y
contentOffset = offset
}
}
priorOffset = offset
super.layoutSubviews()
}
}
Result:
select your Scrollview
select identity inspector
set user define attributes (see image)
in image first 435 vertical scrolling & second 116 is horizontal scroll
Note : set your own scrolling
You can set the direction of UIPanGestureRecognizer which is attached to your UIScrollView.
Check out some popular question which has an up-to-date answer, for example this one.
Or just go with pod 'UIPanGestureRecognizerDirection'
Related
By default, scrollView.contentOffset = 0 means left edge (side) of scrollView (UIScrollView).
Is there anyway to change this behavior? Something like making Right-to-Left UIScrollView, that it's contentOffset = 0 means right edge (side) of scrollView.
let rightOffset = scrollView.contentSize.width - scrollView.contentOffset.x
that way you can
Calculate contentOffset from right side of screen in UIScrollView
I have added header with custom view to my TableView. It is used as a search bar, I set contentOffset so it is hidden in beginning.
Everything works as expected but I am wondering if there is any way to make it harder to pull down? (should be stickier) Now it opens with just regular scrolling which is too easy.
EDIT: ScrollViewDidScroll Code
if tableView.contentOffset.y < 80 && tableView.contentOffset.y > 40 {
subscriptionsTableView.contentOffset.y += 0.23
print("Content offset")
print(tableView.contentOffset.y)
}
I had a similiar problem and solved in quite easily in the end. I set the table view top inset to the height of the header
tableView.contentInset.top = -1 * headerView.frame.size.height
This makes the header be shown "off screen", the user is now not being able to scroll to it. I added a code to change the top inset to 0, showing the header, when the user scrolls to the top over the header
func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y < 0 {
UIView.animate(withDuration: 0.25, animations: {
self.tableView.contentInset.top = 0
})
} else if scrollView.contentOffset.y > headerView.frame.size.height {
UIView.animate(withDuration: 0.25, animations: {
self.searchBar.resignFirstResponder()
self.tableView.contentInset.top = -1 * self.headerView.frame.size.height
})
}
}
I also set the top inset back when the user scrolls the table view.
My 2 cents: observe the contentOffset property of the table view (or implement the scrollViewDidScroll: method) and adjust the position of the view you want to stick.
Here is a reference to something that uses the same principle:
make UIView in UIScrollView stick to the top when scrolled up
Is it possible to add fixed content to a UIScrollView?
Make static UIView sticky when UIScrollView is scrolling
I have a UIScrollView A (in fact a UICollectionView) filling the screen inside a UINavigationController B. The controller B's adjustScrollViewInsets is set to true.
I want to hide the navigation bar when user scrolls up, and show it when down. Following is my code:
func scrollViewDidScroll(scrollView: UIScrollView) {
if (self.lastContentOffset < scrollView.contentSize.height - scrollView.frame.size.height && self.lastContentOffset > scrollView.contentOffset.y) {
// dragging down
if self.navigationController!.navigationBarHidden {
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
} else if (self.lastContentOffset > 0 && self.lastContentOffset < scrollView.contentOffset.y) {
// dragging up
if !self.navigationController!.navigationBarHidden {
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
}
self.lastContentOffset = scrollView.contentOffset.y
}
Now the problem is, since the screen of iPhone 6+ is too large, the contentSize of the scroll view A is smaller than its frame(i.e. the full screen frame) when the navigation bar is hidden. In such circumstance, the scroll view will not be scrollable, and the navigation bar will never be back again.
I want to manually maintain the height of the contentSize of A to screen at least height + 1, but don't know how to do this. Could anyone help? Or provide a better solution?
BTW, I am using iOS 8 and Swift.
Lets say you need to keep the minimum content size of scroll view to 100(of course this will be dynamic and vary according to device)
NSInteger minScrollViewContentHeight = 100;
After populating the scroll view with content, you need to check if the scroll view's content size is less than minimum required scroll views content size. If its lesser than the required content size than you need to set the minimum content size of the scroll view as follows -
if(scrollView.contentSize.height < minScrollViewContentHeight)
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width, minScrollViewContentHeight)];
Off the top of my head (I'm on a phone), contentSize is not read-only I think.
How about changing it manually to the desired amount depending on the circumstances of scrolling direction etc?
Something like:
IF navbar is hidden THEN contentSize = whatever
An option would be to use the appearance and disappearance of cells to trigger the show/hide.
Use the delegate methods collectionView:willDisplayCell:forItemAtIndexPath: and collectionView:didEndDisplayingCell:forItemAtIndexPath: to detect movement. You can work out the direction from the index change of the cells being shown or removed. If you cannot scroll off screen then nothing happens.
You have to change no offset (which is actually just scrolling position), but contentSize itself. That means, that when you hide navigation bar, increase contentSize by navigation height (don't remember numbers) and when you show navigation bar, decrease contentSize. Or... Use AutoLayout and layoutIfNeeded method after showing/hiding navigation bar.
I stumbled upon a similar problem. I needed a minimum scrollable area for a tableview i was using.
ScrollView might be a bit easier since you can directly modify the contentView size.
If you're using autoLayout, try adding equal heights constraint between the contentView and the scrollView itself. Something along the lines of contentView.height = scrollView.height + scrollMin;
For my tableView i had to subclass UITableView and override the contentSize setter.
#define MIN_SCROLL 60
- (void)setContentSize:(CGSize)contentSize {
//take insets into account
UIEdgeInsets insets = self.contentInset;
CGFloat minHeight = self.frame.size.height - insets.top - insets.bottom + MIN_SCROLL;
if(contentSize.height < minHeight) {
contentSize.height = minHeight;
}
[super setContentSize:contentSize];
}
I would like to know how to implement parallax scrolling similar to Yahoo News Digest app. In which when user scroll horizontally background image scrolls in a different speed with the paging is enabled.
May be they do it with a ScrollView with a background view. Not exactly sure. Hint to implement such scrolling would be great. I have checked similar questions but couldn't find the answer I was looking for.
I've done this before with 2 scrollviews.
You have the main detail scroll view and then the parallax scroll view behind it (or wherever you want it).
Then you become the delegate of the detail scrollview.
In the method scrollView:didScroll you can then adjust the scroll of the parallax view.
If you're just doing the x axis then you want something like this...
CGFloat detailMaxOffset = self.detailScrollView.contentSize.width - CGRectGetWidth(self.scrollView.frame);
CGFloat percentage = self.detailScrollView.contentOffset.x / maxOffset;
CGFloat parallaxMaxOffset = self.parallaxScrollView.contentSize.width - CGRectGetWidth(self.parallaxScrollView.frame);
[self.parallaxScrollView setContentOffset:CGPointMake(percentage * parallaxOffset, 0);
This will set the scrollviews content offset "percentage" to be the same on each.
To get the parallax effect you just need to make the contentSize of each scrollview different.
If the parallax scroll view has a bigger content size than the detail scroll view it will scroll faster. If it has a smaller content size it will scroll slower.
Here is the answer. I subclass it from uitableview so that data can be reusable and wrap it in a uiview. https://github.com/michaelhenry/MHYahooParallaxView
Thanks,
Kel
100% working and dam easy
Take a view on imageview exactly size of image view.
by default alpha for view set 0.
//MARK: Scroll View Delegate methods
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSLog(#"X: %f Y: %f",scrollView.contentOffset.x,scrollView.contentOffset.y);
CGFloat scrollY = _mainScrollView.contentOffset.y;
CGFloat height = _alphaView.frame.size.height;
CGFloat alphaMonitor = scrollY/height;
_alphaView.alpha = alphaMonitor;
}
Swift 3
Here's how I got a parallax effect to work in Swift 3 for a vertical scroll in a tvOS app.
In ViewDidLoad():
parallaxScrollView.delegate = self
detailScrollView.delegate = self
And following:
override func viewDidLayoutSubviews() {
self.detailScrollView!.contentSize = CGSize(width: 1920, height: 2700)
self.parallaxScrollView?.contentSize = CGSize(width: 1920, height: 3000)
}
//// THE SCROLL VIEW
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Parallax effect
let detailMaxOffset = self.detailScrollView.contentSize.height - self.detailScrollView.frame.height;
let percentage = self.detailScrollView.contentOffset.y / detailMaxOffset
let parallaxMaxOffset = self.parallaxScrollView.contentSize.height - self.parallaxScrollView.frame.height;
let parallaxOffset = CGPoint(x:0,y:(percentage * parallaxMaxOffset))
self.parallaxScrollView.setContentOffset((parallaxOffset), animated: false)
}
I have a UIView like iPhone's Springboard. I have created it using a UIScrollView and UIButtons. I want to disable horizontal scrolling on said scrollview. I want only vertical scrolling. How do I accomplish this?
You have to set the contentSize property of the UIScrollView. For example, if your UIScrollView is 320 pixels wide (the width of the screen), then you could do this:
CGSize scrollableSize = CGSizeMake(320, myScrollableHeight);
[myScrollView setContentSize:scrollableSize];
The UIScrollView will then only scroll vertically, because it can already display everything horizontally.
UPDATED: (After #EranMarom pointed out on his comment)
You can stop horizontal scrolling or vertical scrolling in the ScrollViewDelegate Method.
Here it is how,
Stops Horizontal Scrolling:
If you want to scroll horizontally, then you need to increase the contentOffset.x. Preventing that stops the scrollview scroll in horizontal direction.
- (void)scrollViewDidScroll:(UIScrollView *)sender {
sender.contentOffset.x = 0.0
}
Stops Vertical Scrolling:
If you want to scroll vertically, then you need to increase the contentOffset.y. Preventing that stops the scrollview scroll in vertical direction.
- (void)scrollViewDidScroll:(UIScrollView *)sender {
sender.contentOffset.y = 0.0
}
Above code prevents the changes in x and y of a scrollview contentOffset and it leads to stop the scrolling in scrollViewDidScroll: method.
since iOS7 use
self.automaticallyAdjustsScrollViewInsets = NO;
//and create you page scroller with 3 pages
self.pageView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[self.pageView setContentSize:CGSizeMake(self.view.frame.size.width*3, self.view.frame.size.height)];
[self.pageView setShowsVerticalScrollIndicator:NO];
[self.pageView setPagingEnabled:YES];
[self.view addSubview:self.pageView];
Swift solution
Create two outlets, one for your view and one for your scroll view:
#IBOutlet weak var myView: UIView!
#IBOutlet weak var scrollView: UIScrollView!
Then in your viewDidLayoutSubviews you can add the following code:
let scrollSize = CGSize(width: myView.frame.size.width,
height: myView.frame.size.height)
scrollView.contentSize = scrollSize
What we've done is collected the height and width of the view and set the scrollViews content size to match it. This will stop your scrollview from scrolling horizontally.
More Thoughts:
CGSizeMake takes a width & height using CGFloats. You may need to use your UIScrollViews existing height for the second parameter. Which would look like this:
let scrollSize = CGSize(width: myView.frame.size.width,
height: scrollView.contentSize.height)
In my case, with Swift 4.2 you can use:
Disable vertical scroll:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
scrollView.contentOffset.y = 0.0
}
Disable horizontal scroll:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
scrollView.contentOffset.x = 0.0
}
In my case the width of the contentView was greater than the width of UIScrollView and that was the reason for unwanted horizontal scrolling. I solved it by setting the width of contentView equal to width of UIScrollView.
Hope it helps someone
You can select the view, then under Attributes Inspector uncheck User Interaction Enabled .
Introduced in iOS 11 is a new property on UIScrollView
var contentLayoutGuide: UILayoutGuide
The documentation states that you:
Use this layout guide when you want to create Auto Layout constraints related to the content area of a scroll view.
Along with any other Autolayout constraints that you might be adding you will want to constrain the widthAnchor of the UIScrollView's contentLayoutGuide to be the same size as the "frame". You can use the frameLayoutGuide (also introduced in iOS 11) or any external width (such as your superView's.)
example:
NSLayoutConstraint.activate([
scrollView.contentLayoutGuide.widthAnchor.constraint(equalTo: self.widthAnchor)
])
Documentation:
https://developer.apple.com/documentation/uikit/uiscrollview/2865870-contentlayoutguide
#Gulfam Khan's answer is the correct one, I am adding imagery to help the concept get more visibility.
When we set the contentView to have equal width's with the Scroll view, if the multiplier is even slightly greater than 1:1, then we will get horizontal scrolling.
Here is what it produces:
If you do not want horizontal scrolling, you most likely do not have horizontal content that exceeds the width of the superview.
Therefore if you ensure the contentView width does not exceed the width of the scroll view, that will automatically resolve the problem as UIKit recognizes there is no horizontal content to scroll to. Like so:
Now you should only see vertical:
I had the tableview contentInset set in viewDidLoad (as below) that what causing the horizontal scrolling
self.tableView.contentInset = UIEdgeInsetsMake(0, 30, 0, 0);
Check if there are any tableview contentInset set for different reasons and disable it
I struggled with this for some time trying unsuccessfully the various suggestions in this and other threads.
However, in another thread (not sure where) someone suggested that using a negative constraint on the UIScrollView worked for him.
So I tried various combinations of constraints with inconsistent results. What eventually worked for me was to add leading and trailing constraints of -32 to the scrollview and add an (invisible) textview with a width of 320 (and centered).
Try This:
CGSize scrollSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, scrollHeight);
[scrollView setContentSize: scrollSize];
Disable horizontal scrolling by overriding contentOffset property in subclass.
override var contentOffset: CGPoint {
get {
return super.contentOffset
}
set {
super.contentOffset = CGPoint(x: 0, y: newValue.y)
}
}
Once I did it replacing the UIScrollView with a UITableView with only 1 cell, it worked fine.
Use this single line.
self.automaticallyAdjustsScrollViewInsets = NO;