IOS 8 SDK: модальный интерфейс UIWebView и камера/подборщик изображений

Я обнаружил, что при компиляции для iOS 8 (и работающей в iOS 8), UIWebView не может отображать устройство выбора камеры/изображения, если UIWebView находится в контроллере представления, представленном модально. Он работает без проблем в представлении контроллеров, непосредственно "висящих" из окна rootViewController или контроллеров просмотра, выталкиваемых из него.

Приложение-тест можно найти в https://dl.dropboxusercontent.com/u/6214425/TestModalWebCamera.zip, но я опишу его ниже.

Мое тестовое приложение (построенное с помощью раскадровки, но реальное приложение не использует их) имеет два контроллера вида (неорганно названных ViewController и ViewController2). ViewController содержится в UINavigationController, который является контроллером корневого представления. ViewController содержит a UIWebView (работает нормально), кнопка, которая "показывает" ( "толкает" ) ViewController2, и UIBarButtonItem, которая представляет модально ViewController2. ViewController2 имеет другой UIWebView, который работает, когда "толкается", но не "представлен".

Оба ViewController и ViewController2 загружаются с помощью:

- (void)viewDidLoad {
  [super viewDidLoad];

  [self.webView loadHTMLString:@"<input type=\"file\" accept=\"image/*;capture=camera\">" baseURL:nil];
}

При попытке использовать модальный UIWebView Xcode печатает на консоли консоль и отклоняет приложение модальное:

Warning: Attempt to present <UIImagePickerController: 0x150ab800> on <ViewController2: 0x14623580> whose view is not in the window hierarchy!

Моя нынешняя теория состоит в том, что изменения в UIActionSheet до UIAlertController могли привести к этой ситуации, но ее довольно сложно доказать. На всякий случай я открою радар с Apple.

Кто-то нашел такую ​​же ситуацию и некоторое обходное решение?

Ответ 1

Я обнаружил, что в iOS 8.0.2 у iPad, похоже, нет этой ошибки, но iPhone по-прежнему.

Однако переопределение в контроллере представления, содержащем uiwebview

-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion

И проверка того, что есть представленныйViewController, кажется, работает.

Но нужно проверить побочные эффекты

#import "UiWebViewVC.h"

@interface UiWebViewVC ()

@property (weak, nonatomic) IBOutlet UIWebView *uiwebview;

@end

@implementation UiWebViewVC

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSURL *url = [NSURL URLWithString:@"http://html5demos.com/file-api-simple"];

    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    self.uiwebview.scalesPageToFit = YES;

    [self.uiwebview loadRequest:request];
}


-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
    if ( self.presentedViewController)
    {
        [super dismissViewControllerAnimated:flag completion:completion];
    }
}


@end

Ответ 2

У меня такая же проблема на iOS 9. Попытайтесь добавить ivar '_flag', а затем переопределите эти методы в контроллере представления с помощью UIWebView

#pragma mark - Avoiding iOS bug

- (UIViewController *)presentingViewController {

    // Avoiding iOS bug. UIWebView with file input doesn't work in modal view controller

    if (_flagged) {
        return nil;
    } else {
       return [super presentingViewController];
    }
}

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {

    // Avoiding iOS bug. UIWebView with file input doesn't work in modal view controller

    if ([viewControllerToPresent isKindOfClass:[UIDocumentMenuViewController class]]
    ||[viewControllerToPresent isKindOfClass:[UIImagePickerController class]]) {
        _flagged = YES;
    }

    [super presentViewController:viewControllerToPresent animated:flag completion:completion];
}

- (void)trueDismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {

    // Avoiding iOS bug. UIWebView with file input doesn't work in modal view controller

    _flagged = NO;
    [self dismissViewControllerAnimated:flag completion:completion];
}

Это работает для меня отлично

Ответ 3

У меня была аналогичная проблема, и я обнаружил, что элементы UIWebView в IOS не поддерживают элемент html:

Я не уверен, почему Apple решила не поддерживать этот ВАЖНЫЙ элемент html, но я уверен, что у них есть свои причины. (Несмотря на то, что этот элемент отлично работает на Safari в IOS.)

Во многих случаях, когда пользователь нажимает кнопку такого типа в UIWebView, он позволяет им выбирать/выбирать фотографию. ОДНАКО, UIWebView в IOS не имеет возможности прикреплять файлы, подобные этому, в данные POST при отправке формы.

Решение. Для выполнения одной и той же задачи вы можете создать аналогичную форму в InterfaceBuilder с помощью кнопки, запускающей UIImagePickerController. Затем вы создаете запрос HTTP POST со всеми данными формы и изображением. Это не так сложно, как кажется, посмотрите ссылку ниже для примера кода, который выполняет задание: ios Загрузка изображения и текста с помощью HTTP POST

Ответ 4

Для меня у меня обычно есть пользовательский UINavigationController, так что несколько представлений могут использовать одну и ту же логику. Поэтому для моего обходного пути (в Swift) вот что я ввел в свой пользовательский NavigationController.

import UIKit

class NavigationController: UINavigationController {

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

    override func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)?) {
        if let vc = self.presentedViewController {
            // don't bother dismissing if the view controller being presented is a doc/image picker
            if !vc.isKindOfClass(UIDocumentMenuViewController) || !vc.isKindOfClass(UIImagePickerController) {
                super.dismissViewControllerAnimated(flag, completion:completion)
            }
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // make sure that the navigation controller can't be dismissed
        self.view.window?.rootViewController = self
    }
}

Это означает, что вы можете иметь множество контроллеров представлений с веб-просмотрами, и все они будут работать с элементом загрузки файла.

Ответ 5

Хорошо, здесь обходной путь, который я использовал. Это 2-минутное изменение, довольно хакерское, но работает как ожидалось и позволяет избежать ошибок, которые у всех нас есть. В принципе, вместо того, чтобы представлять контроллер дочернего представления по модулю, я устанавливаю его в окно rootViewController и сохраняю ссылку на родительский контроллер. Итак, где я использовал это (в контроллере родительского представления):

presentViewController(newController, animated: true, completion: nil)

Теперь у меня есть этот

view.window?.rootViewController = newController

В обоих случаях родительский контроллер newController delegate, так как я сохраняю ссылку.

newController.delegate = self

Наконец, где в моем обратном вызове делегата для закрытия модальности, где я использовал это:

dismissViewControllerAnimated(true, completion: nil)

Теперь у меня есть это:

viewController.view.window?.rootViewController = self

Итак, мы получаем тот же эффект, что и контроллер представления, полностью захватив экран, а затем приведя его в действие, когда он будет выполнен. В этом случае, однако, он не анимирован. Я чувствую, что анимация перехода контроллера не будет очень сложной с некоторыми из встроенных анимаций просмотра.

Надеюсь, вы уже используете шаблон делегата для управления связью с модульным контроллером по рекомендации Apple, но если вы этого не сделаете, я уверен, что вы можете использовать любое количество методов, которые сохраняют ссылку и получают обратный вызов.

Ответ 6

Мое решение - создать пользовательский View Controller с настраиваемой модальной презентацией на основе иерархии дочерних элементов-контроллеров. Анимация будет такой же, чтобы пользователь не заметил разницы.

Мои предложения для анимации:

animateWithDuration:(animated ? 0.6 : 0.0)
                      delay:0.0
     usingSpringWithDamping:1.f
      initialSpringVelocity:0.6f
                    options:UIViewAnimationOptionCurveEaseOut

Он будет выглядеть так же, как анимация модального представления по умолчанию в iOS7/8.

Ответ 7

Что я делал, каждый раз, когда контроллер просмотра изменяется, конвертируйте представление назначения в корневой каталог.

с первого контроллера:

let web = self.storyboard?.instantiateViewController(withIdentifier:"web") as! UINavigationController

view.window?.rootViewController = web

вот как я передаю корень другому, и когда я вернулся к первому, я сделал это.

с веб-контроллера:

let first = self.storyboard?.instantiateViewController(withIdentifier: "reveal") as! SWRevealViewController
    present(first, animated: true, completion: nil)

и все в порядке, я могу показать модальный выбор фотографии из библиотеки, сделать фотографию и все остальное.

Я не знаю, хорошо ли это, но отлично работает в эмуляторе и устройстве.

Надеюсь, что это поможет.