Mam UIWebView
inną (pojedynczą stronę) zawartość. Chciałbym poznać CGSize
zawartość, aby odpowiednio zmienić rozmiar widoków moich rodziców. Oczywiste -sizeThatFits:
niestety po prostu zwraca aktualny rozmiar ramki WebView.
Mam UIWebView
inną (pojedynczą stronę) zawartość. Chciałbym poznać CGSize
zawartość, aby odpowiednio zmienić rozmiar widoków moich rodziców. Oczywiste -sizeThatFits:
niestety po prostu zwraca aktualny rozmiar ramki WebView.
Odpowiedzi:
Okazało się, że moje pierwsze przypuszczenie -sizeThatFits:
nie było całkowicie błędne. Wygląda na to, że działa, ale tylko wtedy, gdy ramka webView jest ustawiona na minimalny rozmiar przed wysłaniem -sizeThatFits:
. Następnie możemy skorygować zły rozmiar ramy przez rozmiar dopasowania. Brzmi okropnie, ale w rzeczywistości nie jest tak źle. Ponieważ obie zmiany klatek wykonujemy bezpośrednio po sobie, widok nie jest aktualizowany i nie migocze.
Oczywiście musimy poczekać, aż zawartość zostanie załadowana, więc umieszczamy kod w -webViewDidFinishLoad:
metodzie delegata.
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
}
Powinienem zwrócić uwagę, że istnieje inne podejście (dzięki @GregInYEG) z wykorzystaniem JavaScript. Nie wiem, które rozwiązanie działa lepiej.
Z dwóch hackerskich rozwiązań bardziej mi się podoba.
UIWebView
jest wyższy niż widok rodzica, musisz osadzić go w pliku UIScrollView
.
Mam inne rozwiązanie, które działa świetnie.
Z jednej strony podejście i rozwiązanie Ortwina działa tylko z iOS 6.0 i nowszymi wersjami, ale nie działa poprawnie na iOS 5.0, 5.1 i 5.1.1, az drugiej strony jest coś, czego nie lubię i nie mogę zrozumieć w podejściu Ortwina jest to użycie metody [webView sizeThatFits:CGSizeZero]
z parametrem CGSizeZero
: Jeśli czytasz oficjalną dokumentację Apple na temat tych metod i ich parametrów, jest to wyraźnie napisane:
Domyślna implementacja tej metody zwraca część rozmiaru prostokąta granic widoku. Podklasy mogą przesłonić tę metodę, aby zwrócić wartość niestandardową na podstawie żądanego układu dowolnych widoków podrzędnych. Na przykład obiekt UISwitch zwraca wartość o stałym rozmiarze, która reprezentuje standardowy rozmiar widoku przełącznika, a obiekt UIImageView zwraca rozmiar aktualnie wyświetlanego obrazu.
Chodzi mi o to, że to tak, jakby natknął się na swoje rozwiązanie bez żadnej logiki, ponieważ czytając dokumentację, przekazany parametr [webView sizeThatFits: ...]
powinien mieć przynajmniej pożądane width
. W jego rozwiązaniu żądana szerokość jest ustawiana na webView
ramkę przed wywołaniem sizeThatFits
z CGSizeZero
parametrem. Utrzymuję więc, że to rozwiązanie działa na iOS 6 przez przypadek.
Wyobraziłem sobie bardziej racjonalne podejście, które ma tę zaletę, że działa na iOS 5.0 i nowszych ... A także w złożonych sytuacjach, w których więcej niż jeden webView (ze swoją właściwością webView.scrollView.scrollEnabled = NO
jest osadzony w scrollView
.
Oto mój kod, który wymusza układ webView
na pożądany width
i height
przywraca odpowiedni zestaw do webView
samego siebie:
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;
}
Należy zauważyć, że w moim przykładzie, webView
został osadzony w zwyczaju scrollView
posiadania innych webViews
... Wszystkie te webViews
miały swoje webView.scrollView.scrollEnabled = NO
, a ostatni kawałek kodu Miałem dodać było obliczenie z height
z contentSize
mojego zwyczaju scrollView
osadzania nich webViews
, ale to było tak proste jak zsumowanie mój webView
„s frame.size.height
obliczane z trick opisany powyżej ...
-[UIWebView scrollView]
jest dostępny tylko w iOS 5.0 lub nowszym.
Wskrzeszenie tego pytania, ponieważ znalazłem odpowiedź Ortwina, która działa tylko przez większość czasu ...
webViewDidFinishLoad
Metoda może być wywoływany więcej niż raz, a pierwsza wartość zwracana przez sizeThatFits
to tylko pewna część tego, co ostateczna wielkość powinna być. Wtedy, z jakiegokolwiek powodu, następne wywołanie funkcji, sizeThatFits
gdy webViewDidFinishLoad
ponownie pożary, zwróci nieprawidłowo tę samą wartość, co wcześniej! Będzie się to zdarzało losowo dla tej samej zawartości, jakby był to jakiś problem z współbieżnością. Może to zachowanie zmieniło się z biegiem czasu, ponieważ buduję na iOS 5 i odkryłem, że sizeToFit
działa w bardzo podobny sposób (chociaż wcześniej tak nie było?)
Zdecydowałem się na to proste rozwiązanie:
- (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
}
}
Aktualizacja: znalazłem, jak wspomniano w komentarzach, nie wydaje się, aby uchwycić przypadek, w którym zawartość się zmniejszyła. Nie jesteś pewien, czy to prawda dla wszystkich treści i wersji systemu operacyjnego, spróbuj.
webView.scalesPageToFit = YES
tak, jak @Sijo wspomniano w komentarzu powyżej.
W Xcode 8 i iOS 10 do określenia wysokości widoku internetowego. Możesz uzyskać wysokość za pomocą
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
CGFloat height = [[webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"] floatValue];
NSLog(@"Webview height is:: %f", height);
}
LUB dla Swift
func webViewDidFinishLoad(aWebView:UIWebView){
let height: Float = (aWebView.stringByEvaluatingJavaScriptFromString("document.body.scrollHeight")?.toFloat())!
print("Webview height is::\(height)")
}
Prostym rozwiązaniem byłoby po prostu użycie, webView.scrollView.contentSize
ale nie wiem, czy to działa z JavaScript. Jeśli nie jest używany JavaScript, to na pewno działa:
- (void)webViewDidFinishLoad:(UIWebView *)aWebView {
CGSize contentSize = aWebView.scrollView.contentSize;
NSLog(@"webView contentSize: %@", NSStringFromCGSize(contentSize));
}
To dziwne!
Testowałem oba rozwiązania sizeThatFits:
i [webView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight"]
są nie pracuje dla mnie.
Jednak znalazłem interesujący, łatwy sposób na uzyskanie odpowiedniej wysokości zawartości strony internetowej. Obecnie użyłem go w mojej metodzie delegata scrollViewDidScroll:
.
CGFloat contentHeight = scrollView.contentSize.height - CGRectGetHeight(scrollView.frame);
Zweryfikowano w symulatorze / urządzeniu iOS 9.3, powodzenia!
EDYTOWAĆ:
Tło: zawartość html jest obliczana przez moją zmienną łańcuchową i szablon treści HTTP, ładowana metodą loadHTMLString:baseURL:
, brak tam zarejestrowanych skryptów JS.
Używam, UIWebView
który nie jest widokiem podrzędnym (a zatem nie jest częścią hierarchii okien), aby określić rozmiary zawartości HTML UITableViewCells
. Okazało się, że odłączony UIWebView
nie zgłasza poprawnie swojego rozmiaru z -[UIWebView sizeThatFits:]
. Ponadto, jak wspomniano w https://stackoverflow.com/a/3937599/9636 , należy ustawić UIWebView
„s frame
height
do 1 w celu uzyskania odpowiedniej wysokości w ogóle.
Jeśli UIWebView
wysokość jest zbyt duża (tj. Ustawiono ją na 1000, ale rozmiar zawartości HTML to tylko 500):
UIWebView.scrollView.contentSize.height
-[UIWebView stringByEvaluatingJavaScriptFromString:@"document.height"]
-[UIWebView sizeThatFits:]
Wszystkie zwracają height
1000.
Aby rozwiązać mój problem w tym przypadku, użyłem https://stackoverflow.com/a/11770883/9636 , na który sumiennie zagłosowałem. Jednak używam tego rozwiązania tylko wtedy, gdy mój UIWebView.frame.width
jest taki sam jak -[UIWebView sizeThatFits:]
width
.
Żadna z propozycji nie pomogła mi w mojej sytuacji, ale przeczytałem coś, co dało mi odpowiedź. Mam ViewController ze stałym zestawem kontrolek interfejsu użytkownika, a następnie UIWebView. Chciałem, aby cała strona przewijała się tak, jakby kontrolki interfejsu użytkownika były połączone z zawartością HTML, więc wyłączam przewijanie w UIWebView, a następnie muszę poprawnie ustawić rozmiar zawartości nadrzędnego widoku przewijania.
Ważną wskazówką okazało się to, że UIWebView nie zgłasza poprawnie swojego rozmiaru, dopóki nie zostanie wyrenderowany na ekranie. Więc kiedy ładuję zawartość, ustawiam rozmiar zawartości na dostępną wysokość ekranu. Następnie w viewDidAppear aktualizuję rozmiar zawartości widoku scrollview do prawidłowej wartości. To zadziałało dla mnie, ponieważ wywołuję loadHTMLString w lokalnej zawartości. Jeśli używasz loadRequest, może być konieczne zaktualizowanie contentSize również w webViewDidFinishLoad, w zależności od szybkości pobierania html.
Nie ma migotania, ponieważ zmienia się tylko niewidoczna część widoku przewijania.
webViewDidFinishLoad
bezpiecznym podejściu.
Jeśli twój HTML zawiera ciężkie treści HTML, takie jak iframe (tj. Facebook-, Twitter, Instagram-embed), prawdziwe rozwiązanie jest znacznie trudniejsze, najpierw zawiń swój 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>"];
Następnie dodaj obsługę x-update-webview-height -scheme do swojego shouldStartLoadWithRequest :
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;
}
...
Na koniec dodaj następujący kod w swoim 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 Możesz wdrożyć opóźnienie czasowe (SetTimeout i setInterval) w kodzie ObjectiveC / Swift - to zależy od Ciebie.
PSS Ważne informacje o UIWebView i osadzeniach na Facebooku: Osadzony post na Facebooku nie wyświetla się poprawnie w UIWebView
Używając widoku internetowego jako widoku podrzędnego gdzieś w widoku przewijania, możesz ustawić ograniczenie wysokości na jakąś stałą wartość, a później zrobić z niego wylot i używać go tak, jak:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
webView.scrollView.scrollEnabled = NO;
_webViewHeight.constant = webView.scrollView.contentSize.height;
}
Utknąłem też w tym problemie, wtedy zdałem sobie sprawę, że jeśli chcę obliczyć dynamiczną wysokość webView, to najpierw muszę podać szerokość webView, więc dodaję jedną linię przed js i okazuje się, że mogę uzyskać bardzo dokładna rzeczywista wysokość.
Kod jest prosty w następujący sposób:
-(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;
//......
}
Xcode8 swift3.1:
webViewDidFinishLoad
delegacie:let height = webView.scrollView.contentSize.height
Bez kroku 1, jeśli webview.height> current contentHeight, krok 2 zwróci wartość webview.height, ale nie contentize.height.
Również w iOS 7 dla poprawnego działania wszystkich wymienionych metod dodaj to w viewDidLoad
metodzie kontrolera widoku :
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {
self.automaticallyAdjustsScrollViewInsets = NO;
}
W przeciwnym razie żadna z metod nie działałaby tak, jak powinna.