iOS: Subclassing UIWebView URLRequest crash - ios

I'm subclassing UIWebView but when I trying to make a request it crash with this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is my implementation:
ViewController:
var web:MyWebView!
override func viewDidLoad() {
super.viewDidLoad()
self.webPage = MyWebView(frame: self.view.frame)
self.view.bringSubview(toFront: self.webPage!)
}
class MyWebView: UIWebView,UIWebViewDelegate {
let urlStr = "http://apple.com"
override init(frame: CGRect) {
super.init(frame: frame)
scalesPageToFit = true
scrollView.bounces = false
delegate = self
let urlRequest = URL(string: urlStr)
let request = URLRequest(url: urlRequest!)
self.loadRequest(request)
}
If check the value of urlRequest in the console:
po urlRequest
nil
Any of you knows what I'm doing wrong or why this urlRequest is set as nil?
I'll really appreciate your help.

This works for me fine
import UIKit
class MyWebView: UIWebView,UIWebViewDelegate {
let urlStr = "https://www.apple.com"
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
scalesPageToFit = true
scrollView.bounces = false
delegate = self
let urlRequest = URL(string: urlStr)
let request = URLRequest(url: urlRequest!)
self.loadRequest(request)
}
}
Then call it from a viewcontroller
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(_ animated: Bool) {
let webView = MyWebView.init(frame: self.view.frame)
self.view.addSubview(webView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Related

Calling a function from one ViewController to another

I have two functions that i would like to call in another ViewController. I don't want to write the same code in the other Viewcontroller. I have tried creating a ViewController object and call the two methods this but keep getting crashes. I have also tried extending the UIViewController but no luck. How can i go on about
import UIKit
import WebKit
class PrivacyPolicyController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func loadView() {
super.loadView()
createWebView()
}
override func viewDidLoad() {
super.viewDidLoad()
loadURL(url: "https://www.apple.com")
}
func loadWebView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
func loadURL(url: String) {
guard let myURL = URL(string: url) else { return }
let myRequest = URLRequest(url: myURL)
webView.load(myRequest)
}
}
I wanna call loadURL and loadWebView functions in this ViewController
import UIKit
import WebKit
class TermsAndConditionsController: UIViewController{
override func loadView() {
super.loadView()
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
The ViewController where actions are performed
import UIKit
class WelcomeViewController: UIViewController {
#IBAction func termsAndConditionsTapped(_ sender: UIButton) {
let termsAndConditions = TermsAndConditionsController()
self.navigationController?.pushViewController(termsAndConditions, animated: true)
}
#IBAction func privacyPolicyTapped(_ sender: UIButton) {
let privacyPolicy = PrivacyPolicyController()
self.navigationController?.pushViewController(privacyPolicy, animated: true)
}
}
As I wrote in a comment, use the same viewcontroller for both the Privacy Policy and T&C:
import UIKit
import WebKit
class MyWebViewController: UIViewController {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
}
func loadWebView() {
let webConfiguration = WKWebViewConfiguration()
self.webView = WKWebView(frame: .zero, configuration: webConfiguration)
self.view.addSubview(self.webView)
webView.translatesAutoresizingMaskIntoConstraints = false
webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
webView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
webView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
}
func loadURL(url: String) {
guard let myURL = URL(string: url) else { return }
let myRequest = URLRequest(url: myURL)
if (self.webView == nil) {
self.loadWebView()
}
self.webView.load(myRequest)
}
}
Then:
import UIKit
class MakeItHappenViewController: UIViewController {
#IBAction func termsAndConditionsTapped(_ sender: UIButton) {
let termsAndConditions = MyWebViewController()
self.navigationController?.pushViewController(termsAndConditions, animated: true)
termsAndConditions.loadURL("http://someurl.com")
}
#IBAction func privacyPolicyTapped(_ sender: UIButton) {
let privacyPolicy = MyWebViewController()
self.navigationController?.pushViewController(privacyPolicy, animated: true)
privacyPolicy.loadURL("http://someotherurl.com")
}
}
You can create Base class(Which inherits UIViewController) for that and make your both view controller inherit the base class. That way you can have both the methods in both classes and you can just make calls to them
Class BaseClass: UIViewController{
var webView: WKWebView
func loadWebView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
}
func loadURL(url: String) {
guard let myURL = URL(string: url) else { return }
let myRequest = URLRequest(url: myURL)
webView.load(myRequest)
}}
The base class now has all the required methods for creating adding and realoading url for web view. Now for view controller classes.
Class PrivacyPolicyController: BaseClass, WKUIDelegate {func methodWhereYouwantToCallWebviewMthods(){
self.loadWebView()
self.loadURL()
self.view = self.webView}}
Same for other class
class TermsAndConditionsController: BaseClass{func methodWhereYouwantToCallWebviewMthods(){
self.loadWebView()
self.loadURL()
self.view = self.webView}
override func loadView() {
super.loadView()
}
override func viewDidLoad() {
super.viewDidLoad()
}}

Swift4 - WkwebView warning of Could not signal service com.apple.WebKit

I have a log when I load youtube URL in WKWebView.
I have searched the same title of my question in stackOverFlow.
But it didn't work for me.
What's wrong with my code?
And I use swift4 & xcode9.2.
Thanks.
Transport also set true.
Warning like this:
Could not signal service com.apple.WebKit.Networking: 113: Could not find specified service
import UIKit
import WebKit
class DetailViewController: UIViewController {
var videoId: String = ""
var videoTitle: String = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
loadUI()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadUI() {
view.backgroundColor = UIColor.white
naviSetting()
webViewSetting()
}
func naviSetting() {
self.title = videoTitle
}
func webViewSetting() {
let webview = WKWebView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
let url = URL(string: "https://www.youtube.com/watch?v=\(self.videoId)")
let request = URLRequest(url: url!)
webview.load(request)
self.view.addSubview(webview)
}
}
I have tried to encode parameter and Its working fine for me. Please check below code.
var wkWebView = WKWebView()
override func viewDidLoad()
{
super.viewDidLoad()
self.automaticallyAdjustsScrollViewInsets = false
self.wkWebView.translatesAutoresizingMaskIntoConstraints = false
self.wkWebView.frame = CGRect.init(x: 0, y: 0, width: self.wkWebView.frame.size.width, height: self.wkWebView.frame.size.height)
self.view.addSubview(wkWebView)
loadUrl()
}
override func viewWillLayoutSubviews()
{
super.viewWillLayoutSubviews()
self.wkWebView.frame = CGRect.init(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
self.wkWebView.contentMode = .scaleAspectFit
}
func loadUrl()
{
let webConfiguration = WKWebViewConfiguration()
webConfiguration.allowsInlineMediaPlayback = true
let url_join = "v=1p38GWfCIhQ"
let myURL = URL(string: "https://www.youtube.com/watch?")
var myRequest = URLRequest(url: myURL!)
myRequest.httpMethod = "POST"
myRequest.httpBody = url_join.data(using: String.Encoding.utf8)
wkWebView.load(myRequest)
}
I hope It will also work for you. :)
For me this Code worked fine.
Notice, I Used http:// and not https://.
But you already have set the settings for http:// to work.
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
#IBOutlet weak var webView: WKWebView!
private let url = URL(string: "http://www.google.com")
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let request = URLRequest(url: url!)
webView = WKWebView(frame: self.view.frame)
self.view.addSubview(webView)
webView.navigationDelegate = self
webView.load(request)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

UIActivityIndicatorView not showing in webview

I have a UINavigationController that I am adding a UIViewController to. Inside my UIViewController I have a webview and also trying to set a UIActivityIndicatorView. However I can't seem to get the UIActivityIndicatorView to show up. Any help would be great!
class TestWebViewController: UIViewController {
var loadSpinner: UIActivityIndicatorView!
var webView: UIWebView!
var title: String?
init(title: String?) {
self.title = title
self.webView = UIWebView(frame: CGRect.zero)
super.init(nibName: nil, bundle: nil)
commonInit()
}
func commonInit() {
loadSpinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
loadSpinner.center = webView.center
view.addSubview(loadSpinner)
webView.delegate = self
view = webView
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
UINavigationController(rootViewController: testWebViewController)
Even when I try to add the loadSpinner to the webView it does not appear webView.addSubview(loadSpinner)
Result:
Code:
class ViewController: UIViewController {
var loadSpinner: UIActivityIndicatorView!
var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
self.webView = UIWebView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
loadSpinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
webView.addSubview(loadSpinner)
view = webView
loadSpinner.startAnimating()
}
override func viewDidLayoutSubviews() {
loadSpinner.center = webView.center
}
}
Your primary issue is that you first add the activity indicator to the view controller's view but two lines later you replace the view controller's view with the web view.
If you are going to replace the view controller's view you should do that in loadView. You should also setup other views in viewDidLoad, not in an init method.
The simplest change is to get rid of your commonInit method and put all of that code in viewDidLoad. Then in viewDidLoad, setup the web view first, then setup the activity indicator.
class TestWebViewController: UIViewController {
var loadSpinner: UIActivityIndicatorView!
var webView: UIWebView!
var title: String?
init(title: String?) {
self.title = title
super.init(nibName: nil, bundle: nil)
}
override func loadView() {
// don't call super
webView = UIWebView(frame: CGRect.zero)
webView.delegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
loadSpinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
loadSpinner.center = view.center
// Keep it in the center
loadSpinner.autoresizingMask = [.flexibleWidth, .flexibleLeftMargin, .flexibleRightMargin]
view.addSubview(loadSpinner)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
You also have an issue with title. UIViewController already has a title property. You should not add your own.
You need to refactor your code like this inside viewDidLoad
class TestWebViewController: UIViewController {
var loadSpinner: UIActivityIndicatorView!
var webView: UIWebView!
var title: String?
override func viewDidLoad()
{
super.viewDidLoad()
webView = UIWebView(frame:self.view.frame)
webView.delegate = self
self.view.addSubview(webview)
loadSpinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
self.view.addSubview(loadSpinner)
loadSpinner.center = self.view.center
loadSpinner.startAnimating()
}
}

Swipeable video filter in video playback

I am using the pod SCRecorder, and I am trying to playback a recorded video in a separate view controller. I have no issue recording the video however when I initialize the videoPlayer view controller I get a blank screen. I was just wondering if anyone who has used the pod has any experience with this and could provide insight? Here is the code below:
RecorderViewController:
#IBOutlet weak var previewView: UIView!
#IBOutlet weak var recordButton: UIButton!
let session = SCRecordSession()
let recorder = SCRecorder()
var exportSession = SCAssetExportSession()
var photoOutput: AVCaptureStillImageOutput?
var photo: UIImage?
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
override func viewDidLoad() {
super.viewDidLoad()
self.recorder.captureSessionPreset = SCRecorderTools.bestCaptureSessionPresetCompatibleWithAllDevices()
if (!recorder.startRunning()) {
debugPrint("Recorder error: ", recorder.error)
}
recorder.session = session
recorder.device = AVCaptureDevicePosition.Back
recorder.videoConfiguration.size = CGSizeMake(screenWidth, screenHeight)
recorder.delegate = self
}
func capturePhoto(image: UIImage) {
var connection = photoOutput!.connectionWithMediaType(AVMediaTypeVideo)
photoOutput!.captureStillImageAsynchronouslyFromConnection(photoOutput!.connectionWithMediaType(AVMediaTypeVideo)) {
(imageDataSampleBuffer: CMSampleBuffer!, error: NSError!) in
if error == nil {
let data:NSData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer)
let image:UIImage = UIImage( data: data)!
//add showPhoto call so that functions won't have to be called and can be called from one central place
// BY 8PM TONIGHT APP SHOULD BE FINISHED!!!!!!!!!!!!!!!
} else{
print("Error")
}
}
}
func showPhoto(photo: UIImage) {
self.photo = photo
self.performSegueWithIdentifier("Photo", sender: self)
}
#IBAction func recordButtonTapped(sender: UIButton) {
recorder.record()
if recorder.isRecording{
print("video is recording")
}
}
func showVideo() {
self.performSegueWithIdentifier("Video", sender: self)
}
#IBAction func stopTapped(sender: UIButton) {
print("video was paused")
recorder.pause()
self.showVideo()
}
override func viewWillLayoutSubviews() {
}
override func viewDidLayoutSubviews()
{
recorder.previewView = previewView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
PlayerController:
override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
// Custom initialization
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
var player : SCPlayer?
self.filterView.removeObserver(self, forKeyPath: "selectedFilter")
self.filterView = nil
self.player.pause()
player = nil
}
let player = SCPlayer()
let session = SCRecordSession()
#IBOutlet weak var filterView: SCSwipeableFilterView!
override func viewWillAppear(animated: Bool) {
player.setItemByAsset(session.assetRepresentingSegments())
let playerLayer = AVPlayerLayer(player: player)
filterView.frame = self.view.bounds
filterView.layer.addSublayer(playerLayer)
self.filterView.contentMode = .ScaleAspectFill
player.play()
}
override func viewDidLoad() {
super.viewDidLoad()
if NSProcessInfo.processInfo().activeProcessorCount > 1 {
self.filterView.contentMode = .ScaleAspectFill
var emptyFilter = SCFilter.emptyFilter()
emptyFilter.name = "#nofilter"
self.filterView.filters = [emptyFilter, SCFilter(CIFilterName:"CIPhotoEffectNoir"), SCFilter(CIFilterName:"CIVignetteEffect"), SCFilter(CIFilterName:"CIPhotoEffect") ]
self.player.SCImageView = self.filterView
self.filterView.addObserver(self, forKeyPath: "selectedFilter", options: .New, context: nil)
}
else {
var playerView = SCVideoPlayerView(player: player)
playerView.playerLayer!.videoGravity = "ResizeAspectFill"
playerView.frame = self.filterView.frame
playerView.autoresizingMask = self.filterView.autoresizingMask
self.filterView.superview!.insertSubview(playerView, aboveSubview: self.filterView)
self.filterView.removeFromSuperview()
}
player.loopEnabled = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}

WebView cannot scroll from top to bottom of the webpage

I have loaded a webpage to xcode WebView. But only the upper portion of the page is loaded. I can not scroll down to the bottom of the page. Same fact for pdf. Only upper 2 pages can be scrolled. What can i do ?Here is my code.
Thanks in advance.
import UIKit
class ViewController: UIViewController {
#IBOutlet var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
var URL = NSURL(string: "http://www.archetapp.com")
webView.loadRequest(NSURLRequest(URL: URL!))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//for pdf
import UIKit
class ViewController: UIViewController {
#IBOutlet var webViews: UIWebView!
var path = ""
override func viewDidLoad() {
super.viewDidLoad()
path = NSBundle.mainBundle().pathForResource("ibook", ofType: "pdf")!
let url = NSURL.fileURLWithPath(path)
webViews.scalesPageToFit = true
webViews.scrollView.scrollEnabled = true
webViews.userInteractionEnabled = true
self.webViews.loadRequest(NSURLRequest(URL: url!))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Try this!
import UIKit
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet var webView : UIWebView
override func viewDidLoad() {
super.viewDidLoad()
//load initial
path = NSBundle.mainBundle().pathForResource("ibook", ofType: "pdf")!
let url = NSURL.fileURLWithPath(path)
var req = NSURLRequest(URL : url)
webView.delegate = self // <---
webView.loadRequest(req)
}
func webViewDidStartLoad(webView : UIWebView) {
//UIApplication.sharedApplication().networkActivityIndicatorVisible = true
println("webViewDidStartLoad")
}
func webViewDidFinishLoad(webView : UIWebView) {
//UIApplication.sharedApplication().networkActivityIndicatorVisible = false
webViews.scalesPageToFit = true
webViews.scrollView.scrollEnabled = true
webViews.userInteractionEnabled = true
println("webViewDidFinishLoad")
}
}
I had the same issue just today and at least in my case it was caused by having the "Scale Pages to Fit" option checked in the WebView properties. I'm assuming Carlos' reply regarding the zoom scale fixes it anyways but I didn't need the option enabled in the first place so that was my easy fix.

Resources