[ios] 더 작은 경우 UIScrollView의 컨텐츠 중심

확대 / 축소 및 스크롤에 사용 하는 UIImageView내부가 UIScrollView있습니다. 스크롤보기의 이미지 / 내용이 스크롤보기보다 크면 모든 것이 잘 작동합니다. 그러나 이미지가 스크롤보기보다 작아지면 스크롤보기의 왼쪽 상단에 고정됩니다. 사진 앱과 같이 중앙에 유지하고 싶습니다.

UIScrollView더 작을 때 중심 의 내용을 유지하는 것에 대한 아이디어 나 예가 있습니까?

iPhone 3.0으로 작업하고 있습니다.

다음 코드는 거의 작동합니다. 최소 확대 / 축소 수준에 도달 한 후 꼬 으면 이미지가 왼쪽 상단으로 돌아갑니다.

- (void)loadView {
    [super loadView];

    // set up main scroll view
    imageScrollView = [[UIScrollView alloc] initWithFrame:[[self view] bounds]];
    [imageScrollView setBackgroundColor:[UIColor blackColor]];
    [imageScrollView setDelegate:self];
    [imageScrollView setBouncesZoom:YES];
    [[self view] addSubview:imageScrollView];

    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"WeCanDoIt.png"]];
    [imageView setTag:ZOOM_VIEW_TAG];
    [imageScrollView setContentSize:[imageView frame].size];
    [imageScrollView addSubview:imageView];

    CGSize imageSize = imageView.image.size;
    [imageView release];

    CGSize maxSize = imageScrollView.frame.size;
    CGFloat widthRatio = maxSize.width / imageSize.width;
    CGFloat heightRatio = maxSize.height / imageSize.height;
    CGFloat initialZoom = (widthRatio > heightRatio) ? heightRatio : widthRatio;

    [imageScrollView setMinimumZoomScale:initialZoom];
    [imageScrollView setZoomScale:1];

    float topInset = (maxSize.height - imageSize.height) / 2.0;
    float sideInset = (maxSize.width - imageSize.width) / 2.0;
    if (topInset < 0.0) topInset = 0.0;
    if (sideInset < 0.0) sideInset = 0.0;
    [imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return [imageScrollView viewWithTag:ZOOM_VIEW_TAG];
}

/************************************** NOTE **************************************/
/* The following delegate method works around a known bug in zoomToRect:animated: */
/* In the next release after 3.0 this workaround will no longer be necessary      */
/**********************************************************************************/
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
    [scrollView setZoomScale:scale+0.01 animated:NO];
    [scrollView setZoomScale:scale animated:NO];
    // END Bug workaround

    CGSize maxSize = imageScrollView.frame.size;
    CGSize viewSize = view.frame.size;
    float topInset = (maxSize.height - viewSize.height) / 2.0;
    float sideInset = (maxSize.width - viewSize.width) / 2.0;
    if (topInset < 0.0) topInset = 0.0;
    if (sideInset < 0.0) sideInset = 0.0;
    [imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}



답변

매우 간단한 해결책이 있습니다! ScrollViewDelegate를 확대하면서 하위보기의 중앙 (이미지보기)을 업데이트하기 만하면됩니다. 확대 / 축소 된 이미지가 스크롤보기보다 작 으면 subview.center를 조정하고 그렇지 않으면 가운데는 (0,0)입니다.

- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    UIView *subView = [scrollView.subviews objectAtIndex:0];

    CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
    CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);

    subView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX,
                                 scrollView.contentSize.height * 0.5 + offsetY);
}


답변

@EvelynCordner의 답변 은 내 앱에서 가장 잘 작동했습니다. 다른 옵션보다 코드가 훨씬 적습니다.

필요한 경우 Swift 버전이 있습니다.

func scrollViewDidZoom(_ scrollView: UIScrollView) {
    let offsetX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0)
    let offsetY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0)
    scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0, 0)
}


답변

좋아, 나는 지난 이틀 동안이 싸움을하고 마침내 마침내 믿을만한 (지금까지 …) 해결책을 찾았으며 그것을 공유하고 다른 사람들에게 약간의 고통을 덜어 야한다고 생각했습니다. :)이 솔루션에 문제가 있으면 소리 지르십시오!

기본적으로 StackOverflow, Apple 개발자 포럼 검색, three20, ScrollingMadness, ScrollTestSuite 등의 코드를 살펴 보았습니다 .UIScrollView의 오프셋 및 / 또는 삽입과 함께 UIImageView 프레임을 확대하려고했습니다. ViewController 등에서 아무 효과가 없었습니다 (다른 사람들도 알았 듯이).

그것에 자고 난 후, 나는 몇 가지 대안 각도를 시도했다.

  1. UIImageView를 서브 클래스 화하여 자체 크기를 동적으로 변경합니다. 이는 전혀 효과가 없었습니다.
  2. UIScrollView를 서브 클래스 화하여 자체 contentOffset을 동적으로 변경합니다. 이것이 나에게 승자 인 것 같습니다.

이 서브 클래 싱 UIScrollView 메서드를 사용하면 contentOffset 뮤 테이터를 재정의하므로 이미지가 뷰포트보다 작게 조정될 때 {0,0}으로 설정되지 않습니다. 대신 이미지가 뷰포트의 중앙에 유지되도록 오프셋을 설정합니다. 지금까지는 항상 작동하는 것 같습니다. 넓고, 크고, 작고 큰 이미지로 확인했으며 “작동하지만 최소 확대 / 축소로 핀치가 끊어집니다”문제가 없습니다.

이 솔루션을 사용하는 예제 프로젝트를 github에 업로드했습니다. http://github.com/nyoron/NYOBetterZoom


답변

이 코드는 대부분의 iOS 버전에서 작동해야하며 3.1 이상에서 작동하도록 테스트되었습니다.

포토 콜러 용 Apple WWDC 코드를 기반으로합니다.

아래를 UIScrollView의 하위 클래스에 추가하고 tileContainerView를 이미지 또는 타일이 포함 된 뷰로 바꾸십시오.

- (void)layoutSubviews {
    [super layoutSubviews];

    // center the image as it becomes smaller than the size of the screen
    CGSize boundsSize = self.bounds.size;
    CGRect frameToCenter = tileContainerView.frame;

    // center horizontally
    if (frameToCenter.size.width < boundsSize.width)
        frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
    else
        frameToCenter.origin.x = 0;

    // center vertically
    if (frameToCenter.size.height < boundsSize.height)
        frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
    else
        frameToCenter.origin.y = 0;

    tileContainerView.frame = frameToCenter;
}


답변

자동 레이아웃을 사용하는 스크롤보기에 더 적합한 솔루션을 보려면 스크롤보기 하위보기의 프레임을 업데이트하는 대신 스크롤보기의 내용 삽입을 사용하십시오.

- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
    CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);

    self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0.f, 0.f);
}


답변

현재 나는에 기반하여 오프셋을 조정하기 위해 서브 클래스 화 UIScrollView및 재정의 중 setContentOffset:입니다 contentSize. 핀치 및 프로그래밍 방식의 확대 / 축소 모두에서 작동합니다.

@implementation HPCenteringScrollView

- (void)setContentOffset:(CGPoint)contentOffset
{
    const CGSize contentSize = self.contentSize;
    const CGSize scrollViewSize = self.bounds.size;

    if (contentSize.width < scrollViewSize.width)
    {
        contentOffset.x = -(scrollViewSize.width - contentSize.width) / 2.0;
    }

    if (contentSize.height < scrollViewSize.height)
    {
        contentOffset.y = -(scrollViewSize.height - contentSize.height) / 2.0;
    }

    [super setContentOffset:contentOffset];
}

@end

이 코드는 짧고 달콤 할뿐만 아니라 @Erdemus 솔루션보다 훨씬 부드러운 확대 / 축소를 생성합니다. RMGallery 데모 에서 실제로 작동하는 것을 볼 수 있습니다 .


답변

나는이 문제와 싸우는 데 하루를 보냈고 결국에는 scrollViewDidEndZooming : withView : atScale : 을 다음과 같이 .

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
    CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
    CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
    CGFloat viewWidth = view.frame.size.width;
    CGFloat viewHeight = view.frame.size.height;

    CGFloat x = 0;
    CGFloat y = 0;

    if(viewWidth < screenWidth) {
        x = screenWidth / 2;
    }
    if(viewHeight < screenHeight) {
        y = screenHeight / 2 ;
    }

    self.scrollView.contentInset = UIEdgeInsetsMake(y, x, y, x);
}

이렇게하면 이미지가 화면보다 작을 때 이미지 주위에 충분한 공간이있어 원하는 위치에 이미지를 배치 할 수 있습니다.

(UIScrollView에 이미지를 보유 할 UIImageView가 포함되어 있다고 가정)

본질적으로 이것이하는 일은 이미지보기의 너비 / 높이가 화면의 너비 / 높이보다 작은 지 확인하고 그렇다면 화면 너비 / 높이의 절반을 삽입하십시오 (이미지를 원한다면이 크기를 더 크게 만들 수 있습니다) 화면 경계를 벗어납니다).

이것은 UIScrollViewDelegate 메소드이므로 빌드 문제가 발생하지 않도록 뷰 컨트롤러의 선언에 추가하는 것을 잊지 마십시오.