I have a webView in a simple application which is under a UIImageView. I intend on displaying the UIImageView until webView loads some data and fires a method in the web view delegate. Webview loads the data, delegate method is called fine. However, I am having trouble manipulating other views like the UIImageView.
Here is my code;
class ViewController: UIViewController,UIWebViewDelegate {
//contains the imageView and activityWorkingIndicator
#IBOutlet weak var splash_view: UIView!
#IBOutlet var contentWebView: UIWebView!
//Contains the Webview and other views to be displayed after the splash his hidden
#IBOutlet weak var splash_view_not: UIView!
override func viewWillAppear(animated: Bool)
{
contentWebView?.scrollView.bounces = false;
contentWebView?.delegate = self
let url = NSURL(string: "http://localhost/something/");
let request = NSURLRequest(URL: url!);
contentWebView?.loadRequest(request);
}
....
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if let scheme = request.URL?.scheme {
if scheme == "myRequestScheme"{
let task : String = (request.URL?.host)!;
switch task {
case "systemReady":
print("checkpoint 1");//works fine
splash_view?.hidden = true;//no effect at all
splash_view_not?.hidden = false;//no effect at all
print("checkpoint 2");//works fine
break;
default:
break
}
}
}
return true;
}
}
What am I missing here?
It looks like you are using Auto Layout. If that is the case, using .hidden has more implications than you think. If you are calling it in a method after the view has already been loaded, you need to work with constraints instead of hidden. Some people create constraint outlets and set them to active to hide things. Some people set alphas and bring subviews to front to hide things, etc.
I believe this is the preferred way:
http://candycode.io/hiding-views-with-auto-layout/
And here is a stack overflow thread with A LOT of information on your best options to hide things with AutoLayout:
AutoLayout with hidden UIViews?
You should hide or show any control in delegate method on main thread.
Use like this
DispatchQueue.main.async {
splash_view_not?.hidden = false
}
Related
I want a transparent web page in swift, so I have tried the below code according to this answer. Still, I am not getting a transparent web page. nothing changes in webview colour.. may I know why??
where am I going wrong? please help me in below code.
Total code:
import UIKit
import WebKit
class WebviewViewController: UIViewController {
#IBOutlet weak var testWebView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "https://developer.apple.com/swift/") else { return }
let request = URLRequest(url: url)
testWebView.load(request)
// Do any additional setup after loading the view.
self.testWebView = WKWebView()
self.testWebView!.isOpaque = false
self.testWebView!.backgroundColor = UIColor.clear
self.testWebView!.scrollView.backgroundColor = UIColor.clear
}
}
Please help me with the code.
the code to make transparent background is as follow what you already added.
self.testWebView!.backgroundColor = UIColor.clear
now question is you added right code already then why you are not getting reliable output ..?
Also , if you try
self.testWebView!.alpha with any value, it will affect all of WebPages as WkWebView is a single view and changing it's alpha will also affect the components within...
it happened because the page you load in WebViewController has some HTML and CSS code, you make your WebViewController transparent but because of that HTML &CSS you can't see it's transparency as each webpage has it's own background color settings (which is merely impossible to change for each webpage)
I hope you will understand and it will help you ...:)
as I see, you use storyboard (#IBOutlet) and you can use Storyboard for setting your WKWebView:
And about code. This is enough for the result. You shouldn't set again self.testWebView = WKWebView(), because you use storyboard and you can set isOpaque with storyboard. As result:
class ViewController: UIViewController, WKNavigationDelegate {
#IBOutlet var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView.navigationDelegate = self
let url = URL(string: "https://developer.apple.com/swift/")!
webView.load(URLRequest(url: url))
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
let js = "(function() { document.body.style.background='transparent'; })();"
webView.evaluateJavaScript(js) { (_, error) in
print(error)
}
}
}
and evaluateJavaScript helped to add transparency for background:
Using swift3 with xcode8
Below is my viewconroller.swift
class ViewController: UIViewController {
#IBOutlet weak var YahooWebview: UIWebView!
#IBOutlet weak var activity: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
let YURL = URL(string: "http://www.yahoo.com")
let YURLRequest = URLRequest(url: YURL!)
YahooWebview.loadRequest(YURLRequest)
}
}
func webViewDidStartLoad(YahooWebview: UIWebView) {
activity.startAnimating()
}
func webViewDidFinishLoad(YahooWebview: UIWebView) {
print("show indicator")
activity.stopAnimating()
}
Why my indicator is not showing when webview is loading?
I can not even see string "show indicator" from my log in Xcode.
You need to set your class as the delegate of UIWebView as
YahooWebview.delegate = self
Check my answer to get details regarding delegates and delegation pattern.
Note from apple developer: In apps that run in iOS 8 and later, use the
WKWebView class instead of using UIWebView. Additionally, consider setting the WKPreferences property javaScriptEnabled to false if you render files that are not supposed to run JavaScript.
There can be two problems as mentioned in comments.
Both can be solved in your Stroyboard/xib file.
Your UIActivityIndicatorView is may be hidden behind UIWebView. Just change the position of the view so that it comes above in the view heirarchy.
You may not have set delegate property of UIWebview class as your ViewController. Right click on webview and check. This needs to be set as you are animating the activity indicator view inside delegate methods.
I want my several view controllers to have a player in the bottom. This player consists of 2 views: the player and a button which toggles it (can be hidden or expanded).
Now I use the code below in each view controller to add this player.
#IBOutlet weak var broadcastView: BroadcastView!
#IBOutlet weak var broadcastViewBottomConstraint: NSLayoutConstraint!
#IBOutlet weak var avatarImageView: UIImageView!
#IBAction func toggleBroadcastMode(_ sender: ToggleBroadcastButton) {
if sender.isExpanded {
broadcastViewBottomConstraint.hideBroadcastView()
} else {
broadcastViewBottomConstraint.expandBroadcastView()
}
animateBroadcastToggle()
sender.toggle()
broadcastView.toggleBroadcastView()
}
Is there a way not to duplicate the code over and over? Maybe I can create parent VC or View to do it? If so, then how?
I personally would subclass a UINavigationController and have it in there, that way you can navigate through the flow while the player stays looking good at the bottom, if you need a VC to interact with it then you can
if let nav = navigationController as? MyPlayerNavController {
nav.PlayThis()
}
you can have it change size and everything from there and you wont lose it during transitions and stuff like the music app when playing music.
Add it as a subview of the keyWindow. That way it will always stay in all the viewControllers.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var overlayViewFrame = UIScreen.main.bounds
overlayViewFrame.origin.y = overlayViewFrame.height - 200
overlayViewFrame.size.height = 200
let overlayView = UIView(frame:overlayViewFrame)
overlayView.backgroundColor = UIColor.cyan
UIApplication.shared.keyWindow?.addSubview(overlayView)
}
This code is a sample generic code. If you want certain VCs to not show this, keep this overlay view in a singleton, and hide in appropriate VCs.
Output screenshots:
I have a view which I set up as input accessory view for view controller the following way:
#IBOutlet private weak var bottomPane: UIView!
override func canBecomeFirstResponder() -> Bool {
return true
}
override var inputAccessoryView: UIView {
return bottomPane
}
Everything works just fine until I try to view YouTube video in fullscreen mode (video is loaded in UIWebView). When video enters fullscreen mode, keyboard and my input accessory view disappear (which is normal, I guess), but when I exit fullscreen mode, they do not appear. If I keep the reference to bottomPane weak, it becomes nil and application crashes, if I change it to strong, input accessory view remains hidden until the keyboard appears next time.
Can anybody explain what's going on and how to fix this?
Here's what's going on.
When user interacts with UIWebView, it becomes first responder and inputAccessoryView provided by view controller disappears (no idea why behavior in this case is different from, say, UITextField). Subclassing UIWebView and overriding inputAccessoryView property does not work (never gets called). So I block interaction with UIWebView until user loads video.
private func displayVideo(URL: String) {
if let video = Video(videoURL: URL) {
// load video in webView
webView.userInteractionEnabled = true
} else {
webView.userInteractionEnabled = false
}
}
When user loads video, the only way to detect that user has entered/exited fullscreen mode is to listen to UIWindowDidBecomeKeyNotification and UIWindowDidResignKeyNotification and detect when our window loses/gains key status:
//in view controller:
private func windowDidBecomeKey(notification: NSNotification!) {
let isCurrentWindow = (notification.object as! UIWindow) == view.window
if isCurrentWindow {
// this restores our inputAccessoryView
becomeFirstResponder()
}
}
private func windowDidResignKey(notification: NSNotification!) {
let isCurrentWindow = (notification.object as! UIWindow) == view.window
if isCurrentWindow {
// this hides our inputAccessoryView so that it does not obscure video
resignFirstResponder()
}
}
And, of course, since inputAccessoryView can be removed at some point, we should recreate it if needed:
//in view controller:
override var inputAccessoryView: UIView {
if view == nil {
// load view here
}
return view
}
I've been playing around with web views in swift this evening, but have run into a bit of an issue.
For some reason I'm not able to get the webViewDidStartLoad or webViewDidFinishLoad methods to fire.
In my storyboard, I have an outlet called webView linked to my UIWebView element.
Is anyone able to help me with what I am doing wrong?
This is my viewController.swift file:
import UIKit
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet var webView : UIWebView
var url = NSURL(string: "http://google.com")
override func viewDidLoad() {
super.viewDidLoad()
//load initial URL
var req = NSURLRequest(URL : url)
webView.loadRequest(req)
}
func webViewDidStartLoad(webView : UIWebView) {
//UIApplication.sharedApplication().networkActivityIndicatorVisible = true
println("AA")
}
func webViewDidFinishLoad(webView : UIWebView) {
//UIApplication.sharedApplication().networkActivityIndicatorVisible = false
println("BB")
}
}
Try this!
var req = NSURLRequest(URL: url)
webView.delegate = self
webView.loadRequest(req)
I experienced the same issue, even I did confirmed the UIWebViewDelete to self and implemented its methods.
//Document file url
var docUrl = NSURL(string: "https://www.google.co.in/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0ahUKEwjjwPSnoKfNAhXFRo8KHf6ACGYQFggbMAA&url=http%3A%2F%2Fwww.snee.com%2Fxml%2Fxslt%2Fsample.doc&usg=AFQjCNGG4FxPqcT8RXiIRHcLTu0yYDErdQ&sig2=ejeAlBgIZG5B6W-tS1VrQA&bvm=bv.124272578,d.c2I&cad=rja")
let req = NSURLRequest(URL: docUrl!)
webView.delegate = self
//here is the sole part
webView.scalesPageToFit = true
webView.contentMode = .ScaleAspectFit
webView.loadRequest(req)
above logic worked perfectly with test URl I got from quick google search.
But I when I replaced with mine. webViewDidFinishLoad never get called.
then How we solved?
On backed side we had to define content-type as document in headers. and it works like charm.
So please make sure on your server back-end side as well.
Here's my 2 cents battling with the same problem in SWIFT 3:
class HintViewController: UIViewController, UIWebViewDelegate
Declare the delegate methods this way (note the declaration of the arguments):
func webViewDidStartLoad(_ webView: UIWebView)
func webViewDidFinishLoad(_ webView: UIWebView)
Remember to set self to the webview's delegate property either in Interface Builder (Select the webview, drag from the delegate outlet to the webview from the Connections Inspector OR programmatically: self.webview.delegate = self)
As others noted, setting the delegate of UIWebView and conforming to the UIWebViewDelegate protocol is the best possible solution out there.
For other's who might make it here. I had put my delegate methods in a private extension which couldn't be accessed by the delegate caller. Once I changed the extension to internal the delegates started to get called properly.
There is my detailed solution for Swift 3:
1) in class declaration write the UIWebViewDelegate. For example:
class MyViewController: UIViewController, UIWebViewDelegate {
2) of course in storyboard make link to your UIViewController like this:
#IBOutlet weak var webView: UIWebView!
3) in the func viewDidLoad add one line:
self.webView.delegate = self
Nothing more. Special thinks to LinusGeffarth and LLIAJLbHOu for idea.