Bir UIWebView'ın içerik boyutu nasıl belirlenir?


116

Ben var UIWebViewfarklı (tek sayfa) içeriği ile. CGSizeAna görünümlerimi uygun şekilde yeniden boyutlandırmak için içeriğin ne olduğunu öğrenmek istiyorum . Bariz olan şey -sizeThatFits:maalesef webView’un geçerli çerçeve boyutunu döndürür.


1
Emin değilim, ama belki bu sorunun yanıtları yardımcı olacaktır: stackoverflow.com/questions/745160/…
Greg

Yanıtlar:


250

İlk tahminimin kullanımının -sizeThatFits:tamamen yanlış olmadığı ortaya çıktı . Çalışıyor gibi görünüyor, ancak yalnızca webView çerçevesi gönderilmeden önce minimum boyuta ayarlanmışsa -sizeThatFits:. Bundan sonra, yanlış çerçeve boyutunu uygun boyuta göre düzeltebiliriz. Kulağa korkunç geliyor ama aslında o kadar da kötü değil. Her iki kare değişikliğini de birbiri ardına yaptığımız için, görünüm güncellenmez ve titremiyor.

Elbette içerik yüklenene kadar beklememiz gerekiyor, bu yüzden kodu -webViewDidFinishLoad:delege yöntemine koyuyoruz .

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);
}

Swift 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
}

Ben orada işaret etmelidir başka yaklaşım JavaScript kullanarak (teşekkürler @GregInYEG). Hangi çözümün daha iyi performans gösterdiğinden emin değilim.

İki hileli çözümden bunu daha çok seviyorum.


Yaklaşımınızı denedim. Yaşadığım sorun, kaydırma olmaması. Baska öneri?
test

1
Kaydırma ile ilgili sorunun değişen web görünümü yüksekliğinden kaynaklandığını öğrendim. UIView boyutlarını değiştirmem gerekiyor mu?
test

Sorunun ne olduğundan emin değilim. Belki bir kod veya açıklama içeren yeni bir soru gönderebilirsiniz?
Ortwin Gentz

@testing, @Bogatyr: Doğru, eğer UIWebViewsizin ebeveyn görüşünüzden daha yüksekse, onu bir UIScrollView.
Ortwin Gentz

7
webView.scalesPageToFit = YES verirsem; o zaman sadece bu çözüm işe yarar..çözüm için teşekkürler ..
SP

92

Harika çalışan başka bir çözümüm var.

Bir yandan Ortwin'in yaklaşımı ve çözümü yalnızca iOS 6.0 ve sonraki sürümlerde çalışıyor, ancak iOS 5.0, 5.1 ve 5.1.1'de doğru çalışmıyor, diğer yandan sevmediğim ve anlayamadığım bir şey var. Ortwin'in yaklaşımıyla, yöntemin [webView sizeThatFits:CGSizeZero]parametresiyle kullanılmasıdır CGSizeZero: Bu yöntemler ve parametresi hakkında Apple Resmi belgelerini okursanız, açıkça şunu söyler:

Bu yöntemin varsayılan uygulaması, görünümün sınır dikdörtgeninin boyut bölümünü döndürür. Alt sınıflar, herhangi bir alt görünümün istenen düzenine göre özel bir değer döndürmek için bu yöntemi geçersiz kılabilir. Örneğin, bir UISwitch nesnesi, bir anahtar görünümünün standart boyutunu temsil eden sabit bir boyut değeri döndürür ve bir UIImageView nesnesi, o anda görüntülediği görüntünün boyutunu döndürür.

Demek istediğim, çözümüne herhangi bir mantık olmadan rastlamış gibi, çünkü dokümantasyonu okurken, iletilen parametre [webView sizeThatFits: ...]en azından istenen değerlere sahip olmalı width. Onun çözümü ile, istenen genişlik bir parametre ile webViewçağırmadan önce 'nin çerçevesine ayarlanır . Bu yüzden bu çözümün iOS 6'da "şans eseri" çalıştığını düşünüyorum.sizeThatFitsCGSizeZero

IOS 5.0 ve sonrası için çalışma avantajına sahip daha rasyonel bir yaklaşım hayal ettim ... Ve ayrıca birden fazla webView'un bulunduğu karmaşık durumlarda (Mülkünün webView.scrollView.scrollEnabled = NObir scrollView.

Düzenini webViewistenen düzene zorlamak widthve karşılık gelen heightseti webViewkendisine geri almak için kodum :

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.
}

Swift 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;
}

Benim örnekte, o Not webViewözel bir gömülmüş scrollViewdiğer sahip webViews... Tüm bu webViewsonların vardı webView.scrollView.scrollEnabled = NOve ben eklenti zorunda kodunun son parçası hesaplanması oldu heightait contentSizebenim geleneğin scrollViewbu gömme webViewsama kolay gibiydi toplayarak olarak benim webView's frame.size.heighthile ile hesaplanan yukarıda açıklanan ...


3
Bu, kabul edilen çözümden çok daha zariftir ve belgelenmemiş ve tanımlanmamış davranışlara dayanmaz. Javascript eklemekten çok daha az hack. Keşke bunu 64 kez yükseltebilseydim.
bugloaf

Ben de tam olarak bunu açıklamaya çalıştım. Teşekkür ederim. Ve büyük avantajı, tüm iOS sürümlerinde çalışıyor gibi görünmesidir.
Phenomena

Çözüm gerçekten güzel görünüyor, ancak hala iOS 4.x'i hedefliyorsanız dikkatli olun. -[UIWebView scrollView]yalnızca iOS 5.0 veya sonraki sürümlerde mevcuttur.
Ortwin Gentz

2
Bunu ve benzer bir çözümü denedim ve bunun yalnızca web görünümü veya gömülü olduğu görünüm zaten ekranda varsa işe yaradığı ortaya çıktı. Bunu, web görünümünü görünüm hiyerarşisine eklemeden önce denerseniz, sonuç manuel boyut yüksekliği olacaktır.
2013

1
diğer çözüm her seferinde işe yaramıyor, nedenini bilmiyorum, ama işin püf noktası bu !!! bu doğru cevap olarak etiketlenmelidir.
Vassily

37

Ortwin'in cevabını çoğu zaman sadece işe yarayan bulduğum için bu soruyu yeniden canlandırıyorum ...

webViewDidFinishLoadYöntem birden çok kez çağrılabilir ve döndürdüğü ilk değer sizeThatFitsnihai boyutu ne olmalı sadece bazı kısmıdır. Sonra nedeni ne olursa olsun sonraki çağrısı için sizeThatFitszaman webViewDidFinishLoadyangınlar yine yanlış Daha önce yaptığı aynı değeri dönecektir! Bu, aynı içerik için bir tür eşzamanlılık sorunu gibi rastgele gerçekleşecek. Belki de bu davranış zamanla değişti, çünkü iOS 5 için geliştiriyorum ve bunun sizeToFitda aynı şekilde çalıştığını buldum (daha önce böyle olmamasına rağmen?)

Bu basit çözüme karar verdim:

- (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
    }
}

Güncelleme: Yorumlarda belirtildiği gibi, içeriğin küçüldüğü durumu yakalayamadığını gördüm. Tüm içerik ve işletim sistemi sürümü için doğru olup olmadığından emin değil, bir deneyin.


Web sayfanıza resimler veya başka varlıklar eklediniz mi? Bu, delegenin ek işten çıkarılmasına neden olabilir. Bunun dışında, çözümünüz yalnızca webView.scalesPageToFit = YESyukarıdaki yorumda belirtilen @Sijo gibi bir şey vermediğinizde çalışır .
Ortwin Gentz

Evet, muhtemelen budur - çözümünüz salt metin durumlarımda iyi çalışıyor. Ancak sizeThatFits'in son delege çağrısı sırasında doğru değeri vermemesi sorun ... çok garip. Hangi çağrının son olacağını nasıl bilebiliriz ki o zamana kadar sizeThatFits'i kullanmaya çalışmayız?
Kieran Harper

İlk temsilci aramasında sizeThatFits'ten doğru sonucu alırsanız, sonraki aramaları yok sayamaz mısınız?
Ortwin Gentz

Evet ama tam tersi :) İlk delege çağrısı yanlış sizeThatFits ile sonuçlanabilir, sonra bu değer sabitlenir. Sanki her iki sizeThatFits web görünümünü bir şekilde değiştiriyor, böylece başka sizeThatFits çağrıları da aynı sonucu veriyor veya bunu yapan, hala yüklenirken çerçevenin ayarıdır.
Kieran Harper

1
Bu çözüm de doğru çalışmıyor. Daha büyük bir sayfa ayarladıktan ve ardından daha küçük bir sayfa belirledikten sonra, her zaman daha büyük değeri döndürürsünüz. Görünüşe göre "tembel" iç görüşleri genişletiyor. Bunun için herhangi bir geçici çözüm var mı?
Legoless

23

Xcode 8 ve iOS 10'da bir web görünümünün yüksekliğini belirlemek için. kullanarak boy alabilirsin

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

OR Swift için

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

1
Web görünümü içerik boyutunu kullanarak bulmaya çalışın .. myWebView.scrollView.contentSize.height
Hardik Thakkar

8

Basit bir çözüm sadece kullanmak olabilir, webView.scrollView.contentSizeancak bunun JavaScript ile çalışıp çalışmadığını bilmiyorum. Kullanılan JavaScript yoksa, bu kesinlikle işe yarar:

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

3

AFAIK, [webView sizeThatFits:CGSizeZero]içerik boyutunu anlamak için kullanabilirsiniz .


3

Bu tuhaf!

Ben çözümler test hem sizeThatFits:ve [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"]edilmektedir DEĞİL benim için çalışıyor.

Ancak, web sayfası içeriğinin doğru yüksekliğini elde etmenin ilginç ve kolay bir yolunu buldum. Şu anda bunu temsilci yöntemimde kullandım scrollViewDidScroll:.

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

İOS 9.3 Simülatör / Cihazda doğrulandı, iyi şanslar!

DÜZENLE:

Arka plan: html içeriği, dize değişkenim ve HTTP içerik şablonum tarafından hesaplanır loadHTMLString:baseURL:, yöntemle yüklenir , burada kayıtlı JS komut dosyası yoktur.


3

İOS10 için, ben 0 (sıfır) değerini başlamıştı document.heightböylece document.body.scrollHeightWeb görünümü doküman yüksekliği elde etmek için bir çözümdür. Sorun için de çözülebilir width.


2

Ben kullanıyorum UIWebViewbir Subview değildir (ve böylece pencere hiyerarşisinin parçası olmayan) için HTML içeriğinin boyutlarını belirlemek için bu UITableViewCells. Bağlantısı kesilen kişinin UIWebViewboyutunu doğru şekilde bildirmediğini buldum -[UIWebView sizeThatFits:]. Ek olarak, https://stackoverflow.com/a/3937599/9636'da belirtildiği gibi , uygun yüksekliği elde etmek için UIWebView's' frame heightyi 1'e ayarlamalısınız .

Eğer UIWebViewyüksekliği çok büyükse (yani 1000 olarak ayarladıysanız, ancak HTML içerik boyutu sadece 500 ise):

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

Hepsi height1000 döndürür .

Bu durumda sorunumu çözmek için , görev bilinciyle oyladığım https://stackoverflow.com/a/11770883/9636'yı kullandım . Ancak, bu çözümü yalnızca benim UIWebView.frame.width. İle aynı olduğunda kullanırım -[UIWebView sizeThatFits:] width.


1
Bu iş parçacığındaki tüm örnekleri denedim ve belgenin yüksekliğini almak için "document.height" kullanmak en güvenilir olanıdır. Yaptığım tüm testlerde getElementById (...) 'ı güvenilmez kılan bir konteynerin (DIV gibi) dışına ek HTML eklenebilir. document.height çok işe yarar.
John Rogers

2

Buradaki önerilerin hiçbiri durumuma yardımcı olmadı, ancak bana cevap veren bir şey okudum. Sabit bir UI denetimleri kümesini takiben bir UIWebView olan bir ViewController'ım var. Tüm sayfanın, UI kontrolleri HTML içeriğine bağlıymış gibi kaydırılmasını istedim, bu yüzden UIWebView'da kaydırmayı devre dışı bıraktım ve ardından bir üst kaydırma görünümünün içerik boyutunu doğru şekilde ayarlamam gerekiyor.

Önemli ipucu, UIWebView'in ekrana işlenene kadar boyutunu doğru bir şekilde rapor etmemesidir. Bu yüzden içeriği yüklediğimde içerik boyutunu mevcut ekran yüksekliğine ayarlıyorum. Ardından, viewDidAppear'da kaydırma görünümünün içerik boyutunu doğru değere güncellerim. Yerel içerik için loadHTMLString'i çağırdığım için bu benim için çalıştı. LoadRequest kullanıyorsanız, html'nin ne kadar hızlı alındığına bağlı olarak webViewDidFinishLoad'daki contentSize'yi de güncellemeniz gerekebilir.

Titreşim olmaz, çünkü kaydırma görünümünün yalnızca görünmeyen kısmı değiştirilir.


Korkarım, web görünümünün viewDidAppear'da bitecek kadar hızlı yüklenmesi saf bir şans. Muhtemelen bu, yalnızca görünüm animasyonlu göründüğünde işe yarar, böylece animasyon içeriği yüklemek için yeterince uzun olur. webViewDidFinishLoadGüvenli yaklaşıma güvenmeyi tercih ederim .
Ortwin Gentz

2

HTML'niz iframe'ler gibi ağır HTML içerikleri içeriyorsa (ör. Facebook-, twitter, instagram-embed'ler) gerçek çözüm çok daha zordur, önce HTML'nizi sarın:

[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>"];

Ardından , shouldStartLoadWithRequest öğenize x-update- webview -height -scheme işleme ekleyin :

    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;
    }
    ...

Ve son olarak aşağıdaki kodu layoutSubviews'ınıza ekleyin :

    ...
    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 kodunuzun içinde zaman geciktirme (SetTimeout ve setInterval) uygulayabilirsiniz - bu size kalmış.

PSS UIWebView ve Facebook Yerleştirmeleri hakkında önemli bilgi: Gömülü Facebook gönderisi, UIWebView'da düzgün görünmüyor


Bu ilginç! Komut dosyası bölümünün ne işe yaradığını ve gerekirse nasıl değiştirilebileceğini açıklayabilir misiniz? Periyodik olarak yüksekliği kontrol edecek gibi görünüyor, ancak birden fazla zaman aralığı mı var?
Kieran Harper

@KieranHarper, tam olarak neye ihtiyacın var?
MingalevME

2

Web görünümünü kaydırma görünümünde bir yerde alt görünüm olarak kullanırken, yükseklik sınırlamasını sabit bir değere ayarlayabilir ve daha sonra ondan çıkış yapabilir ve aşağıdaki gibi kullanabilirsiniz:

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

1

Ben de bu soruna takılı kaldım, sonra fark ettim ki webView'ın dinamik yüksekliğini hesaplamak istersem, önce webView genişliğini söylemem gerekiyor, bu yüzden js'den önce bir satır ekliyorum ve alabiliyorum çok doğru gerçek yükseklik.

Kod şu şekilde basit:

-(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. Önce webView yüksekliğini 0 olarak ayarlayın .
  2. In webViewDidFinishLoadDelegesi:

let height = webView.scrollView.contentSize.height

Adım 1 olmadan, webview.height> gerçek contentHeight ise, 2. adım webview.height'ı döndürür, ancak contentize.height'ı döndürmez.


Benim durumumda gerekli çerçeveyi alamamamın nedeni budur .. yükseltildi
NSPratik

0

Ayrıca iOS 7'de belirtilen tüm yöntemlerin düzgün çalışması için bunu görünüm denetleyicisi viewDidLoadyönteminize ekleyin :

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

Aksi takdirde hiçbir yöntem olması gerektiği gibi çalışmaz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.