[ios] 프레임 크기보다 작은 증분으로 UIScrollView 페이징
화면 너비이지만 높이가 약 70 픽셀 인 스크롤보기가 있습니다. 여기에는 사용자가 선택할 수있는 50 x 50 아이콘 (주위에 공백이 있음)이 많이 포함되어 있습니다. 하지만 항상 스크롤 뷰가 페이지 방식으로 작동하고 항상 정확한 중앙에 아이콘이 표시되기를 원합니다.
아이콘이 화면의 너비 인 경우 UIScrollView의 페이징이 처리하므로 문제가되지 않습니다. 하지만 내 작은 아이콘이 콘텐츠 크기보다 훨씬 작기 때문에 작동하지 않습니다.
앱 호출 AllRecipes에서 이전에이 동작을 본 적이 있습니다. 나는 그것을하는 방법을 모른다.
아이콘 단위로 페이징을 수행하려면 어떻게해야합니까?
답변
스크롤 뷰를 화면 크기 (너비 방향)보다 작게 만드 되 IB에서 “Clip Subviews”확인란을 선택 취소합니다. 그런 다음, 스크롤 뷰를 반환하기 위해 hitTest : withEvent :를 재정의하는 투명한 userInteractionEnabled = NO 뷰를 그 위에 오버레이합니다 (전체 너비). 그것은 당신이 찾고있는 것을 당신에게 줄 것입니다. 자세한 내용은 이 답변 을 참조하십시오.
답변
스크롤 뷰를 다른 뷰와 오버레이하고 hitTest를 재정의하는 것보다 약간 더 나은 또 다른 솔루션이 있습니다.
UIScrollView를 하위 클래스로 만들고 pointInside를 재정의 할 수 있습니다. 그러면 스크롤 뷰가 프레임 외부의 터치에 응답 할 수 있습니다. 물론 나머지는 동일합니다.
@interface PagingScrollView : UIScrollView {
UIEdgeInsets responseInsets;
}
@property (nonatomic, assign) UIEdgeInsets responseInsets;
@end
@implementation PagingScrollView
@synthesize responseInsets;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint parentLocation = [self convertPoint:point toView:[self superview]];
CGRect responseRect = self.frame;
responseRect.origin.x -= responseInsets.left;
responseRect.origin.y -= responseInsets.top;
responseRect.size.width += (responseInsets.left + responseInsets.right);
responseRect.size.height += (responseInsets.top + responseInsets.bottom);
return CGRectContainsPoint(responseRect, parentLocation);
}
@end
답변
많은 솔루션을 보았지만 매우 복잡합니다. 작은 페이지를 가지면서도 모든 영역을 스크롤 가능하게 유지 하는 훨씬 더 쉬운 방법 은 스크롤을 더 작게 만들고 scrollView.panGestureRecognizer
부모보기 로 이동하는 것입니다. 단계는 다음과 같습니다.
-
코드에서 scrollview 팬 제스처를 전체 너비의 상위 컨테이너보기로 이동합니다.
override func viewDidLoad() {
super.viewDidLoad()
statsView.addGestureRecognizer(statsScrollView.panGestureRecognizer)
}
답변
받아 들여지는 대답은 매우 좋지만 UIScrollView
클래스 에서만 작동 하고 하위 항목은 없습니다. 예를 들어 뷰가 많고으로 변환 UICollectionView
하면이 메서드를 사용할 수 없습니다. 컬렉션 뷰 가 “표시되지 않는다”고 생각하는 뷰를 제거 하기 때문입니다 (따라서 잘리지 않더라도 사라지다).
그 언급에 대한 scrollViewWillEndDragging:withVelocity:targetContentOffset:
의견은 내 의견으로는 정답입니다.
할 수있는 일은이 델리게이트 메서드 내에서 현재 페이지 / 인덱스를 계산하는 것입니다. 그런 다음 속도 및 목표 오프셋이 “다음 페이지”이동에 적합한 지 여부를 결정합니다. 당신은 pagingEnabled
행동에 꽤 가까워 질 수 있습니다 .
참고 : 요즘 저는 보통 RubyMotion 개발자이므로 누군가이 Obj-C 코드의 정확성을 증명해주세요. camelCase와 snake_case를 섞어서 죄송합니다.이 코드의 대부분을 복사하여 붙여 넣었습니다.
- (void) scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetOffset
{
CGFloat x = targetOffset->x;
int index = [self convertXToIndex: x];
CGFloat w = 300f; // this is your custom page width
CGFloat current_x = w * [self convertXToIndex: scrollView.contentOffset.x];
// even if the velocity is low, if the offset is more than 50% past the halfway
// point, proceed to the next item.
if ( velocity.x < -0.5 || (current_x - x) > w / 2 ) {
index -= 1
}
else if ( velocity.x > 0.5 || (x - current_x) > w / 2 ) {
index += 1;
}
if ( index >= 0 || index < self.items.length ) {
CGFloat new_x = [self convertIndexToX: index];
targetOffset->x = new_x;
}
}
답변
- (void) scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetOffset
{
static CGFloat previousIndex;
CGFloat x = targetOffset->x + kPageOffset;
int index = (x + kPageWidth/2)/kPageWidth;
if(index<previousIndex - 1){
index = previousIndex - 1;
}else if(index > previousIndex + 1){
index = previousIndex + 1;
}
CGFloat newTarget = index * kPageWidth;
targetOffset->x = newTarget - kPageOffset;
previousIndex = index;
}
kPageWidth는 페이지의 너비입니다. kPageOffset은 셀을 왼쪽으로 정렬하지 않으려는 경우입니다 (즉, 셀을 가운데 정렬하려면 셀 너비의 절반으로 설정). 그렇지 않으면 0이어야합니다.
또한 한 번에 한 페이지 만 스크롤 할 수 있습니다.
답변
에 대한 -scrollView:didEndDragging:willDecelerate:
방법을 살펴보십시오 UIScrollViewDelegate
. 다음과 같은 것 :
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
int x = scrollView.contentOffset.x;
int xOff = x % 50;
if(xOff < 25)
x -= xOff;
else
x += 50 - xOff;
int halfW = scrollView.contentSize.width / 2; // the width of the whole content view, not just the scroll view
if(x > halfW)
x = halfW;
[scrollView setContentOffset:CGPointMake(x,scrollView.contentOffset.y)];
}
완벽하지 않습니다. 마지막으로이 코드를 시도해 보았을 때 고무줄 스크롤에서 돌아올 때 약간의 추악한 동작 (제가 기억하는 것처럼 점프)이 발생했습니다. 단순히 스크롤 뷰의 bounces
속성을로 설정하여이를 피할 수 있습니다 NO
.
답변
아직 댓글이 허용되지 않는 것 같기 때문에 여기에 Noah의 답변에 댓글을 추가하겠습니다.
나는 노아 위더스푼이 설명한 방법으로 성공적으로 이것을 달성했습니다. setContentOffset:
scrollview가 가장자리를 지나갈 때 메서드를 호출하지 않음으로써 점프 동작을 해결했습니다 .
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
// Don't snap when at the edges because that will override the bounce mechanic
if (self.contentOffset.x < 0 || self.contentOffset.x + self.bounds.size.width > self.contentSize.width)
return;
...
}
또한 모든 경우를 포착하기 위해 -scrollViewWillBeginDecelerating:
메서드를 구현해야한다는 것을 알았습니다 UIScrollViewDelegate
.