UIWebView의 콘텐츠 크기를 결정하는 방법은 무엇입니까?


116

나는이 UIWebView다른 (단일 페이지) 내용으로. CGSize부모 뷰의 크기를 적절하게 조정하기 위해 콘텐츠 를 찾고 싶습니다 . -sizeThatFits:불행히도 명백한 것은 webView의 현재 프레임 크기를 반환합니다.


1
잘 모르겠지만이 질문에 대한 답변이 도움이 될 것입니다. stackoverflow.com/questions/745160/…
Greg

답변:


250

사용하는 첫 번째 추측 -sizeThatFits:이 완전히 잘못된 것은 아니라는 것이 밝혀졌습니다 . 작동하는 것처럼 보이지만 webView의 프레임이 -sizeThatFits:. 그 후 피팅 크기로 잘못된 프레임 크기를 수정할 수 있습니다. 이것은 끔찍하게 들리지만 실제로 그렇게 나쁘지는 않습니다. 두 프레임 변경을 바로 수행하기 때문에 뷰가 업데이트되지 않고 깜박이지 않습니다.

물론 콘텐츠가로드 될 때까지 기다려야하므로 코드를 -webViewDidFinishLoad:delegate 메서드에 넣습니다 .

Obj-C

- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
    CGRect frame = aWebView.frame;
    frame.size.height = 1;
    aWebView.frame = frame;
    CGSize fittingSize = [aWebView sizeThatFits:CGSizeZero];
    frame.size = fittingSize;
    aWebView.frame = frame;

    NSLog(@"size: %f, %f", fittingSize.width, fittingSize.height);
}

스위프트 4.x

func webViewDidFinishLoad(_ webView: UIWebView) {
    var frame = webView.frame
    frame.size.height = 1
    webView.frame = frame
    let fittingSize = webView.sizeThatFits(CGSize.init(width: 0, height: 0))
    frame.size = fittingSize
    webView.frame = frame
}

JavaScript를 사용하는 또 다른 접근 방식 (@GregInYEG에게 감사) 이 있음을 지적해야합니다 . 어떤 솔루션이 더 잘 수행되는지 확실하지 않습니다.

두 가지 해키 솔루션 중 나는 이것을 더 좋아합니다.


나는 당신의 접근 방식을 시도했습니다. 내가 경험 한 문제는 스크롤이 없다는 것입니다. 어떤 제안?
테스트

1
변경된 webview 높이로 인해 스크롤 문제가 발생한다는 것을 알았습니다. UIView 크기를 변경해야합니까?
테스트

당신의 문제가 무엇인지 확실하지 않습니다. 코드 나 설명이 포함 된 새 질문을 게시 할 수 있습니까?
Ortwin Gentz

@testing, @Bogatyr : 맞습니다. UIWebView부모보기보다 높으면 UIScrollView.
Ortwin Gentz ​​2011

7
webView.scalesPageToFit = YES를 제공하는 경우; 그러면이 솔루션 만 작동합니다. 솔루션에 감사드립니다 ..
SP

92

잘 작동하는 또 다른 솔루션이 있습니다.

한편으로 Ortwin의 접근 방식 및 솔루션은 iOS 6.0 이상에서만 작동하지만 iOS 5.0, 5.1 및 5.1.1에서는 제대로 작동하지 않으며 다른 한편으로는 내가 좋아하지 않고 이해할 수없는 것이 있습니다. Ortwin의 접근 방식에서는 [webView sizeThatFits:CGSizeZero]매개 변수와 함께 메소드 를 사용하는 것입니다.이 메소드 와 매개 변수 CGSizeZero에 대한 Apple 공식 문서를 읽으면 다음과 같이 명확하게 말합니다.

이 메서드의 기본 구현은 뷰 경계 사각형의 크기 부분을 반환합니다. 하위 클래스는이 메서드를 재정 의하여 원하는 하위 뷰의 레이아웃을 기반으로 사용자 지정 값을 반환 할 수 있습니다. 예를 들어 UISwitch 객체는 스위치 뷰의 표준 크기를 나타내는 고정 크기 값을 반환하고 UIImageView 객체는 현재 표시중인 이미지의 크기를 반환합니다.

내 말은 문서를 읽으면 전달 된 매개 변수가 [webView sizeThatFits: ...]최소한 원하는 width. 그 용액으로 원하는 폭이 설정되는 webView호출 이전의 프레임 sizeThatFits로모그래퍼 CGSizeZero파라미터. 그래서 저는이 솔루션이 "우연히"iOS 6에서 작동하고 있다고 유지합니다.

iOS 5.0 이상에서 작동하는 이점이있는보다 합리적인 접근 방식을 상상했습니다. 또한 둘 이상의 webView (속성 webView.scrollView.scrollEnabled = NOscrollView.

다음은의 레이아웃을 webView원하는대로 강제 설정 width하고 해당 height세트를 다시 가져 오는 코드 webView입니다.

Obj-C

- (void)webViewDidFinishLoad:(UIWebView *)aWebView
{   
    aWebView.scrollView.scrollEnabled = NO;    // Property available in iOS 5.0 and later 
    CGRect frame = aWebView.frame;

    frame.size.width = 200;       // Your desired width here.
    frame.size.height = 1;        // Set the height to a small one.

    aWebView.frame = frame;       // Set webView's Frame, forcing the Layout of its embedded scrollView with current Frame's constraints (Width set above).

    frame.size.height = aWebView.scrollView.contentSize.height;  // Get the corresponding height from the webView's embedded scrollView.

    aWebView.frame = frame;       // Set the scrollView contentHeight back to the frame itself.
}

스위프트 4.x

func webViewDidFinishLoad(_ aWebView: UIWebView) {

    aWebView.scrollView.isScrollEnabled = false
    var frame = aWebView.frame

    frame.size.width = 200
    frame.size.height = 1

    aWebView.frame = frame
    frame.size.height = aWebView.scrollView.contentSize.height

    aWebView.frame = frame;
}

내 예에서이 있습니다 webView사용자 정의에 포함 된 scrollView다른 가지고 webViews... 모든 이들은 webViews자신이 있었다 webView.scrollView.scrollEnabled = NO, 내가 추가 한 코드의 마지막 조각의 계산이었다 heightcontentSize내 사용자 지정의 scrollView이러한 내장 webViews, 그러나 그것은 쉽게로했다 합산 나의 webViewframe.size.height요령으로 계산 상술 ...


3
이것은 허용 된 솔루션보다 훨씬 더 우아하며 문서화되지 않고 정의되지 않은 동작에 의존하지 않습니다. 자바 스크립트를 삽입하는 것보다 훨씬 덜 해킹 적입니다. 나는 이것을 64 번 찬성 할 수 있으면 좋겠다.
bugloaf

그것이 바로 제가 설명하려고 한 것입니다. 감사합니다. 그리고 큰 장점은 모든 iOS 버전에서 작동하는 것 같습니다.
Phenomena

솔루션은 실제로 멋지게 보이지만 여전히 iOS 4.x를 대상으로하는 경우주의해야합니다. -[UIWebView scrollView]iOS 5.0 이상에서만 사용할 수 있습니다.
Ortwin Gentz ​​2012

2
나는 이것을 시도하고 유사한 솔루션을 시도했으며 webview 또는 내장 된 뷰가 이미 화면에있는 경우에만 작동한다는 것이 밝혀졌습니다. 뷰 계층 구조에 웹 뷰를 추가하기 전에 이것을 시도하면 결과는 수동 크기 높이가됩니다.
k1th June

1
다른 솔루션은 매번 작동하지 않으며 이유를 모르지만 트릭을 수행합니다 !!! 이것은 정답으로 태그되어야합니다.
Vassily

37

Ortwin의 대답이 대부분의 시간에만 작동한다는 것을 알았 기 때문에이 질문을 부활시키는 중 ...

webViewDidFinishLoad방법은 한번 이상 호출 될 수 있고,에 의해 반환되는 첫 번째 값은 sizeThatFits최종 크기가되어야 하는지를의 일부분이다. 그런 다음 어떤 이유에서든 sizeThatFitswhen에 대한 다음 호출 webViewDidFinishLoad은 이전과 동일한 값을 잘못 반환합니다! 이것은 일종의 동시성 문제인 것처럼 동일한 콘텐츠에 대해 무작위로 발생합니다. 시간이 지남에 따라이 동작이 변경되었을 수 있습니다. 제가 iOS 5 용으로 구축 중이고 sizeToFit거의 동일한 방식으로 작동 한다는 것을 발견했기 때문입니다 (이전에는 그렇지 않았습니까?).

이 간단한 해결책을 결정했습니다.

- (void)webViewDidFinishLoad:(UIWebView *)aWebView
{        
    CGFloat height = [[aWebView stringByEvaluatingJavaScriptFromString:@"document.height"] floatValue];
    CGFloat width = [[aWebView stringByEvaluatingJavaScriptFromString:@"document.width"] floatValue];
    CGRect frame = aWebView.frame;
    frame.size.height = height;
    frame.size.width = width;
    aWebView.frame = frame;
}

Swift (2.2) :

func webViewDidFinishLoad(webView: UIWebView) {

    if let heightString = webView.stringByEvaluatingJavaScriptFromString("document.height"),
        widthString = webView.stringByEvaluatingJavaScriptFromString("document.width"),
        height = Float(heightString),
        width = Float(widthString) {

        var rect = webView.frame
        rect.size.height = CGFloat(height)
        rect.size.width = CGFloat(width)
        webView.frame = rect
    }
}

업데이트 : 댓글에서 언급했듯이 콘텐츠가 축소 된 경우를 파악하지 못하는 것 같습니다. 모든 콘텐츠 및 OS 버전에 해당하는지 확실하지 않은 경우 시도해보세요.


웹 페이지에 이미지 나 기타 자산을 포함 시켰습니까? 이로 인해 대리자가 추가 발사 될 수 있습니다. 그 외에도 귀하의 솔루션은 webView.scalesPageToFit = YES위의 주석에서 언급 한 @Sijo로 제공하지 않을 때만 작동합니다 .
Ortwin Gentz

예, 아마도 그럴 것입니다-귀하의 솔루션은 텍스트 전용 경우에서 잘 작동합니다. 그러나 sizeThatFits가 최종 델리게이트 호출 중에 올바른 값을 제공하지 않는 것이 문제입니다 ... 매우 이상합니다. 그때까지 sizeThatFits를 사용하지 않도록 어떤 호출이 마지막이 될지 어떻게 알 수 있습니까?
Kieran Harper 2012 년

첫 번째 델리게이트 호출에서 sizeThatFits에서 올바른 결과를 얻는 경우 다음 호출을 무시할 수 없습니까?
Ortwin Gentz

네,하지만 그 반대입니다. :) 첫 번째 델리게이트 호출은 잘못된 sizeThatFits를 초래할 수 있으며 그 값은 고정됩니다. sizeThatFits가 어떤 식 으로든 웹 뷰를 수정하여 추가 sizeThatFits 호출이 동일한 결과를 제공하거나 여전히로드하는 동안 프레임 설정이 수행되는 것과 같습니다.
Kieran Harper 2012 년

1
이 솔루션도 제대로 작동하지 않습니다. 더 큰 페이지를 설정 한 다음 더 작은 페이지를 설정하면 항상 더 큰 값이 반환됩니다. "게으르게"내부보기를 확장하는 것처럼 보입니다. 이에 대한 해결 방법이 있습니까?
Legoless

23

Xcode 8 및 iOS 10에서 웹보기의 높이를 결정합니다. 당신은 사용하여 높이를 얻을 수 있습니다

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    CGFloat height = [[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] floatValue];
    NSLog(@"Webview height is:: %f", height);
}

또는 Swift

 func webViewDidFinishLoad(aWebView:UIWebView){
        let height: Float = (aWebView.stringByEvaluatingJavaScriptFromString("document.body.scrollHeight")?.toFloat())!
        print("Webview height is::\(height)")
    }

1
웹보기 콘텐츠 크기를 사용하여 찾아보십시오 .. myWebView.scrollView.contentSize.height
Hardik Thakkar에게

8

간단한 해결책은 그냥 사용하는 webView.scrollView.contentSize것이지만 이것이 JavaScript에서 작동하는지 모르겠습니다. JavaScript가 사용되지 않으면 확실히 작동합니다.

- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
    CGSize contentSize = aWebView.scrollView.contentSize;
    NSLog(@"webView contentSize: %@", NSStringFromCGSize(contentSize));
}

3

AFAIK는 [webView sizeThatFits:CGSizeZero]콘텐츠 크기를 파악하는 데 사용할 수 있습니다 .


3

이상 해요!

나는 솔루션을 테스트 모두 sizeThatFits:하고 [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"]있습니다 NOT 나를 위해 작동합니다.

그러나 웹 페이지 콘텐츠의 높이를 적절하게 얻을 수있는 흥미롭고 쉬운 방법을 찾았습니다. 현재는 위임 메서드에서 사용했습니다 scrollViewDidScroll:.

CGFloat contentHeight = scrollView.contentSize.height - CGRectGetHeight(scrollView.frame);

iOS 9.3 시뮬레이터 / 기기에서 확인되었습니다. 행운을 빕니다!

편집하다:

배경 : html 콘텐츠는 내 문자열 변수와 HTTP 콘텐츠 템플릿으로 계산되며 메서드 loadHTMLString:baseURL:에 의해로드되며 등록 된 JS 스크립트가 없습니다.


3

iOS10의 경우 0 (영) 값 을 얻었 document.height으므로 document.body.scrollHeightWebview에서 문서 높이를 얻는 솔루션입니다. 이 문제는 width.


2

UIWebView대한 HTML 콘텐츠의 크기를 결정하기 위해 하위 뷰가 아니므로 (따라서 창 계층의 일부가 아님)을 사용하고 UITableViewCells있습니다. 연결 UIWebView이 끊어진 파일 이 -[UIWebView sizeThatFits:]. 또한 https://stackoverflow.com/a/3937599/9636 에서 언급했듯이 적절한 높이를 얻으려면 UIWebView's frame height를 1로 설정해야합니다 .

는 IF UIWebView의 높이가 너무 큰 (즉, 당신은 1000으로 설정되어 있지만, HTML 콘텐츠 크기는 500입니다) :

UIWebView.scrollView.contentSize.height
-[UIWebView stringByEvaluatingJavaScriptFromString:@"document.height"]
-[UIWebView sizeThatFits:]

모두 height1000을 반환합니다 .

이 경우 내 문제를 해결하기 위해 https://stackoverflow.com/a/11770883/9636을 사용 하여 성실하게 투표했습니다. 그러나이 솔루션 UIWebView.frame.width-[UIWebView sizeThatFits:] width.


1
이 스레드의 모든 예제를 시도했으며 "document.height"를 사용하여 문서의 높이를 검색하는 것이 가장 신뢰할 수 있습니다. 컨테이너 (예 : DIV) 외부에 추가 HTML을 추가하여 수행 한 모든 테스트에서 getElementById (...)를 신뢰할 수 없게 만들 수 있습니다. document.height는 대우를 작동합니다.
John Rogers

2

여기에 제시된 제안 중 어느 것도 내 상황에 도움이되지 않았지만 답을 얻을 수있는 내용을 읽었습니다. 고정 된 UI 컨트롤 집합과 UIWebView가있는 ViewController가 있습니다. UI 컨트롤이 HTML 콘텐츠에 연결된 것처럼 전체 페이지가 스크롤되기를 원했기 때문에 UIWebView에서 스크롤을 비활성화 한 다음 부모 스크롤 뷰의 콘텐츠 크기를 올바르게 설정해야합니다.

중요한 팁은 UIWebView가 화면에 렌더링 될 때까지 크기를 올바르게보고하지 않는다는 것입니다. 그래서 콘텐츠를로드 할 때 콘텐츠 크기를 사용 가능한 화면 높이로 설정했습니다. 그런 다음 viewDidAppear에서 scrollview의 콘텐츠 크기를 올바른 값으로 업데이트합니다. 로컬 콘텐츠에서 loadHTMLString을 호출하기 때문에 이것은 나를 위해 일했습니다. loadRequest를 사용하는 경우 html이 검색되는 속도에 따라 webViewDidFinishLoad의 contentSize도 업데이트해야 할 수 있습니다.

스크롤 뷰의 보이지 않는 부분 만 변경되기 때문에 깜박임이 없습니다.


웹보기가 viewDidAppear에서 완료 될만큼 빠르게로드되는 것은 운이 좋을까 봐 걱정됩니다. 아마도 이것은 뷰가 애니메이션으로 표시되어 애니메이션이 콘텐츠를로드 할만큼 충분히 긴 경우에만 작동합니다. 차라리 webViewDidFinishLoad안전한 접근 방식으로 의존하고 싶습니다 .
Ortwin Gentz

2

HTML에 iframe (예 : facebook-, twitter, instagram-embeds)과 같은 무거운 HTML 콘텐츠 가 포함 된 경우 실제 솔루션이 훨씬 더 어렵습니다. 먼저 HTML을 래핑합니다.

[htmlContent appendFormat:@"<html>", [[LocalizationStore instance] currentTextDir], [[LocalizationStore instance] currentLang]];
[htmlContent appendFormat:@"<head>"];
[htmlContent appendString:@"<script type=\"text/javascript\">"];
[htmlContent appendFormat:@"    var lastHeight = 0;"];
[htmlContent appendFormat:@"    function updateHeight() { var h = document.getElementById('content').offsetHeight; if (lastHeight != h) { lastHeight = h; window.location.href = \"x-update-webview-height://\" + h } }"];
[htmlContent appendFormat:@"    window.onload = function() {"];
[htmlContent appendFormat:@"        setTimeout(updateHeight, 1000);"];
[htmlContent appendFormat:@"        setTimeout(updateHeight, 3000);"];
[htmlContent appendFormat:@"        if (window.intervalId) { clearInterval(window.intervalId); }"];
[htmlContent appendFormat:@"        window.intervalId = setInterval(updateHeight, 5000);"];
[htmlContent appendFormat:@"        setTimeout(function(){ clearInterval(window.intervalId); window.intervalId = null; }, 30000);"];
[htmlContent appendFormat:@"    };"];
[htmlContent appendFormat:@"</script>"];
[htmlContent appendFormat:@"..."]; // Rest of your HTML <head>-section
[htmlContent appendFormat:@"</head>"];
[htmlContent appendFormat:@"<body>"];
[htmlContent appendFormat:@"<div id=\"content\">"]; // !important https://stackoverflow.com/a/8031442/1046909
[htmlContent appendFormat:@"..."]; // Your HTML-content
[htmlContent appendFormat:@"</div>"]; // </div id="content">
[htmlContent appendFormat:@"</body>"];
[htmlContent appendFormat:@"</html>"];

그런 다음 shouldStartLoadWithRequest에 x-update-webview-height -scheme 처리를 추가 합니다 .

    if (navigationType == UIWebViewNavigationTypeLinkClicked || navigationType == UIWebViewNavigationTypeOther) {
    // Handling Custom URL Scheme
    if([[[request URL] scheme] isEqualToString:@"x-update-webview-height"]) {
        NSInteger currentWebViewHeight = [[[request URL] host] intValue];
        if (_lastWebViewHeight != currentWebViewHeight) {
            _lastWebViewHeight = currentWebViewHeight; // class property
            _realWebViewHeight = currentWebViewHeight; // class property
            [self layoutSubviews];
        }
        return NO;
    }
    ...

마지막으로 layoutSubviews 안에 다음 코드를 추가하십시오 .

    ...
    NSInteger webViewHeight = 0;

    if (_realWebViewHeight > 0) {
        webViewHeight = _realWebViewHeight;
        _realWebViewHeight = 0;
    } else {
        webViewHeight = [[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"content\").offsetHeight;"] integerValue];
    }

    upateWebViewHeightTheWayYorLike(webViewHeight);// Now your have real WebViewHeight so you can update your webview height you like.
    ...

PS ObjectiveC / Swift 코드 내에서 시간 지연 (SetTimeout 및 setInterval)을 구현할 수 있습니다.

PSS UIWebView 및 Facebook Embeds에 대한 중요 정보 : 포함 된 Facebook 게시물이 UIWebView에서 제대로 표시되지 않습니다.


이건 재미 있네! 스크립트 부분이하는 일과 필요한 경우 어떻게 조정할 수 있는지 설명해 주시겠습니까? 주기적으로 높이를 확인하는 것처럼 보이지만 여러 시간 간격이 관련되어 있습니까?
키에 란 하퍼

@KieranHarper, 정확히 무엇을 설명해야합니까?
MingalevME

2

webview를 scrollview 어딘가에 하위 뷰로 사용할 때 높이 제약 조건을 일정한 값으로 설정하고 나중에 콘센트를 만들고 다음과 같이 사용할 수 있습니다.

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    webView.scrollView.scrollEnabled = NO;
    _webViewHeight.constant = webView.scrollView.contentSize.height;
}

1

나는 또한이 문제에 갇혀 있었고, webView의 동적 높이를 계산하려면 먼저 webView의 너비를 알려야하므로 js 앞에 한 줄을 추가하면 얻을 수 있음을 알았습니다. 매우 정확한 실제 높이.

코드는 다음과 같이 간단합니다.

-(void)webViewDidFinishLoad:(UIWebView *)webView
{

    //tell the width first
    webView.width = [UIScreen mainScreen].bounds.size.width;

    //use js to get height dynamically
    CGFloat scrollSizeHeight = [[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] floatValue];
    webView.height = scrollSizeHeight;

    webView.x = 0;
    webView.y = 0;

    //......
}

1

Xcode8 swift3.1 :

  1. 먼저 webView 높이 를 0으로 설정하십시오 .
  2. 에서 webViewDidFinishLoad위임 :

let height = webView.scrollView.contentSize.height

1 단계가 없으면 webview.height> 실제 contentHeight 인 경우 2 단계는 webview.height를 반환하지만 contentsize.height는 반환하지 않습니다.


이것이 제 경우에 필요한 프레임을 얻을 수없는 이유입니다 .. upvoted
NSPratik

0

또한 언급 된 모든 방법의 올바른 작동을 위해 iOS 7에서 뷰 컨트롤러 viewDidLoad메서드에 다음을 추가하십시오 .

if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {
    self.automaticallyAdjustsScrollViewInsets = NO;
}

그렇지 않으면 어떤 방법도 제대로 작동하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.