Disable Bounce Effect in Uipageviewcontroller

How to remove UIPageViewController's Bounce effect in iOS for Swift 3/4

First find scrollview inside your UIPageViewController and add UIScrollViewDelegate

for view in self.pageViewController!.view.subviews {
if let subView = view as? UIScrollView {
subView.delegate = self
subView.isScrollEnabled = true
subView.bouncesZoom = false

}
}

(Extends UIScrollViewDelegate in your ViewController class)

For UIScrollViewDelegate on method "scrollViewDidScroll" and "scrollViewWillEndDragging" you can add something like this

func scrollViewDidScroll(_ scrollView: UIScrollView) {

if(currentIndex == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width){
scrollView.contentOffset = CGPoint(x:scrollView.bounds.size.width, y:0.0)
}else if(currentIndex == 2 && scrollView.contentOffset.x > scrollView.bounds.size.width) {
scrollView.contentOffset = CGPoint(x:scrollView.bounds.size.width, y:0.0)
}
}

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if(currentIndex == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width){
scrollView.contentOffset = CGPoint(x:scrollView.bounds.size.width, y:0.0)
}else if(currentIndex == 2 && scrollView.contentOffset.x > scrollView.bounds.size.width) {
scrollView.contentOffset = CGPoint(x:scrollView.bounds.size.width, y:0.0)
}
}

Disable bounce effect in UIPageViewController

Thus far, none of the answers actually work fully. The edge case that they all fail on is this:

  1. Scroll to page 2.
  2. Using one finger, drag towards page 1.
  3. Place a second finger on the screen and drag towards page 1.
  4. Lift the first finger.
  5. Repeat until you have dragged past page 0.

In that situation, every solution I've seen so far goes past the bounds of page 0. The core problem is that the underlying API is broken, and begins reporting a content offset relative to page 0 without calling our callback to let us know that it is showing a different page. Throughout this process, the API still claims to be showing page 1, going towards page zero even while it is really on page zero going towards page -1.

The workaround for this design flaw is remarkably ugly, but here it is:

@property (weak,nonatomic) UIPageControl *pageControl;
@property (nonatomic,assign) BOOL shouldBounce;
@property (nonatomic,assign) CGFloat lastPosition;
@property (nonatomic,assign) NSUInteger currentIndex;
@property (nonatomic,assign) NSUInteger nextIndex;

- (void)viewDidLoad {

[super viewDidLoad];

...

self.shouldBounce = NO;

for (id testView in self.pageController.view.subviews) {
UIScrollView *scrollView = (UIScrollView *)testView;
if ([scrollView isKindOfClass:[UIScrollView class]]) {
scrollView.delegate = self;
// scrollView.bounces = self.shouldBounce;
}
}
}

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController{

return (NSInteger)self.currentIndex;
}

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers{

id controller = [pendingViewControllers firstObject];
self.nextIndex = [viewControllers indexOfObject:controller];
}

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed{

if(completed) {
// At this point, we can safely query the API to ensure
// that we are fully in sync, just in case.
self.currentIndex = [viewControllers indexOfObject:[pageViewController.viewControllers objectAtIndex:0]];
[self.pageControl setCurrentPage:self.currentIndex];
}

self.nextIndex = self.currentIndex;

}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
/* The iOS page view controller API is broken. It lies to us and tells us
that the currently presented view hasn't changed, but under the hood, it
starts giving the contentOffset relative to the next view. The only
way to detect this brain damage is to notice that the content offset is
discontinuous, and pretend that the page changed.
*/
if (self.nextIndex > self.currentIndex) {
/* Scrolling forwards */

if (scrollView.contentOffset.x < (self.lastPosition - (.9 * scrollView.bounds.size.width))) {
self.currentIndex = self.nextIndex;
[self.pageControl setCurrentPage:self.currentIndex];
}
} else {
/* Scrolling backwards */

if (scrollView.contentOffset.x > (self.lastPosition + (.9 * scrollView.bounds.size.width))) {
self.currentIndex = self.nextIndex;
[self.pageControl setCurrentPage:self.currentIndex];
}
}

/* Need to calculate max/min offset for *every* page, not just the first and last. */
CGFloat minXOffset = scrollView.bounds.size.width - (self.currentIndex * scrollView.bounds.size.width);
CGFloat maxXOffset = (([viewControllers count] - self.currentIndex) * scrollView.bounds.size.width);

NSLog(@"Page: %ld NextPage: %ld X: %lf MinOffset: %lf MaxOffset: %lf\n", (long)self.currentIndex, (long)self.nextIndex,
(double)scrollView.contentOffset.x,
(double)minXOffset, (double)maxXOffset);

if (!self.shouldBounce) {
CGRect scrollBounds = scrollView.bounds;
if (scrollView.contentOffset.x <= minXOffset) {
scrollView.contentOffset = CGPointMake(minXOffset, 0);
// scrollBounds.origin = CGPointMake(minXOffset, 0);
} else if (scrollView.contentOffset.x >= maxXOffset) {
scrollView.contentOffset = CGPointMake(maxXOffset, 0);
// scrollBounds.origin = CGPointMake(maxXOffset, 0);
}
[scrollView setBounds:scrollBounds];
}
self.lastPosition = scrollView.contentOffset.x;
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
/* Need to calculate max/min offset for *every* page, not just the first and last. */
CGFloat minXOffset = scrollView.bounds.size.width - (self.currentIndex * scrollView.bounds.size.width);
CGFloat maxXOffset = (([viewControllers count] - self.currentIndex) * scrollView.bounds.size.width);

if (!self.shouldBounce) {
if (scrollView.contentOffset.x <= minXOffset) {
*targetContentOffset = CGPointMake(minXOffset, 0);
} else if (scrollView.contentOffset.x >= maxXOffset) {
*targetContentOffset = CGPointMake(maxXOffset, 0);
}
}
}

Basically, it records the offset for each scroll event. If the scroll position has moved a distance that is impossible (I arbitrarily picked 90% of the width of the screen) in the opposite direction from the direction of scrolling, the code assumes that iOS is lying to us, and behaves as though the transition finished properly, treating the offsets as being relative to the new page instead of the old one.

Disable UIPageViewController bouncing - Swift

I don't know if you can. UIPageController is not very customizable.

Personally, when I want to scroll between UIViewController, I prefer using a simple UIViewController, which will be a container, with an UIScrollView in it. Then I add programmatically all the controllers in the contentSize of the UIScrollView. You have to add all the controllers as child of the container.

UPDATE iOS 9

func setupDetailViewControllers() {
var previousController: UIViewController?
for controller in self.controllers {
addChildViewController(controller)
addControllerInContentView(controller, previousController: previousController)
controller.didMoveToParentViewController(self)
previousController = controller
}
}

func addControllerInContentView(controller: UIViewController, previousController: UIViewController?) {
contentView.addSubview(controller.view)
controller.view.translatesAutoresizingMaskIntoConstraints = false

// top
controller.view.topAnchor.constraintEqualToAnchor(contentView.topAnchor).active = true

// bottom
controller.view.bottomAnchor.constraintEqualToAnchor(contentView.bottomAnchor).active = true

// trailing
trailingContentViewConstraint?.active = false
trailingContentViewConstraint = controller.view.trailingAnchor.constraintEqualToAnchor(contentView.trailingAnchor)
trailingContentViewConstraint?.active = true

// leading
let leadingAnchor = previousController?.view.trailingAnchor ?? contentView.leadingAnchor
controller.view.leadingAnchor.constraintEqualToAnchor(leadingAnchor).active = true

// width
controller.view.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
}

PREVIOUS ANSWER

Like so :

scrollView is an IBOutlet with contraints to each edge of the ContainerViewController

class ContainerViewController: UIViewController {

@IBOutlet var scrollView: UIScrollView!

override func viewDidLoad() {
super.viewDidLoad()
scrollView.bounces = false
scrollView.pagingEnabled = true
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()

}

override func viewDidLayoutSubviews() {
initScrollView()
}

func initScrollView(){
let viewController1 = storyboard?.instantiateViewControllerWithIdentifier("ViewController1") as! ViewController1

viewController1.willMoveToParentViewController(self)
viewController1.view.frame = scrollView.bounds

let viewController2 = storyboard?.instantiateViewControllerWithIdentifier("ViewController2") as! ViewController2

viewController2.willMoveToParentViewController(self)
viewController2.view.frame.size = scrollView.frame.size
viewController2.view.frame.origin = CGPoint(x: view.frame.width, y: 0)

scrollView.contentSize = CGSize(width: 2 * scrollView.frame.width, height: scrollView.frame.height)

scrollView.addSubview(viewController2.view)
self.addChildViewController(viewController2)
viewController2.didMoveToParentViewController(self)

scrollView.addSubview(viewController1.view)
self.addChildViewController(viewController1)
viewController1.didMoveToParentViewController(self)
}}

No bouncing on UIPageViewController?

It can't be done without hacks. See this question: Disable bounce effect in UIPageViewController

On the few occassions I've needed to disable the bounce, I've just put all of my views in a UIScrollView and turned off bounces, instead of using UIPageViewController

UIPageViewController don't move when there's no more pages?

Disable bounce effect in UIPageViewController:

for (UIView *view in self.pageViewController.view.subviews ) {
if ([view isKindOfClass:[UIScrollView class]]) {
UIScrollView *scroll = (UIScrollView *)view;
scroll.bounces = NO;
}
}


Related Topics



Leave a reply



Submit