WKWebView, iOS 8 altında yerel dosyaları yüklemiyor


123

Önceki iOS 8 betalar için, (Bundle) yerel bir web uygulaması yüklemek ve hem cezasını çalışır UIWebViewve WKWebView, ve hatta yeni kullanarak bir web oyun taşıdık WKWebViewAPI.

var url = NSURL(fileURLWithPath:NSBundle.mainBundle().pathForResource("car", ofType:"html"))

webView = WKWebView(frame:view.frame)
webView!.loadRequest(NSURLRequest(URL:url))

view.addSubview(webView)

Ancak beta 4'te, boş bir beyaz ekranım var ( UIWebViewhala çalışıyor), hiçbir şey yüklenmemiş veya çalıştırılmamış gibi görünüyor. Günlükte bir hata gördüm:

İçin korumalı alan uzantısı oluşturulamadı /

Beni doğru yöne yönlendirecek herhangi bir yardım var mı? Teşekkürler!


Ayrıca, viewDidLoad'daki görünüm hiyerarşisine webView eklemeyi deneyin ve isteği viewWillAppear'a yükleyin. WKWebView'um hala çalışıyor, ancak buna sahip oluyorum. Belki de WebView, bir görünüm hiyerarşisinde değilse istekleri yüklememek için bir optimizasyona sahiptir?
rvijay007

Bitti (viewDidLoad'daki view.addView ve viewWillAppear'daki loadRequest) ve aynı beyaz ekranı ve aynı hata mesajını görüyorum.
Lim Thye Chean

9
Bu sadece cihazda görülüyor çünkü simülatörde iyi çalışıyor. Bu arada Objc kullanıyorum.
GuidoMB

1
Bu, XCode 6 - beta 7'de bir sorun olmaya devam ediyor. Geçici çözümüm, yerel dosyaları sunmak için github.com/swisspol/GCDWebServer'ı kullanmaktı .
GuidoMB

2
İOS 8.0.1 altında test eden oldu mu?
Lim Thye Chean

Yanıtlar:


106

Sonunda hatayı çözdüler! Şimdi kullanabiliriz -[WKWebView loadFileURL:allowingReadAccessToURL:]. Görünüşe göre bu düzeltme WWDC 2015 videosunda birkaç saniye değerindeydi 504 Safari View Controller Tanıtımı

https://developer.apple.com/videos/wwdc/2015/?id=504

İOS8 ~ iOS10 için (Swift 3)

As Dan Fabulish cevabı bu WKWebView bir hata olduğunu bildiren görünüşte yakın zamanda çözülmüyor ve dedi gibi bir iş çevresinde var olduğu :)

Cevap veriyorum çünkü buradaki çalışmayı göstermek istedim. Https://github.com/shazron/WKWebViewFIleUrlTest'te gösterilen IMO kodu , çoğu insanın muhtemelen ilgilenmediği ilgisiz ayrıntılarla doludur.

Çözüm 20 satır kod, hata işleme ve yorumlar dahildir, sunucuya gerek yoktur :)

func fileURLForBuggyWKWebView8(fileURL: URL) throws -> URL {
    // Some safety checks
    if !fileURL.isFileURL {
        throw NSError(
            domain: "BuggyWKWebViewDomain",
            code: 1001,
            userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("URL must be a file URL.", comment:"")])
    }
    try! fileURL.checkResourceIsReachable()

    // Create "/temp/www" directory
    let fm = FileManager.default
    let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("www")
    try! fm.createDirectory(at: tmpDirURL, withIntermediateDirectories: true, attributes: nil)

    // Now copy given file to the temp directory
    let dstURL = tmpDirURL.appendingPathComponent(fileURL.lastPathComponent)
    let _ = try? fm.removeItem(at: dstURL)
    try! fm.copyItem(at: fileURL, to: dstURL)

    // Files in "/temp/www" load flawlesly :)
    return dstURL
}

Ve şu şekilde kullanılabilir:

override func viewDidLoad() {
    super.viewDidLoad()
    var fileURL = URL(fileURLWithPath: Bundle.main.path(forResource:"file", ofType: "pdf")!)

    if #available(iOS 9.0, *) {
        // iOS9 and above. One year later things are OK.
        webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
    } else {
        // iOS8. Things can (sometimes) be workaround-ed
        //   Brave people can do just this
        //   fileURL = try! pathForBuggyWKWebView8(fileURL: fileURL)
        //   webView.load(URLRequest(url: fileURL))
        do {
            fileURL = try fileURLForBuggyWKWebView8(fileURL: fileURL)
            webView.load(URLRequest(url: fileURL))
        } catch let error as NSError {
            print("Error: " + error.debugDescription)
        }
    }
}

2
Klasörün tamamını, resimleri ve tümünü kopyalamak için bazı değişiklikler. let orgFolder = NSBundle.mainBundle().resourcePath! + "/www"; var newFilePath = pathForBuggyWKWebView(orgFolder) self.loadingWebView!.loadRequest(NSURLRequest(URL: NSURL.fileURLWithPath(newFilePath!+"/Loading.html")!))
Piwaf

1
Piwaf'ın çözümü benim için biraz daha iyi çalıştı, yukarıdaki örnekte verilen "self.loadingWebView" değil "self.webView" olması gerektiğini unutmayın
Patrick Fabrizius

2
Teşekkürler @ nacho4d. TLDR; / tmp klasör çözümü 8.0'da çalışmayacak, 8.0.2'de çalışacaktır. Bu çözümü test cihazımda çalıştırmak için sorun yaşıyordum ve sonunda denemek için shazron'un deposunu klonladım. Bu da işe yaramadı. Shazron'un çözümünün, cihazımın iPhone 6 Plus'ta 8.0 (12A366) çalıştırdığı iOS sürümünde çalışmadığı ortaya çıktı. İOS 8.0.2 çalıştıran bir cihazda (iPad Mini) denedim ve iyi çalışıyor.
jvoll

1
_ = deneyin? let _ = try yerine fm.removeItemAtURL (dstURL)? fileMgr.removeItemAtURL (dstURL)
DàChún

3
Yalnızca basit web siteleri için. Ajax kullanıyorsanız veya yerel görünümleri açısal olarak yüklüyorsanız, "Çapraz kaynak istekleri yalnızca HTTP için desteklenir" bekleyin. Tek geri dönüşünüz, yerel ağda göründüğü için sevmediğim yerel web sunucusu yaklaşımıdır. Bunu gönderide belirtmek gerekiyor, halkı birkaç saat kurtarın.
jenson-button-event

83

WKWebView, loadRequest:yöntemi aracılığıyla dosya: URL'lerden içerik yükleyemez . http://www.openradar.me/18039024

Üzerinden içerik yükleyebilirsiniz loadHTMLString:, ancak temelURL'niz bir dosya: URL ise, o zaman yine çalışmaz.

iOS 9, istediğinizi yapacak yeni bir API'ye sahip [WKWebView loadFileURL:allowingReadAccessToURL:] .

İOS 8 için Shazron tarafından Objective-C'de gösterilen bir geçici çözüm var https://github.com/shazron/WKWebViewFIleUrlTest , dosyaları/tmp/www buraya kopyalayıp oradan yüklemek için .

Swift'de çalışıyorsanız, bunun yerine nachos4d'nin örneğini deneyebilirsiniz . (Ayrıca shazron'un örneğinden çok daha kısadır, bu nedenle shazron'un koduyla ilgili sorun yaşıyorsanız, bunun yerine onu deneyin.)


1
Bir geçici çözüm (burada bahsedilmiştir: devforums.apple.com/message/1051027 ) içeriği tmp'ye taşımak ve oradan ona erişmek. Hızlı
Mike M

6
8.1'de düzeltilmedi.
Matt

1
Dosyaları / tmp konumuna taşımak benim için çalışıyor. Ama ... aman tanrım, bu testlerden nasıl geçti?
Greg Maletic

1
Bu örnek , sadece bir demo için ÇOK ÇOK FAZLA koddur. Okunacak dosya içeride olmalıdır /tmp/www/. Dizin oluşturmak için NSTemporaryDirectory()ve kullanın (çünkü varsayılan olarak böyle bir dizin yoktur). Sonra dosyanızı oraya kopyalayın ve şimdi bu dosyayı okuyun :)NSFileManagerwww
nacho4d

2
8.3 ve hala düzeltilmedi ...?
ninjaneer

8

Nasıl kullanımına bir örnek [WKWebView loadFileURL: allowingReadAccessToURL:] ile IOS 9 .

Web klasörünü bir projeye taşıdığınızda, "Klasör referansları oluştur" u seçin

görüntü açıklamasını buraya girin

Sonra şuna benzer bir kod kullanın (Swift 2):

if let filePath = NSBundle.mainBundle().resourcePath?.stringByAppendingString("/WebApp/index.html"){
  let url = NSURL(fileURLWithPath: filePath)
  if let webAppPath = NSBundle.mainBundle().resourcePath?.stringByAppendingString("/WebApp") {
    let webAppUrl = NSURL(fileURLWithPath: webAppPath, isDirectory: true)
    webView.loadFileURL(url, allowingReadAccessToURL: webAppUrl)
  }
}

Html dosyasında bunun gibi dosya yollarını kullanın

<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">

böyle değil

<link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet">

Bir xcode projesine taşınan bir dizin örneği.

görüntü açıklamasını buraya girin


6

Geçici çözüm: GuidoMB tarafından önerildiği gibi GCDWebServer kullanıyorum.

Önce paketlenmiş "www /" klasörümün yolunu buluyorum ("index.html" içeren):

NSString *docRoot = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"www"].stringByDeletingLastPathComponent;

... sonra şu şekilde başlatın:

_webServer = [[GCDWebServer alloc] init];
[_webServer addGETHandlerForBasePath:@"/" directoryPath:docRoot indexFilename:@"index.html" cacheAge:3600 allowRangeRequests:YES];
[_webServer startWithPort:port bonjourName:nil];

Durdurmak için:

[_webServer stop];
_webServer = nil;

Performans, iPad 2'de bile iyi görünüyor.


Uygulama arka plana girdikten sonra bir kilitlenme fark ettim, bu yüzden onu durdurdum applicationDidEnterBackground:ve applicationWillTerminate:; Başlatıyorum / yeniden başlatıyorum application:didFinishLaunching...ve applicationWillEnterForeground:.


1
Bir "sunucu" kullanmak, muhtemelen daha WKWebViewfazlasını sağlayan performans avantajlarını tüketir UIWebView. Bu düzeltilene kadar eski API ile devam etmek de mümkün.
ray

@ray Tek Sayfalı Uygulama ile Değil.
EthanB

GCDWebServer'ı da uygulamamın dahili yapılarında çalıştırdım. Ve uygulamanızda çok sayıda javascript varsa, sunucu buna değer. Ancak şu anda WKWebView'ı kullanmamı engelleyen başka sorunlar da var, bu yüzden iOS 9'da iyileştirmeler yapmayı umuyorum.
Tom Hamming

Ve bir Web Görünümü'nde nasıl gösterilir? GCDWebServer'ın ne için olduğunu gerçekten anlamıyorum?
chipbk10

1
Web görünümünü "file: // ....." olarak göstermek yerine, "http: // localhost: <port> / ..." 'a yönlendirin.
EthanB

5
[configuration.preferences setValue:@"TRUE" forKey:@"allowFileAccessFromFileURLs"];

Bu benim için sorunu çözdü iOS 8.0+ dev.apple.com

bu da gayet iyi çalışıyor gibi görünüyor ...

NSString* FILE_PATH = [[[NSBundle mainBundle] resourcePath]
                       stringByAppendingPathComponent:@"htmlapp/FILE"];
[self.webView
    loadFileURL: [NSURL fileURLWithPath:FILE_PATH]
    allowingReadAccessToURL: [NSURL fileURLWithPath:FILE_PATH]
];

DOSYA yerine DIR de koyabilirsiniz.
nullqube

Bu büyük bir keşif. Neden daha yüksek oylanmadığını bilmiyorum.
plivesey

Bu gönderi daha fazla bilgi
içeriyor

configuration.preferences setValue, iOS 9.3'te kilitlenmeye neden olacak
Vignesh Kumar

İOS 13 SDK kullanıyorum ancak iOS 8 ile uyumluluğu korumaya çalışıyorum ve allowFileAccessFromFileURLsyaklaşım ile çöküyor NSUnknownKeyException.
arlomedia

4

Dan Fabulich tarafından bahsedilen çözümlerin yanı sıra, XWebView başka bir geçici çözümdür . [WKWebView loadFileURL: allowingReadAccessToURL:] olan uzantısı ile uygulanır .


1
Bu sorun için diğer çözüm yollarına baktıktan sonra XWebView kullanmaya karar verdim. XWebView, Swift'de uygulanan bir çerçevedir ancak onu iOS 8 Objective-C uygulamamda kullanırken herhangi bir sorun yaşamadım.
chmaynard

4

Henüz yorum yapamıyorum, bu yüzden bunu ayrı bir cevap olarak gönderiyorum.

Bu, nacho4d çözümünün nesnel-c sürümüdür . Şimdiye kadar gördüğüm en iyi çözüm.

- (NSString *)pathForWKWebViewSandboxBugWithOriginalPath:(NSString *)filePath
{
    NSFileManager *manager = [NSFileManager defaultManager];
    NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"www"];
    NSError *error = nil;

    if (![manager createDirectoryAtPath:tempPath withIntermediateDirectories:YES attributes:nil error:&error]) {
        NSLog(@"Could not create www directory. Error: %@", error);

        return nil;
    }

    NSString *destPath = [tempPath stringByAppendingPathComponent:filePath.lastPathComponent];

    if (![manager fileExistsAtPath:destPath]) {
        if (![manager copyItemAtPath:filePath toPath:destPath error:&error]) {
            NSLog(@"Couldn't copy file to /tmp/www. Error: %@", error);

            return nil;
        }
    }

    return destPath;
}

1
Simülatörde bunu iOS 8'de çalıştıramıyorum. / Tmp / www içine bir .png koymak ve sonra html'mde img etiketi kullanmak istiyorum. İmg tag src için ne kullanmalıyım?
user3246173

tam da ihtiyacım olan şeydi. Teşekkürler!
Tinkerbell

3

Yerel bir resmi aşağıdaki gibi daha büyük bir HTML dizesinin ortasında görüntülemeye çalışmanız durumunda:, <img src="file://...">hala cihazda görünmüyor, bu yüzden görüntü dosyasını NSData'ya yükledim ve src dizesini ile değiştirerek görüntüleyebildim verinin kendisi. WKWebView'e yüklenecek HTML dizesini oluşturmaya yardımcı olacak örnek kod; burada sonuç, src = "" tırnak işaretlerinin içindekilerin yerini alacaktır:

Swift:

let pathURL = NSURL.fileURLWithPath(attachmentFilePath)
guard let path = pathURL.path else {
    return // throw error
}
guard let data = NSFileManager.defaultManager().contentsAtPath(path) else {
    return // throw error
}

let image = UIImage.init(data: data)
let base64String = data.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
result += "data:image/" + attachmentType + "base64," + base64String

var widthHeightString = "\""
if let image = image {
    widthHeightString += " width=\"\(image.size.width)\" height=\"\(image.size.height)\""
}

result += widthHeightString

Objective-C:

NSURL *pathURL = [NSURL fileURLWithPath:attachmentFilePath];
NSString *path = [pathURL path];
NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];

UIImage *image = [UIImage imageWithData:data];
NSString *base64String = [data base64EncodedStringWithOptions:0];
[result appendString:@"data:image/"];
[result appendString:attachmentType]; // jpg, gif etc.
[result appendString:@";base64,"];
[result appendString:base64String];

NSString *widthHeightString = @"\"";
if (image) {
    widthHeightString = [NSString stringWithFormat:@"\" width=\"%f\" height=\"%f\"", image.size.width, image.size.height];
}
[result appendString:widthHeightString];

hızlı sürümde, lütfen base64'ten önce bir noktalı virgül ekleyin. result += "data:image/" + attachmentType + ";base64," + base64String
shahil

Teşekkürler, benim için işe yarayan tek şey bu, çünkü bir .gif dosyasını cihazdaki bir geçici klasöre indiriyorum ve sonra bu dosyayı bir HTML dizesi içinde WKWebViewolarak <img>yüklüyorum.
Bay Zystem

1

Aşağıdakileri kullanıyorum. Üzerinde çalıştığım fazladan bazı şeyler var, ancak loadRequest hakkında yorum yaptığımı ve loadHTMLString çağrısının yerini aldığımı görebilirsiniz. Umarım bu hatayı düzeltene kadar yardımcı olur.

import UIKit
import WebKit

class ViewController: UIViewController, WKScriptMessageHandler {

    var theWebView: WKWebView?

    override func viewDidLoad() {
        super.viewDidLoad()

        var path = NSBundle.mainBundle().pathForResource("index", ofType: "html", inDirectory:"www" )
        var url = NSURL(fileURLWithPath:path)
        var request = NSURLRequest(URL:url)
        var theConfiguration = WKWebViewConfiguration()

        theConfiguration.userContentController.addScriptMessageHandler(self, name: "interOp")

        theWebView = WKWebView(frame:self.view.frame, configuration: theConfiguration)

        let text2 = String.stringWithContentsOfFile(path, encoding: NSUTF8StringEncoding, error: nil)

        theWebView!.loadHTMLString(text2, baseURL: nil)

        //theWebView!.loadRequest(request)

        self.view.addSubview(theWebView)


    }

    func appWillEnterForeground() {

    }

    func appDidEnterBackground() {

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func userContentController(userContentController: WKUserContentController!, didReceiveScriptMessage message: WKScriptMessage!){
        println("got message: \(message.body)")

    }

}

3
Bu sadece HTML dosyaları için işe yarıyor gibi görünüyor, ancak diğer kaynaklar için geçerli değil.
Lim Thye Chean

1

İOS8 altında bu sorunu çözmesi gerekenler için:

Sayfanız karmaşık değilse, sayfayı Tek Sayfalı Uygulama yapmayı tercih edebilirsiniz.

Başka bir deyişle, tüm kaynakları html dosyasına yerleştirmek için.

Yapılacaklar: 1. js / css dosyanızın içeriğini sırasıyla html dosyasındaki / etiketlerine kopyalayın; 2. Resim dosyalarınızı uygun şekilde değiştirmek için svg'ye dönüştürün. 3. Örneğin [webView loadHTMLString: baseURL:] kullanarak sayfayı eskisi gibi yükleyin

Bir svg görüntüsünü biçimlendirmekten biraz farklıydı, ancak sizi çok fazla engellememelidir.

Sayfa oluşturma performansının biraz azaldığı görülüyordu, ancak iOS8 / 9/10 altında bu kadar basit bir çözümün çalışmasına değerdi.



0

PHP'nin web sunucusunu OS X üzerinde kullanmayı başardım. Geçici / www dizinine kopyalamak benim için işe yaramadı. Python SimpleHTTPServer, muhtemelen bir korumalı alan sorunu olan MIME türlerini okumak istemekten şikayet etti.

İşte kullanan bir sunucu php -S:

let portNumber = 8080

let task = NSTask()
task.launchPath = "/usr/bin/php"
task.arguments = ["-S", "localhost:\(portNumber)", "-t", directoryURL.path!]
// Hide the output from the PHP server
task.standardOutput = NSPipe()
task.standardError = NSPipe()

task.launch()

0

@ nacho4d çözümü iyidir. Biraz değiştirmek istiyorum ama gönderinizde nasıl değiştireceğimi bilmiyorum. Bu yüzden buraya koyuyorum umarım aldırmazsın. Teşekkürler.

Bir www klasörünüz varsa, png, css, js vb. Gibi birçok başka dosya vardır. Daha sonra tüm dosyaları tmp / www klasörüne kopyalamanız gerekir. örneğin, aşağıdaki gibi bir www klasörünüz var: görüntü açıklamasını buraya girin

sonra Swift 2.0'da:

override func viewDidLoad() {
    super.viewDidLoad()

    let path = NSBundle.mainBundle().resourcePath! + "/www";
    var fileURL = NSURL(fileURLWithPath: path)
    if #available(iOS 9.0, *) {
        let path = NSBundle.mainBundle().pathForResource("index", ofType: "html", inDirectory: "www")
        let url = NSURL(fileURLWithPath: path!)
        self.webView!.loadRequest(NSURLRequest(URL: url))
    } else {
        do {
            fileURL = try fileURLForBuggyWKWebView8(fileURL)
            let url = NSURL(fileURLWithPath: fileURL.path! + "/index.html")
            self.webView!.loadRequest( NSURLRequest(URL: url))
        } catch let error as NSError {
            print("Error: \(error.debugDescription)")
        }
    }
}

URLForBuggyWKWebView8 işlev dosyası @ nacho4d'den kopyalanır:

func fileURLForBuggyWKWebView8(fileURL: NSURL) throws -> NSURL {
    // Some safety checks
    var error:NSError? = nil;
    if (!fileURL.fileURL || !fileURL.checkResourceIsReachableAndReturnError(&error)) {
        throw error ?? NSError(
            domain: "BuggyWKWebViewDomain",
            code: 1001,
            userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("URL must be a file URL.", comment:"")])
    }

    // Create "/temp/www" directory
    let fm = NSFileManager.defaultManager()
    let tmpDirURL = NSURL.fileURLWithPath(NSTemporaryDirectory())
    try! fm.createDirectoryAtURL(tmpDirURL, withIntermediateDirectories: true, attributes: nil)

    // Now copy given file to the temp directory
    let dstURL = tmpDirURL.URLByAppendingPathComponent(fileURL.lastPathComponent!)
    let _ = try? fm.removeItemAtURL(dstURL)
    try! fm.copyItemAtURL(fileURL, toURL: dstURL)

    // Files in "/temp/www" load flawlesly :)
    return dstURL
}

-1

Kullanmayı dene

[webView loadHTMLString:htmlFileContent baseURL:baseURL];

Hala çalışıyor gibi görünüyor. Hala.


Teşekkürler deneyeceğim.
Lim Thye Chean

3
Bu, kaynaklar için değil, yalnızca HTML dosyası için çalışır, değil mi?
Lim Thye Chean

1
Ne yazık ki evet, sadece HTML dosyası yükleniyor gibi görünüyor. Umarım yerel dosyaları yüklemek için yeni bir kısıtlama değil, yalnızca bir hata olur. Bu hatayı WebKit kaynaklarında bulmaya çalışıyorum.
Oleksii

1
Aynı sorunla karşılaştım - HTML dosyaları yüklendi ancak yerel dosya sistemindeki resimler ve diğer kaynaklar yüklenmedi. Konsolda, WebKit "yerel kaynak yüklemesine izin verilmiyor" hatası veriyor. Bunun için bir hata kaydettim, radar # 17835098
mszaro
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.