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)
и все в порядке, я могу показать модальный выбор фотографии из библиотеки, сделать фотографию и все остальное.
Я не знаю, хорошо ли это, но отлично работает в эмуляторе и устройстве.
Надеюсь, что это поможет.