搜索了很多这个,但还没找到合适的解决方案。
是否有可能禁用UIPageViewController
的反弹效果并仍然使用UIPageViewControllerTransitionStyleScroll
?
<UIScrollViewDelegate>
委托添加到您的UIPageViewController标头中viewDidLoad
中的父代:
for (UIView *view in self.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)view).delegate = self;
break;
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (_currentPage == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width) {
scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0);
} else if (_currentPage == totalViewControllersInPageController-1 && scrollView.contentOffset.x > scrollView.bounds.size.width) {
scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0);
}
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
if (_currentPage == 0 && scrollView.contentOffset.x <= scrollView.bounds.size.width) {
*targetContentOffset = CGPointMake(scrollView.bounds.size.width, 0);
} else if (_currentPage == totalViewControllersInPageController-1 && scrollView.contentOffset.x >= scrollView.bounds.size.width) {
*targetContentOffset = CGPointMake(scrollView.bounds.size.width, 0);
}
}
放入viewDidLoad
的代码:
for subview in self.view.subviews {
if let scrollView = subview as? UIScrollView {
scrollView.delegate = self
break;
}
}
scrollViewDidScroll的实现:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (currentPage == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width) {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0);
} else if (currentPage == totalViewControllersInPageController - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width) {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0);
}
}
scrollViewWillEndDragging的实现:
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if (currentPage == 0 && scrollView.contentOffset.x <= scrollView.bounds.size.width) {
targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0);
} else if (currentPage == totalViewControllersInPageController - 1 && scrollView.contentOffset.x >= scrollView.bounds.size.width) {
targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0);
}
}
禁用UIPageViewController的反弹
Swift 2.2
1)将UIScrollViewDelegate添加到UIPageViewController
extension PageViewController: UIScrollViewDelegate
2)添加到viewDidLoad
for view in self.view.subviews {
if let scrollView = view as? UIScrollView {
scrollView.delegate = self
}
}
3)添加UIScrollViewDelegate方法
func scrollViewDidScroll(scrollView: UIScrollView) {
if currentIndex == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
} else if currentIndex == totalViewControllers - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 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)
} else if currentIndex == totalViewControllers - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
}
}
我不知道如何正确管理currentIndex
但最终做到了
extension Main: UIPageViewControllerDelegate {
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {
guard let viewController = pageViewController.viewControllers?.first,
index = viewControllerDatasource.indexOf(viewController) else {
fatalError("Can't prevent bounce if there's not an index")
}
currentIndex = index
}
}
}
UIPageViewController实际上并没有为你做太多。您可以非常轻松地使用带有视图控制器的UIScrollView,并禁用它的反弹。
做一些像
int x=0;
for (NSString *storyboardID in storyboardIDs){
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:storyboardID];
[self addChildViewController:vc];
vc.view.frame = CGRectMake(x++*vc.view.frame.size.width, 0, vc.view.frame.size.width, vc.view.frame.size.height);
[self.scrollView addSubview:vc.view];
[vc didMoveToParentViewController:self];
self.scrollView.contentSize = CGSizeMake(storyboardIDs.count*vc.view.frame.size.width, vc.view.frame.size.height);
}
另一个选项是设置ScrollView.bounce = false。它解决了我的问题与pageViewController(当然不是关于ScrollView)滚动弹跳。禁用弹跳,并且所有页面都可以滚动而不会跳出。
编辑:不要使用此解决方案。后来我了解到,这引入了一个大约5%的时间,用户无法在同一方向上页面的错误。他们必须回页,然后再转发继续。
如果你正在使用UIPageViewControllerDataSource
,一个相对简单的解决方法(并且有点hacky)是每次调用pageViewController:viewControllerBeforeViewController:
委托方法时禁用弹跳。这是一个示例实现:
@interface YourDataSourceObject ()
@property (strong, nonatomic) UIScrollView *scrollView;
@end
@implementation
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
if (!self.scrollView) {
for (UIView *view in pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
self.scrollView = (UIScrollView *)view;
}
}
}
self.scrollView.bounces = NO;
// Your other logic to return the correct view controller.
}
@end
如果你试图禁用UIPageViewController.scrollView
的弹跳,你肯定会得到一个破碎的pageViewController
:刷卡不会起作用。所以,不要这样做:
self.theScrollView.alwaysBounceHorizontal = NO;
self.theScrollView.bounces = NO;
在scrollView
子视图中搜索UIPageViewController
引用的解决方案仅用于完全禁用滚动:
@interface MyPageViewController : UIPageViewController
@property (nonatomic, assign) BOOL scrollEnabled;
@end
@interface MyPageViewController ()
@property (nonatomic, weak) UIScrollView *theScrollView;
@end
@implementation MyPageViewController
- (void)viewDidLoad
{
[super viewDidLoad];
for (UIView *view in self.view.subviews) {
if ([view isKindOfClass:UIScrollView.class]) {
self.theScrollView = (UIScrollView *)view;
break;
}
}
}
- (void)setScrollEnabled:(BOOL)scrollEnabled
{
_scrollEnabled = scrollEnabled;
self.theScrollView.scrollEnabled = scrollEnabled;
}
@end
UIScrollView
类别(例如Custom.Scrolling)。 UIScrollView
已经是他们的手势识别器的代表。UIViewController
(也就是baseVC
里面有UIPageViewController
)通过AppDelegate
分享。否则,您可以使用运行时(#import <objc/runtime.h>
)并将引用属性(到您的控制器baseVC
)添加到类别。@interface UIScrollView (CustomScrolling) <UIGestureRecognizerDelegate>
@end
@implementation UIScrollView (CustomScrolling)
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
UIViewController * baseVC = [(AppDelegate *)[[UIApplication sharedApplication] delegate] baseVC];
if (gestureRecognizer.view == baseVC.pageViewController.theScrollView) {
NSInteger page = [baseVC selectedIndex];
NSInteger total = [baseVC viewControllers].count;
UIPanGestureRecognizer *recognizer = (UIPanGestureRecognizer *)gestureRecognizer;
CGPoint velocity = [recognizer velocityInView:self];
BOOL horizontalSwipe = fabs(velocity.x) > fabs(velocity.y);
if (!horizontalSwipe) {
return YES;
}
BOOL scrollingFromLeftToRight = velocity.x > 0;
if ((scrollingFromLeftToRight && page > 0) || (!scrollingFromLeftToRight && page < (total - 1))) {
return YES;
}
return NO;
}
return YES;
}
@end
#import "UIScrollView+CustomScrolling.h"
中导入类别文件baseVC
,它使用UIPageViewController。@Dong Ma的方法很完美,但可以稍微改进和简化。
要放入viewDidLoad的代码:
for subview in view.subviews {
if let scrollView = subview as? UIScrollView {
scrollView.delegate = self
break
}
}
scrollViewDidScroll的实现:
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (currentPage == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width) || (currentPage == totalNumberOfPages - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width) {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
}
}
scrollViewWillEndDragging的实现:
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if (currentPage == 0 && scrollView.contentOffset.x <= scrollView.bounds.size.width) || (currentPage == totalNumberOfPages - 1 && scrollView.contentOffset.x >= scrollView.bounds.size.width) {
targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0)
}
}
编辑回答董马,其中:
currentIndex
时刷卡非常快信息:
如何:
UIViewController
,其中UIPageViewController
被添加为子VC。class ViewController: UIViewController {
var pageNavigationController: UIPageViewController!
private var lastPosition: CGFloat
private var nextIndex: Int
var currentIndex: Int
// rest of UI's setups
}
ViewController
设为UIPageViewController
的代表:extension ViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
guard
let currentVisibleViewController = pageViewController.viewControllers?.first,
let nextIndex = pageViewControllers.firstIndex(of: currentVisibleViewController)
else {
return
}
self.nextIndex = nextIndex
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed, let currentVisibleViewController = pageViewController.viewControllers?.first, let newIndex = pageViewControllers.firstIndex(of: currentVisibleViewController) {
self.currentIndex = newIndex
}
self.nextIndex = self.currentIndex
}
}
ViewController
设置为UIPageController
的数据源:extension ViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
// provide next VC
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
// provide prev VC
}
// IMPORTANT: that's the key why it works, don't forget to add it
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
return currentIndex
}
}
ViewController
设置为UIPageViewController
的UIScrollView
的代表来“禁用”弹跳:// MARK: - UIScrollViewDelegate (disable bouncing for UIPageViewController)
extension BasePaginationVC: UIScrollViewDelegate {
func attachScrollViewDelegate() {
for subview in pageNavigationController.view.subviews {
if let scrollView = subview as? UIScrollView {
scrollView.delegate = self
lastPosition = scrollView.contentOffset.x
break
}
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
switch UIView.userInterfaceLayoutDirection(for: view.semanticContentAttribute) {
case .leftToRight:
if nextIndex > currentIndex {
if scrollView.contentOffset.x < (lastPosition - (0.9 * scrollView.bounds.size.width)) {
currentIndex = nextIndex
}
} else {
if scrollView.contentOffset.x > (lastPosition + (0.9 * scrollView.bounds.size.width)) {
currentIndex = nextIndex
}
}
if currentIndex == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
} else if currentIndex == pageViewControllers.count - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
}
case .rightToLeft:
if nextIndex > currentIndex {
if scrollView.contentOffset.x > (lastPosition + (0.9 * scrollView.bounds.size.width)) {
currentIndex = nextIndex
}
} else {
if scrollView.contentOffset.x < (lastPosition - (0.9 * scrollView.bounds.size.width)) {
currentIndex = nextIndex
}
}
if currentIndex == pageViewControllers.count - 1 && scrollView.contentOffset.x < scrollView.bounds.size.width {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
} else if currentIndex == 0 && scrollView.contentOffset.x > scrollView.bounds.size.width {
scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
}
@unknown default:
fatalError("unknown default")
}
lastPosition = scrollView.contentOffset.x
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
switch UIView.userInterfaceLayoutDirection(for: view.semanticContentAttribute) {
case .leftToRight:
if currentIndex == 0 && scrollView.contentOffset.x <= scrollView.bounds.size.width {
targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0)
} else if currentIndex == pageViewControllers.count - 1 && scrollView.contentOffset.x >= scrollView.bounds.size.width {
targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0)
}
case .rightToLeft:
if currentIndex == pageViewControllers.count - 1 && scrollView.contentOffset.x <= scrollView.bounds.size.width {
targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0)
} else if currentIndex == 0 && scrollView.contentOffset.x >= scrollView.bounds.size.width {
targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0)
}
@unknown default:
fatalError("unknown default")
}
}
}