Swift - Opening web view links modally - ios

So I have a web view displaying a page and would like to open any links in a new view controller containing a web view modally (like twitter and Facebook do).
I have worked out how to get the url of the link clicked:
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
let youClicked = request.URL!
print(youClicked)
return true
}
But haven't managed to work out how I can pass this value into a web view on another page and present that view controller modally as a popover.
Any ideas?

Take a look at SFSafariViewController at Apple Developer Documentation
In order to implement the solution you have to import SafariServices and after that...
Declare an Safari View Controller
let destination: NSURL = NSURL(string: "http://desappstre.com")!
let safari: SFSafariViewController = SFSafariViewController(URL: destination)
Segue from your current view controller to the safari view controller declared on step 1
self.presentViewController(safari, animated: true, completion: nil)
If you need control over HTTP events you can use the SFSafariViewControllerDelegate in your destination view controller.

There isn't an out of the box component which can deal with your need. Instead you have to create your own component:
Create a new UIViewController and place a WKWebView in it (Can be archived by Interface Builder or Code - I usually prefer Interface Builder) . Furthermore create an outlet (e.g. called "webView") for the WKWebView
Create the controller - set the URL and present it
// Create the controller
let controller = storyboard.instantiateViewControllerWithIdentifier("MyCustomWebViewController")
//start loading the URL
controller.webView.loadRequest(request)
// present it
presentViewController(viewController, animated: true, completion: nil)

Related

Recreate iOS 13' share sheet modal in swift (not the share sheet itself, but the way it's presented)

is there a way to easily recreate the modal presentation style of ios 13' new share sheet? (At first, it's only presented halfway and you can swipe up to make it a "full" modal sheet) I can do it using a completely custom presentation and stuff but is there a "native" api for this behavior so that you don't have to use custom code?
Thanks!
Here's what I've tried. I've created a new ViewController class extending UIActivityViewController. And in the viewDidLoad function, I removed all the child views from the controller and added my viewController as a child to it. It seems to be working fine. Although, it is more of a work around it is still sufficient enough for the requirement it seems. Code snippet is as follows. Give it a try.
import UIKit
class CustomActivityViewController: UIActivityViewController {
private let controller: UIViewController!
required init(controller: UIViewController) {
self.controller = controller
super.init(activityItems: [], applicationActivities: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
let subViews = self.view.subviews
for view in subViews {
view.removeFromSuperview()
}
self.addChild(controller)
self.view.addSubview(controller.view)
}
}
Above is the CustomActivityViewController. And you can add your viewController into it as follows.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "YourViewController")
let activityViewController = CustomActivityViewController(controller: controller)
self.present(activityViewController, animated: true, completion: nil)
If you are interested in private API, this is how Apple does it:
There is a class named _UISheetDetent. With this class, you can either create system defined "detents"—medium and large—or provide your own block-based logic (input param is the presentation controller's container view, and the return value is a double—the percent to open the sheet).
You create an array of these "detents" and provide them to the sheet presentation controller using the _setDetents: method (or setValue:forKey:). To replicate the share sheet behavior, you need an array with two "detents": medium and large.
There is also the _indexOfLastUndimmedDetent property, which controls which "detent" starts the dimming process.
I'm not sure why Apple hasn't exposed this as public API. It is concise, simple and works well.
You should probably not use this API, but if you decide to use it, it should be very easy to hide it. In any case, make sure to open a Feedback with Apple to expose this API in a future version of the SDK.

Ending Webview and going back to VIew Controller when URL changes

I have a edit photo php script that i want to load in my iOS app. I am calling it through WebView "www.example/com/myTool". When the edit task is complete the webView is moving to homepage of the website. I want when the Edit task is complete then instead of redirecting url to www.example.com/home the webView closes and it moves back to View Controller.
What i can think of solution is - when the URL of webView changes then the webView should close and move back to view controller .. But i don't know how to apply it in code.
import UIKit
class AddPostVC: UIViewController {
#IBOutlet weak var addPost: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
addPost.loadRequest(URLRequest(url: URL(string: "https://new.example.com/pentool")!))
}
}
You need to implement UIWebViewDelegate methods.
Before loading a URL, UIWebView will call, (webView: shouldStartLoadWith request) delegate method.
Compare the URL in request property with your home page URL. If yes, close the web view.
Hope this helps :)
You could do this
NSString *currentURL = [webView stringByEvaluatingJavaScriptFromString:#"window.location"];
This will get you the url of the current page. Or this
currentURL = currentWebView.request.URL.absoluteString
Credit to https://stackoverflow.com/questions/2491410/get-current-url-of-uiwebview/3654403#3654403

Custom NavigationBar in swift

I am very much new to swift and ios so i need my doubts to be cleared for the below concept
I have created a view with a button having background image of back arrow and on button action i am going back to the previous controller which is the Login, is it safe to do it this way instead of using NavigationController??
#IBAction func onBackPressed(sender: AnyObject) {
let aaa : Login =
self.storyboard?.instantiateViewControllerWithIdentifier("Login")
as! Login
self.presentViewController(aaa, animated: true, completion: nil)
}
Thanks
Navigation controller is best when you want to push to next View Controller.
(i.e) Navigation Controller ->View Controller ->View Controller[for Push].
So my suggestion is if you want to come back to previous view controller use Navigation Controller
When you want to present a new View Controller that time no need.

Overwrite ios back button

I have an app that displays a webView of a web page. The web consists of a main page with a lot of subpages. When I am on the main page I want the navigation Back button to to keep the default behaviour of going back to the previous view controller. But when the web is on a subpage I would like the navigation Back button to take the web to the main web page.
So I need to catch when the user presses the back button and if he is on a web subpage take him to the web main page and prevent the webView from closing. I was thinking that I can catch the back button using viewWillDisappear(), but how can I prevent the view from closing?
Similar to KickimusButticus' answer:
If you want to more easily do it using the storyboard and such, you could hook up your back button to an IBAction like so:
#IBAction func back() {
}
And have internal testing for whether or not the user is in a sub-page of the webview. If they are, you can mess with the webview. If they are on the home page, you could simply use the back button to segue to your main view controller or use the self.dismissViewControllerAnimated(true, completion: nil) to dismiss that view controller.
Check this post out:
Execute action when back bar button of UINavigationController is pressed
Something like this ought to do, in your view controller code:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.hidesBackButton = true
let customBackButtonItem = UIBarButtonItem(title: "Back", style: .Bordered, target: self, action:#selector(customBackMethod(_:)))
self.navigationItem.leftBarButtonItem = customBackButtonItem
}
// #objc is so we can use #selector() up above
#objc func customBackMethod(sender: UIBarButtonItem) {
if webView.canGoBack {
webView.goBack()
}
}
Note that you may have to use UIBarButtonItem(image:, style:, target:, action:) if you want a 'back' image, or UIBarButtonItem(customView:). See this question and answer for more details: https://stackoverflow.com/a/36180934/892990

Accessing extensionContext from a presented view controller

I've tried creating a custom view controller for a share extension.
A confusing situation happens when I present another view controller on top of the initial view controller that was set on the MainInterface.storyboard. This presented view controller is embedded in a navigation controller (it's the root view controller of it).
I did a check on the presentingViewController
(lldb) po [self presentingViewController]
<_UIViewServiceViewControllerOperator: 0x7a978000>
(lldb) po [[self presentingViewController] extensionContext]
nil
So, the extension context is nil at this point. I could access the extensionContext by passing it around from the presentingViewController to the presentedViewController.
But, I found this behavior is a bit strange. Is the app extension designed to only be accessed from one level of view controller hierarchy?
If you're going to use more than a single view controller in your extension storyboard, you'll have to pass a reference to the extensionContext of the original view controller to the view controller that will ultimately be responsible for completing the extension's request. In the initial view controller:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destination = segue.destinationViewController as! FinalViewController
destination.originalExtensionContext = self.extensionContext
}
And in your final view controller:
#IBAction func dismissController(sender: UIButton!) {
dismissViewControllerAnimated(true) { () -> Void in
self.originalExtensionContext.completeRequestReturningItems(self.originalExtensionContext.inputItems, completionHandler: nil)
}
Note that you have to create a uniquely named property for the original extension context, since extensionContext already exists as a property name on the superclass UIViewController. You can't pass the existing extensionContext to the UIViewController's property extensionContext as it is a read-only attribute.
The view controller being presented by a view controller should have no problem using the parent's extension. Taking a look at the documentation:
The view controller can check this property to see if it participates in an extension request. If no extension context is set for the current view controller, the system walks up the view controller hierarchy to find a parent view controller that has a non nil extensionContext value.
Therefore, if you can be certain of the fact that your root view controller does indeed have an extensionContext, any view controller presented by this view controller should have access to it, simply through it's own extensionContext property.
Note: If this is not the behaviour you a re observing, this may be a bug with the SDK, and I would recommend filing a radar.
While it's not the best approach for clean code and architecture, it's quite handy:
In root extension controller where extensionContext exists:
final class ShareRootViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSExtensionContext.shared = self.extensionContext
}
}
extension NSExtensionContext {
fileprivate(set) static var shared: NSExtensionContext!
}
In any other view controller:
let context = NSExtensionContext.shared

Resources