La pagination dans UIScrollView est une excellente fonctionnalité, ce dont j'ai besoin ici est de définir la pagination à une distance plus petite, par exemple je veux que mon UIScrollView pagine moins que la largeur du cadre de l'UIScrollView. Merci
Réponses
Trop de publicités?Il existe un UIScrollView
que vous pouvez utiliser. Définissez votre classe comme le délégué de la vue défilante, puis mettez en œuvre la méthode suivante :
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
CGFloat kMaxIndex = 23;
CGFloat targetX = scrollView.contentOffset.x + velocity.x * 60.0;
CGFloat targetIndex = 0.0;
if (velocity.x > 0) {
targetIndex = ceil(targetX / (kCellWidth + kCellSpacing));
} else if (velocity.x == 0) {
targetIndex = round(targetX / (kCellWidth + kCellSpacing));
} else if (velocity.x < 0) {
targetIndex = floor(targetX / (kCellWidth + kCellSpacing));
}
if (targetIndex < 0)
targetIndex = 0;
if (targetIndex > kMaxIndex)
targetIndex = kMaxIndex;
targetContentOffset->x = targetIndex * (kCellWidth + kCellSpacing);
//scrollView.decelerationRate = UIScrollViewDecelerationRateFast;//uncomment this for faster paging
}
Le paramètre de vélocité est nécessaire pour s'assurer que le défilement est naturel et ne s'arrête pas brusquement lorsqu'une touche se termine alors que votre doigt est toujours en mouvement. La largeur et l'espacement des cellules correspondent à la largeur de la page et à l'espacement entre les pages de votre vue. Dans ce cas, j'utilise une UICollectionView
.
-
Modifiez la taille de votre scrollView en fonction de la taille de la page que vous souhaitez.
-
Définissez votre
scroll.clipsToBounds = NO
-
Créez une sous-classe UIView (par exemple HackClipView) et surchargez la méthode hitTest:withEvent :.
-(UIView *) hitTest:(CGPoint) point withEvent:(UIEvent *)event { UIView* child = [super hitTest:point withEvent:event]; if (child == self && self.subviews.count > 0) { return self.subviews[0]; } return child; }
-
Définissez le
HackClipView.clipsToBounds = YES
-
Placez votre scrollView dans ce HackClipView (avec la taille totale de défilement que vous souhaitez).
Voir cette réponse pour plus de détails
Mise à jour : Comme indiqué dans la réponse de Lucius, vous pouvez maintenant mettre en œuvre l'approche de l'UE. UIScollViewDelegate
et utiliser le protocole - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
méthode. Comme le targetContentOffset
est un pointeur. L'utilisation de cette méthode ne vous garantira pas le même résultat avec les pages en vue défilante, car l'utilisateur peut faire défiler plusieurs pages à la fois. Mais le fait de définir le descelerationRate
a fast
sera presque vous donnent le même résultat
Vous devez désactiver la pagination et ajouter une UIPanGestureRecognizer à votre vue de défilement et gérer la pagination vous-même.
- (void)viewDidLoad {
[super viewDidLoad];
CGRect viewRect = self.view.bounds; // View controller's view bounds
theScrollView = [[UIScrollView alloc] initWithFrame:viewRect];
theScrollView.scrollsToTop = NO;
theScrollView.pagingEnabled = NO;
theScrollView.delaysContentTouches = NO;
theScrollView.delegate = self;
[self.view addSubview:theScrollView];
UIPanGestureRecognizer * peter = [[[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(handlePan:)]
autorelease];
[theScrollView addGestureRecognizer:peter];
}
-(void)handlePan:(UIPanGestureRecognizer*)recognizer{
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:{
// panStart and startPoint are instance vars for the viewContainer
panStart = theScrollView.contentOffset;
startPoint = [recognizer locationInView:theScrollView];
break;
}
case UIGestureRecognizerStateChanged:{
CGPoint newPoint = [recognizer locationInView:theScrollView];
CGFloat delta = startPoint.x - newPoint.x;
if ( abs(delta) > 2)
theScrollView.contentOffset = CGPointMake( theScrollView.contentOffset.x + delta, 0);
CGFloat moveDelta = panStart.x - theScrollView.contentOffset.x;
// current witdh should hold the currently displayed page/view in theScrollView
if ( abs(moveDelta) > (currentWidth * 0.40)){
panStart = theScrollView.contentOffset;
startPoint = newPoint;
//NSLog(@"delta is bigger");
if ( moveDelta < 0 )
[self incrementPageNumber]; // you should implement this method and present the next view
else
[self decrementPageNumber]; // you should implement this method and present the previous view
recognizer.enabled = NO; // disable further event until view change finish
}
break;
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
recognizer.enabled = YES;
[self showDocumentPage:currentPage];
break;
default:
break;
}
}
Solution Swift 4.1 qui simplifie la réutilisation :
/// Protocol that simplifies custom page size configuration for UIScrollView.
/// Sadly, can not be done better due to protocol extensions limitations - https://stackoverflow.com/questions/39487168/non-objc-method-does-not-satisfy-optional-requirement-of-objc-protocol
/// - note: Set `.decelerationRate` to `UIScrollViewDecelerationRateFast` for a fancy scrolling animation.
protocol ScrollViewCustomHorizontalPageSize: UIScrollViewDelegate {
/// Custom page size
var pageSize: CGFloat { get }
/// Helper method to get current page fraction
func getCurrentPage(scrollView: UIScrollView) -> CGFloat
/// Helper method to get targetContentOffset. Usage:
///
/// func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
/// targetContentOffset.pointee.x = getTargetContentOffset(scrollView: scrollView, velocity: velocity)
/// }
func getTargetContentOffset(scrollView: UIScrollView, velocity: CGPoint) -> CGFloat
/// Must be implemented. See `getTargetContentOffset` for more info.
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
}
extension ScrollViewCustomHorizontalPageSize {
func getCurrentPage(scrollView: UIScrollView) -> CGFloat {
return (scrollView.contentOffset.x + scrollView.contentInset.left) / pageSize
}
func getTargetContentOffset(scrollView: UIScrollView, velocity: CGPoint) -> CGFloat {
let targetX: CGFloat = scrollView.contentOffset.x + velocity.x * 60.0
var targetIndex = (targetX + scrollView.contentInset.left) / pageSize
let maxOffsetX = scrollView.contentSize.width - scrollView.bounds.width + scrollView.contentInset.right
let maxIndex = (maxOffsetX + scrollView.contentInset.left) / pageSize
if velocity.x > 0 {
targetIndex = ceil(targetIndex)
} else if velocity.x < 0 {
targetIndex = floor(targetIndex)
} else {
let (maxFloorIndex, lastInterval) = modf(maxIndex)
if targetIndex > maxFloorIndex {
if targetIndex >= lastInterval / 2 + maxFloorIndex {
targetIndex = maxIndex
} else {
targetIndex = maxFloorIndex
}
} else {
targetIndex = round(targetIndex)
}
}
if targetIndex < 0 {
targetIndex = 0
}
var offsetX = targetIndex * pageSize - scrollView.contentInset.left
offsetX = min(offsetX, maxOffsetX)
return offsetX
}
}
Il suffit de se conformer à ScrollViewCustomPageSize
dans votre délégué UIScrollView/UITableView/UICollectionView et vous avez terminé, par exemple :
extension MyCollectionViewController: ScrollViewCustomPageSize {
var pageSize: CGFloat {
return 200
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
targetContentOffset.pointee.x = getTargetContentOffset(scrollView: scrollView, velocity: velocity)
}
}
Pour un défilement fantaisiste, je recommande aussi de définir collectionView.decelerationRate = UIScrollViewDecelerationRateFast
Définissez le contentOffset
sur -(void)scrollViewDidScroll:(UIScrollView *)scrollView
méthode.
Voir également Références à UIScrollViewDelegate
- Réponses précédentes
- Plus de réponses