How to implement Picture in Picture Webview on IOS Swift? - ios

I new swift , i want to implement feature picture in picture (PiP) in webview for android , ios . I've found all the posts here and implement some code :
for android (https://stackoverflow.com/a/54061449/15311463) It working
for ios not working . I configed BackgourdMode in Xcode . What i am missing ?
private var pictureInPictureController: AVPictureInPictureController!
let source = """
document.addEventListener('click', function(e){
if (e.target.id === 'pip_mobile_btn_img') {
window.webkit.messageHandlers.iosListener.postMessage('true');
}
})
"""
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
//self.performSegue(withIdentifier: "goToPlayer", sender: self)
//print("Hello Thinh")
if #available(iOS 14.2, *) {
self.pictureInPictureController.canStartPictureInPictureAutomaticallyFromInline = true
self.pictureInPictureController.startPictureInPicture()
}
}
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webConfiguration.allowsInlineMediaPlayback = true
webConfiguration.mediaTypesRequiringUserActionForPlayback = []
let script = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
webConfiguration.userContentController.addUserScript(script)
webConfiguration.userContentController.add(self, name: "iosListener")
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.scrollView.isScrollEnabled = false
webView.isOpaque = true
webView.scrollView.bounces = false
}
override func viewDidLoad() {
super.viewDidLoad()
webView.uiDelegate = self
webView.navigationDelegate = self
view = webView
let myRequest = URLRequest(url: URL(string: playerURL)!)
webView.load(myRequest)
}

You can get or set allowsPictureInPictureMediaPlayback for your wkwebviewconfiguration
WKWebViewConfiguration().allowsPictureInPictureMediaPlayback = true
A user can disable Picture in Picture’s automatic invocation in Settings > General > Picture in Picture > Start Pip Automatically. If you think you’ve set up everything correctly and find that your video does not enter PiP when you press the Home button, check this settings.

Related

WKWebView does not allow camera access in application

We are trying to make a conference call with multiple users, So by using Kurento server we have achieved this and it's working on safari browser. But when it comes to implementation in WebView / WKWebView. It does not even ask for permissions.
#IBOutlet weak var webViewContainer: UIView!
var webView: WKWebView!
override open func loadView() {
super.loadView()
let webConfiguration = WKWebViewConfiguration()
webConfiguration.ignoresViewportScaleLimits = true
webConfiguration.suppressesIncrementalRendering = true
webConfiguration.allowsInlineMediaPlayback = true
webConfiguration.allowsAirPlayForMediaPlayback = false
webConfiguration.allowsPictureInPictureMediaPlayback = true
webConfiguration.mediaTypesRequiringUserActionForPlayback = .all
webConfiguration.requiresUserActionForMediaPlayback = true
webView = WKWebView(frame: webViewContainer.frame, configuration: webConfiguration)
webView.uiDelegate = self
webView.navigationDelegate = self
webView.sizeToFit()
webView.backgroundColor = .black
webView.isOpaque = false
self.webViewContainer.addSubview(webView)
}
func webContentController()-> WKUserContentController {
let contentController = WKUserContentController()
let script = try! String(contentsOf: Bundle.main.url(forResource: "WebRTC", withExtension: "js")!, encoding: String.Encoding.utf8)
contentController.addUserScript(WKUserScript(source: script, injectionTime: WKUserScriptInjectionTime.atDocumentStart, forMainFrameOnly: true))
contentController.add(self, name: "callbackHandler")
return contentController
}
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL (string: urlStr) else { return
}
let myRequest = URLRequest(url: url)
self.webView.load(myRequest)
}
I even have tried this link in safariViewController, but it does not ask for camera permissions.
This
Did you follow the steps from the documentation?
The most important part is the NSCameraUsageDescription / NSMicrophoneUsageDescription must be present inside the info.plist file
This is a known limitation of WKWebView for now, see the chrome issue for details
It's a Bug, the WkWebView has limited support to the WebRTC
It's now working since IOS 14.3 version
But to get this working, you need to set properties requiresUserActionForMediaPlayback = false
allowsInlineMediaPlayback = true
I had to do this:
import AVFoundation
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AVCaptureDevice.requestAccess(for: .audio) { haveMicAccess in
print("Access to Microphone: \(haveMicAccess)")
}
return true
}
...
}
It still asks me each time I use the camera with getUserMedia() in the page script too. But without the above code getUserMedia is null. The above code triggers the request for access to the microphone so long as you provide a message in the info.plist for NSMicrophoneUsageDescription
In my case, I need camera open for account identifier. Setting config for webview below work for me:
webConfiguration.allowsInlineMediaPlayback = true
webConfiguration.mediaTypesRequiringUserActionForPlayback = []

WKWebView showing white screen, with native<->webview communication

Here are the functions and props inside my ViewController Class,
webViewContainer is a just a View. The page does render because it prints WEBVIEW MSG WORKING to console, but it does not appear where the View is, or it does but is just white. Removing the native-webview communication and making a direct reference from a WebView to the variable works as expected, but I need to retain the messaging functionality.
#IBOutlet weak var webViewContainer: UIView!
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let contentController = WKUserContentController()
contentController.add(self, name: "callback")
let config = WKWebViewConfiguration()
config.userContentController = contentController
print(webViewContainer.frame);
webView = WKWebView(frame: webViewContainer.frame, configuration: config);
webView.loadHTMLString("<html><body><script>try {webkit.messageHandlers.callback.postMessage('WEBVIEW MSG WORKING');} catch(err) {console.log('Can not reach native code');}</script><h1>WEBVIEW SHOWS</h1></body></html>", baseURL: URL(string: "http://localhost"));
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let response = message.body as? String else { return }
print(response)
}
You missed the line
self.webViewContainer.addSubview(webView)
Adding it to the end of viewDidLoad method should fix your issue.

Xcode WKWebView code to allow WebView to process popups

I am totally new to Xcode and the syntax is totally escaping me.
I have one thing left to do and then I've achieved what I need to within Xcode.
I need to insert a function that allows the WebView to process popups, but I have no idea where to begin inserting the code.
This is the current code:
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let webView = WKWebView()
let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")
let folderPath = Bundle.main.bundlePath
let baseUrl = URL(fileURLWithPath: folderPath, isDirectory: true)
do {
let htmlString = try NSString(contentsOfFile: htmlPath!, encoding: String.Encoding.utf8.rawValue)
webView.loadHTMLString(htmlString as String, baseURL: baseUrl)
} catch {
// catch error
}
webView.navigationDelegate = self
view = webView
}
}
How do I edit this in order to allow popups?
Please remember that I don't know how to add the solutions that I'm finding, so please show me where to insert the snippet as well.
Try this code, hope it helps!
class ViewController: UIViewController {
var webView: WKWebView!
var popupWebView: WKWebView?
var urlPath: String = "WEBSITE_URL"
override func viewDidLoad() {
super.viewDidLoad()
setupWebView()
loadWebView()
}
//MARK: Setting up webView
func setupWebView() {
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
preferences.javaScriptCanOpenWindowsAutomatically = true
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
webView = WKWebView(frame: view.bounds, configuration: configuration)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webView.uiDelegate = self
webView.navigationDelegate = self
view.addSubview(webView)
}
func loadWebView() {
if let url = URL(string: urlPath) {
let urlRequest = URLRequest(url: url)
webView.load(urlRequest)
}
}
}
extension ViewController: WKUIDelegate {
//MARK: Creating new webView for popup
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
popupWebView = WKWebView(frame: view.bounds, configuration: configuration)
popupWebView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
popupWebView!.navigationDelegate = self
popupWebView!.uiDelegate = self
view.addSubview(popupWebView!)
return popupWebView!
}
//MARK: To close popup
func webViewDidClose(_ webView: WKWebView) {
if webView == popupWebView {
popupWebView?.removeFromSuperview()
popupWebView = nil
}
}
}
}

iOS WKWebView Swift javascript enable

I'm trying to develop a simple app to browse my website. However my website contains some javascript and it doesn't successfully show my website.
In the past develop with android the same app and had to activate like this:
webSettings.setJavaScriptEnabled(true);
This currently my code I just have missing the option to enable javascript if somebody can help will really appreciate
import WebKit
class ViewController: UIViewController {
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://132.148.136.31:8082")
let urlRequest = URLRequest(url: url!)
webView.load(urlRequest)
}
}
Working with WKWebView is better to create it from code. And you can set javaScriptEnabled to configuration:
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
let webview = WKWebView(frame: .zero, configuration: configuration)
Update. This is WKWebView in view controller class:
import UIKit
import WebKit
class ViewController: UIViewController {
private var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
webView = WKWebView(frame: view.bounds, configuration: configuration)
view.addSubview(webView)
}
}
For those who are building for iOS 14 with Swift 5 and Xcode 12, it has changed to the following ...
webView.configuration.defaultWebpagePreferences.allowsContentJavaScript = true
You are using WKWebView storyboard view so you can directly access the configuration. So use preferences under your WKWebView configuration.
import WebKit
class ViewController: UIViewController {
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://132.148.136.31:8082")
let urlRequest = URLRequest(url: url!)
// enable JS
webView.configuration.preferences.javaScriptEnabled = true
webView.load(urlRequest)
}
}
The above answer is right you need to set this value as true to enable JavaScript.
webView.configuration.preferences.javaScriptEnabled = true
But if the JS scripts are from a non-HTTPS source then you need to allow "App Transport Security's Arbitrary Loads".
Here is how to fix that:
Navigate to Project Settings/Target/[your app's target]/Info.
Check whether there is a dictionary called App Transport Security Settings. If not, create it.
Inside it, create a boolean value called Allow Arbitrary Loads (if it's not there yet) and set it to YES.
IOS 15.5
add this
webView.configuration.defaultWebpagePreferences.allowsContentJavaScript = true
try this code :
override func viewDidLoad() {
super.viewDidLoad()
configureWebView()
currentWebView.load(requestURL)
}
private func configureWebView() {
webView.navigationDelegate = self
webView.uiDelegate = self
webView.allowsBackForwardNavigationGestures = true
/// javaScript 사용 여부
if #available(iOS 14, *) {
let preferences = WKWebpagePreferences()
preferences.allowsContentJavaScript = true
webView.configuration.defaultWebpagePreferences = preferences
}
else {
webView.configuration.preferences.javaScriptEnabled = true
/// user interaction없이 윈도우 창을 열 수 있는지 여부를 나타냄. iOS에서는 기본값이 false이다.
webView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true
}
}
func createNewWebView(_ config: WKWebViewConfiguration) -> WKWebView {
/// autoLayout 되어 있는 webView와 frame을 같게 한다.
let newWebView = WKWebView(frame: webView.frame,
configuration: config)
newWebView.navigationDelegate = self
newWebView.uiDelegate = self
newWebView.allowsBackForwardNavigationGestures = true
view.addSubview(newWebView)
popupWebViews.append(newWebView)
return newWebView
}

ScriptMessageHandler not always called on actual device, works fine on simulator

I use WKWebView and I want to be notified when website is fully loaded. The webView:didFinishNavigation method of WKNavigationDelegate is fired when document.readyState is either interactive or complete and I want to be sure that site was completely loaded. I came up with the solution which uses JavaScript injection. Here is my MWE:
import UIKit
import WebKit
class ViewController: UIViewController, WKScriptMessageHandler, WKNavigationDelegate {
var webView: WKWebView!
#IBOutlet weak var loadLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let contentController = WKUserContentController()
let scriptPath = NSBundle.mainBundle().pathForResource("script", ofType: "js")!
let scriptString = try! String(contentsOfFile: scriptPath)
let script = WKUserScript(source: scriptString, injectionTime: .AtDocumentStart, forMainFrameOnly: true)
contentController.addUserScript(script)
contentController.addScriptMessageHandler(self, name: "readyHandler")
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
webView = WKWebView(frame: CGRect.zero, configuration: configuration)
webView.navigationDelegate = self
loadLabel.text = nil
}
#IBAction func loadWebsite() {
webView.loadRequest(NSURLRequest(URL: NSURL(string: "http://stackoverflow.com")!))
loadLabel.text = "Loading..."
}
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
print("message received")
loadLabel.text = "Complete"
}
}
And this is the content of script.js file:
document.onreadystatechange = function () {
if(document.readyState === "complete"){
webkit.messageHandlers.readyHandler.postMessage("");
}
}
userContentController:didReceiveScriptMessage method is always called on iOS Simulator, but on the actual device (iPhone 6 in my case) it isn't called most of the times. Any idea what can be wrong about it or what's the other way of checking if website is completely loaded?
For some reason you need to add the webView to a visible view for this to work on the device. If you don't want the webView to be visible, add it and then set the hidden property to true.
For the code example above:
func viewDidLoad(){
...
webView.hidden = true
view.addSubview(webView)
}

Resources