programing

UIScrollView를 콘텐츠에 맞게 자동 크기 조정하는 방법

padding 2023. 5. 8. 21:56
반응형

UIScrollView를 콘텐츠에 맞게 자동 크기 조정하는 방법

만들 방법이 있습니까?UIScrollView스크롤하는 콘텐츠의 높이(또는 너비)에 맞게 자동 조정하시겠습니까?

다음과 같은 것:

[scrollView setContentSize:(CGSizeMake(320, content.height))];

콘텐츠 크기를 업데이트하는 최고의 방법입니다.UIScrollView포함된 하위 보기를 기반으로 합니다.

목표-C

CGRect contentRect = CGRectZero;

for (UIView *view in self.scrollView.subviews) {
    contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;

스위프트

let contentRect: CGRect = scrollView.subviews.reduce(into: .zero) { rect, view in
    rect = rect.union(view.frame)
}
scrollView.contentSize = contentRect.size

UIScrollView는 콘텐츠의 높이를 자동으로 인식하지 못합니다.높이와 너비를 직접 계산해야 합니다.

다음과 같은 것으로 합니다.

CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scrollView.subviews)
{
   scrollViewHeight += view.frame.size.height;
}

[scrollView setContentSize:(CGSizeMake(320, scrollViewHeight))];

그러나 이것은 보기가 다른 보기보다 낮은 경우에만 작동합니다.보기가 서로 옆에 있는 경우 스크롤러의 내용을 실제보다 크게 설정하지 않으려면 하나의 높이만 추가하면 됩니다.

자동 레이아웃을 사용하는 경우 해결 방법:

  • translatesAutoresizingMaskIntoConstraintsNO관련된 모든 견해에 관하여

  • 스크롤 뷰 외부의 제약 조건으로 스크롤 뷰를 배치하고 크기를 조정합니다.

  • 제약 조건을 사용하여 스크롤 뷰 내의 하위 뷰를 배치합니다. 제약 조건이 스크롤 뷰의 네 모서리에 모두 연결되고 스크롤 뷰에 의존하여 크기를 가져오지 않도록 합니다.

출처: https://developer.apple.com/library/ios/technotes/tn2154/_index.html

저는 이것을 에스푸즈와 JCC의 답변에 추가했습니다.하위 뷰의 y 위치를 사용하고 스크롤 막대를 포함하지 않습니다.편집 표시되는 가장 낮은 하위 뷰의 맨 아래를 사용합니다.

+ (CGFloat) bottomOfLowestContent:(UIView*) view
{
    CGFloat lowestPoint = 0.0;

    BOOL restoreHorizontal = NO;
    BOOL restoreVertical = NO;

    if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)])
    {
        if ([(UIScrollView*)view showsHorizontalScrollIndicator])
        {
            restoreHorizontal = YES;
            [(UIScrollView*)view setShowsHorizontalScrollIndicator:NO];
        }
        if ([(UIScrollView*)view showsVerticalScrollIndicator])
        {
            restoreVertical = YES;
            [(UIScrollView*)view setShowsVerticalScrollIndicator:NO];
        }
    }
    for (UIView *subView in view.subviews)
    {
        if (!subView.hidden)
        {
            CGFloat maxY = CGRectGetMaxY(subView.frame);
            if (maxY > lowestPoint)
            {
                lowestPoint = maxY;
            }
        }
    }
    if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)])
    {
        if (restoreHorizontal)
        {
            [(UIScrollView*)view setShowsHorizontalScrollIndicator:YES];
        }
        if (restoreVertical)
        {
            [(UIScrollView*)view setShowsVerticalScrollIndicator:YES];
        }
    }

    return lowestPoint;
}

여기 변환하기 귀찮은 사람들을 위한 신속한 답변이 있습니다 :)

var contentRect = CGRectZero
for view in self.scrollView.subviews {
    contentRect = CGRectUnion(contentRect, view.frame)
}
self.scrollView.contentSize = contentRect.size

@leviatan의 대답을 각색한 스위프트 3입니다.

확장

import UIKit


extension UIScrollView {

    func resizeScrollViewContentSize() {

        var contentRect = CGRect.zero

        for view in self.subviews {

            contentRect = contentRect.union(view.frame)

        }

        self.contentSize = contentRect.size

    }

}

사용.

scrollView.resizeScrollViewContentSize()

매우 사용하기 쉽습니다!

스위프트에서 다음 연장이 도움이 될 것입니다.

extension UIScrollView{
    func setContentViewSize(offset:CGFloat = 0.0) {
        // dont show scroll indicators
        showsHorizontalScrollIndicator = false
        showsVerticalScrollIndicator = false

        var maxHeight : CGFloat = 0
        for view in subviews {
            if view.isHidden {
                continue
            }
            let newHeight = view.frame.origin.y + view.frame.height
            if newHeight > maxHeight {
                maxHeight = newHeight
            }
        }
        // set content size
        contentSize = CGSize(width: contentSize.width, height: maxHeight + offset)
        // show scroll indicators
        showsHorizontalScrollIndicator = true
        showsVerticalScrollIndicator = true
    }
}

논리는 주어진 답과 동일합니다.그나내부숨뷰생다니략합는겨진 내의 합니다.UIScrollView스크롤 표시기를 숨긴 후 계산이 수행됩니다.

또한 옵션 함수 매개변수가 있으며 매개변수를 함수로 전달하여 오프셋 값을 추가할 수 있습니다.

@leviathan의 최고의 솔루션.FP(기능 프로그래밍) 접근법을 사용하여 신속하게 번역하는 것뿐입니다.

self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect(), { 
  CGRectUnion($0, $1.frame) 
}.size

UIScrollView 내의 콘텐츠 높이는 "더 멀리 도달"하는 하위 항목을 계산하여 얻을 수 있습니다.이 값을 계산하려면 원점 Y(시작)와 항목 높이를 고려해야 합니다.

float maxHeight = 0;
for (UIView *child in scrollView.subviews) {
    float childHeight = child.frame.origin.y + child.frame.size.height;
    //if child spans more than current maxHeight then make it a new maxHeight
    if (childHeight > maxHeight)
        maxHeight = childHeight;
}
//set content size
[scrollView setContentSize:(CGSizeMake(320, maxHeight))];

이러한 방식으로 작업을 수행하면 항목(개요)을 직접 다른 항목 아래에 쌓을 필요가 없습니다.

저는 @emenegro의 솔루션을 기반으로 다른 솔루션을 생각해냈습니다.

NSInteger maxY = 0;
for (UIView* subview in scrollView.subviews)
{
    if (CGRectGetMaxY(subview.frame) > maxY)
    {
        maxY = CGRectGetMaxY(subview.frame);
    }
}
maxY += 10;
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width, maxY)];

기본적으로 뷰에서 가장 아래쪽에 있는 요소를 파악하고 아래쪽에 10px 패딩을 추가합니다.

아니면 그냥 하라:

int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];

(이 솔루션은 제가 이 페이지의 주석으로 추가한 것입니다.이 의견에 대한 19표의 사전 투표를 받은 후, 저는 커뮤니티의 이익을 위한 공식적인 답변으로 이 솔루션을 추가하기로 결정했습니다!)

ScrollView는 다른 ScrollView를 가질 수도 있고 Depth subViews 트리에서 서로 다를 수도 있기 때문에 깊이에서 재귀적으로 실행하는 것이 좋습니다.여기에 이미지 설명 입력

스위프트 2

extension UIScrollView {
    //it will block the mainThread
    func recalculateVerticalContentSize_synchronous () {
        let unionCalculatedTotalRect = recursiveUnionInDepthFor(self)
        self.contentSize = CGRectMake(0, 0, self.frame.width, unionCalculatedTotalRect.height).size;
    }

    private func recursiveUnionInDepthFor (view: UIView) -> CGRect {
        var totalRect = CGRectZero
        //calculate recursevly for every subView
        for subView in view.subviews {
            totalRect =  CGRectUnion(totalRect, recursiveUnionInDepthFor(subView))
        }
        //return the totalCalculated for all in depth subViews.
        return CGRectUnion(totalRect, view.frame)
    }
}

사용.

scrollView.recalculateVerticalContentSize_synchronous()

swift4의 경우 감소:

self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect.zero, {
   return $0.union($1.frame)
}).size

크기는 내부에 로드된 내용과 클리핑 옵션에 따라 달라집니다.텍스트 보기인 경우에는 래핑, 텍스트 줄 수, 글꼴 크기 등에 따라 달라집니다.스스로 계산하는 것은 거의 불가능합니다.좋은 소식은 보기가 로드되고 WillAppear 보기에서 계산된다는 것입니다.그 전에는 모두 알 수 없으며 내용 크기는 프레임 크기와 같습니다.그러나 WillAppear 메서드 및 그 이후(예: viewDidAppear)에는 내용 크기가 실제 크기가 됩니다.

Richy의 코드를 래핑하여 콘텐츠 크기 조정을 완전히 자동화하는 사용자 정의 UIScrollView 클래스를 만들었습니다!

SBS 크롤뷰.h

@interface SBScrollView : UIScrollView
@end

SBS crollView.m:

@implementation SBScrollView
- (void) layoutSubviews
{
    CGFloat scrollViewHeight = 0.0f;
    self.showsHorizontalScrollIndicator = NO;
    self.showsVerticalScrollIndicator = NO;
    for (UIView* view in self.subviews)
    {
        if (!view.hidden)
        {
            CGFloat y = view.frame.origin.y;
            CGFloat h = view.frame.size.height;
            if (y + h > scrollViewHeight)
            {
                scrollViewHeight = h + y;
            }
        }
    }
    self.showsHorizontalScrollIndicator = YES;
    self.showsVerticalScrollIndicator = YES;
    [self setContentSize:(CGSizeMake(self.frame.size.width, scrollViewHeight))];
}
@end

사용 방법:
인스턴스.h 파일을 보기 컨트롤러로 가져오고 일반 UIScrollView 인스턴스 대신 SBScrollView 인스턴스를 선언하기만 하면 됩니다.

왜 코드 한 줄을 사용하지 않습니까?

_yourScrollView.contentSize = CGSizeMake(0, _lastView.frame.origin.y + _lastView.frame.size.height);

는 하위클래만니다습들의 .ScrollViewintrinsicContentSize그리고 그것은 나에게 완벽하게 효과가 있었습니다.

public final class ContentSizedScrollView: UIScrollView {
    override public var contentSize: CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override public var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return self.contentSize
    }
}

이제 이 클래스로 스크롤 뷰를 만들고 모든 측면에 제약 조건을 설정할 수 있습니다.

하위 보기가 스크롤의 네 모서리에 모두 연결되어 있는지 확인합니다. 보기

내용에 따라 달라집니다. content.frame.키가 당신이 원하는 것을 줄 수 있습니까?내용이 단일 항목인지 또는 항목 모음인지에 따라 다릅니다.

저는 또한 일을 하기 위한 리바이어던의 대답이 가장 좋다는 것을 발견했습니다.하지만, 그것은 이상한 키를 계산하고 있었습니다.하위 보기를 반복할 때 스크롤 보기가 스크롤 표시기를 표시하도록 설정된 경우 하위 보기 배열에 포함됩니다.이 경우 해결책은 루프하기 전에 스크롤 표시기를 일시적으로 비활성화한 다음 이전 가시성 설정을 다시 설정하는 것입니다.

-(void)adjustContentSizeToFit는 UIScrollView의 사용자 지정 하위 클래스에 있는 공용 메서드입니다.

-(void)awakeFromNib {    
    dispatch_async(dispatch_get_main_queue(), ^{
        [self adjustContentSizeToFit];
    });
}

-(void)adjustContentSizeToFit {

    BOOL showsVerticalScrollIndicator = self.showsVerticalScrollIndicator;
    BOOL showsHorizontalScrollIndicator = self.showsHorizontalScrollIndicator;

    self.showsVerticalScrollIndicator = NO;
    self.showsHorizontalScrollIndicator = NO;

    CGRect contentRect = CGRectZero;
    for (UIView *view in self.subviews) {
        contentRect = CGRectUnion(contentRect, view.frame);
    }
    self.contentSize = contentRect.size;

    self.showsVerticalScrollIndicator = showsVerticalScrollIndicator;
    self.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator;
}

이것이 UIScrollView의 콘텐츠 보기 크기를 업데이트하는 깔끔한 방법이 될 수 있다고 생각합니다.

extension UIScrollView {
    func updateContentViewSize() {
        var newHeight: CGFloat = 0
        for view in subviews {
            let ref = view.frame.origin.y + view.frame.height
            if ref > newHeight {
                newHeight = ref
            }
        }
        let oldSize = contentSize
        let newSize = CGSize(width: oldSize.width, height: newHeight + 20)
        contentSize = newSize
    }
}

동적 콘텐츠 크기를 다음과 같이 설정합니다.

 self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20);
import UIKit

class DynamicSizeScrollView: UIScrollView {

    var maxHeight: CGFloat = UIScreen.main.bounds.size.height
    var maxWidth: CGFloat = UIScreen.main.bounds.size.width

    override func layoutSubviews() {
        super.layoutSubviews()
        if !__CGSizeEqualToSize(bounds.size,self.intrinsicContentSize){
            self.invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        let height = min(contentSize.height, maxHeight)
        let width = min(contentSize.height, maxWidth)
        return CGSize(width: width, height: height)
    }

}

자동 레이아웃을 사용하는 경우 테두리 요소의 가장자리를 스크롤 보기와 동일하게 설정하면 됩니다.

예를 들어 수평 스크롤 보기에서 수평 내용을 자동으로 맞추려고 합니다.

스위프트

let bottomConstrint = NSLayoutConstraint.init(item: (bottommost UI element),
                                                  attribute: .bottom,
                                                  relatedBy: .equal,
                                                  toItem: (your UIScrollView),
                                                  attribute: .bottom,
                                                  multiplier: 1.0,
                                                  constant: 0)
bottomConstrint.isActive = true

저처럼 Snapkit를 사용하는 경우 다음과 같이 하십시오.

  scrollView.addSubview( (bottommost element) )
  (bottommost element).snp.makeConstraints { make in
      /*other constraints*/
      make.bottom.equalToSuperview()
  }
class ContentSizedScrollView: UIScrollView {

    override var contentSize:CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
}

// In UIViewController

import SnapKit

...

var scrollView: ContentSizedScrollView!

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  scrollView.contentSize = .init(width: view.bounds.width, height: stackView.bounds.height)
}


// Here some example of content composing inside of UIStackView
func setupContent() {
  scrollView = ContentSizedScrollView()
  blockView.addSubview(scrollView)
  scrollView.snp.makeConstraints { make in
      make.top.equalTo(19)
      make.left.equalToSuperview()
      make.right.equalToSuperview()
      make.bottom.equalTo(-20)
  }
  scrollView.contentInset = .init(top: 0, left: 0, bottom: 26, right: 0)
  scrollView.showsVerticalScrollIndicator = false
  scrollView.clipsToBounds = true
  scrollView.layer.cornerRadius = blockView.layer.cornerRadius / 2

  stackView = UIStackView()
  scrollView.addSubview(stackView)
  stackView.snp.makeConstraints { make in
      make.top.equalToSuperview()
      make.centerX.equalToSuperview()
      make.width.equalToSuperview().offset(-10)
  }
  stackView.axis = .vertical
  stackView.alignment = .center

  textTitleLabel = Label()
  stackView.addArrangedSubview(textTitleLabel)
  textTitleLabel.snp.makeConstraints { make in
      make.width.equalToSuperview().offset(-30)
  }
  textTitleLabel.font = UIFont.systemFont(ofSize: 20, weight: .bold)
  textTitleLabel.textColor = Color.Blue.oxfordBlue
  textTitleLabel.textAlignment = .center
  textTitleLabel.numberOfLines = 0
  stackView.setCustomSpacing(10, after: textTitleLabel)
}

하위 클래스를 만듭니다.UIScrollView다음을 포함합니다.

class ContentSizedScrollView: UIScrollView {
    override var contentSize:CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
}

콘텐츠 높이에 따라 크기가 자동으로 조정됩니다.

언급URL : https://stackoverflow.com/questions/2944294/how-do-i-auto-size-a-uiscrollview-to-fit-its-content

반응형