[ios] WKWebView가 iOS 8에서 로컬 파일을로드하지 않음

이전 iOS 8 베타의 경우 로컬 웹 앱 (번들)을로드하면 UIWebViewWKWebView에서 모두 잘 작동 하며 새로운WKWebView API를 .

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

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

view.addSubview(webView)

하지만 베타 4에서는 빈 흰색 화면이 나타납니다 (UIWebView 아무것도로드되거나 실행되지 않는 것처럼 보이는 여전히 작동)이 나타납니다. 로그에 오류가 있습니다.

에 대한 샌드 박스 확장을 만들 수 없습니다. /

나를 올바른 방향으로 안내하는 데 도움이 필요하십니까? 감사!



답변

그들은 마침내 버그를 해결했습니다! 이제 -[WKWebView loadFileURL:allowingReadAccessToURL:]. 분명히 수정은 WWDC 2015 비디오 504 에서 몇 초의 가치가있었습니다. Safari View Controller를 소개합니다.

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

iOS8 ~ iOS10 (Swift 3) 용

으로 댄 Fabulish의 응답 상태이 WKWebView의 버그가 분명히 빠른 시일 내에 해결되지 않는 그가 말했다 같은 해결 방법이 있습니다 :

여기에서 해결 방법을 보여주고 싶었 기 때문에 대답하고 있습니다. https://github.com/shazron/WKWebViewFIleUrlTest에 표시된 IMO 코드 는 대부분의 사람들이 관심이없는 관련없는 세부 정보로 가득 차 있습니다.

해결 방법은 20 줄의 코드, 오류 처리 및 주석이 포함되어 있으며 서버가 필요하지 않습니다. 🙂

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
}

다음과 같이 사용할 수 있습니다.

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


답변

WKWebView는 해당 loadRequest:메서드 를 통해 file : URL에서 콘텐츠를로드 할 수 없습니다 . http://www.openradar.me/18039024

를 통해 콘텐츠를로드 할 수 loadHTMLString:있지만 baseURL이 file : URL이면 여전히 작동하지 않습니다.

iOS 9에는 원하는 작업을 수행하는 새로운 API 인 [WKWebView loadFileURL:allowingReadAccessToURL:] .

iOS 8에 대한 해결 방법이 있습니다. Shazron이 Objective-C의 https://github.com/shazron/WKWebViewFIleUrlTest 에서 파일/tmp/www복사 하고 거기에서로드 하는 방법을 보여줍니다 .

Swift에서 작업하는 경우 대신 nachos4d의 샘플을 사용해 볼 수 있습니다. (또한 shazron의 샘플보다 훨씬 짧으므로 shazron의 코드에 문제가있는 경우 대신 시도해보세요.)


답변

iOS 9 에서 [WKWebView loadFileURL : allowingReadAccessToURL :]을 사용하는 방법의 예입니다 .

웹 폴더를 프로젝트로 이동할 때 “폴더 참조 생성”을 선택하십시오.

여기에 이미지 설명 입력

그런 다음 다음과 같은 코드를 사용하십시오 (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 파일에서 다음과 같은 파일 경로를 사용하십시오.

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

이건 아니야

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

xcode 프로젝트로 이동 된 디렉토리의 예입니다.

여기에 이미지 설명 입력


답변

임시 해결 방법 : GuidoMB 에서 제안한대로 GCDWebServer를 사용하고 있습니다.

먼저 번들로 제공되는 “www /”폴더 ( “index.html”포함)의 경로를 찾습니다.

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

… 다음과 같이 시작하십시오.

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

중지하려면 :

[_webServer stop];
_webServer = nil;

iPad 2에서도 성능이 좋아 보입니다.


앱이 백그라운드로 들어간 후 충돌이 발생 했으므로 중지 applicationDidEnterBackground:하고 applicationWillTerminate:; 나는 / 시작에 다시 시작 application:didFinishLaunching...하고 applicationWillEnterForeground:.


답변

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

이것은 iOS 8.0+ dev.apple.com 의 문제를 해결했습니다.

또한 이것은 잘 작동하는 것 같습니다 …

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


답변

Dan Fabulich가 언급 한 솔루션 외에도 XWebView 는 또 다른 해결 방법입니다. [WKWebView loadFileURL : allowingReadAccessToURL :]확장을 통해 구현됩니다 .


답변

아직 댓글을 달 수 없으므로 별도의 답변으로 게시하고 있습니다.

이것은 nacho4d 솔루션 의 객관적인 C 버전입니다 . 지금까지 본 최고의 해결 방법입니다.

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