Show WebView ViewController through the xib View - ios

I have a xib view and a button in it but when i try to configure it to present a ViewController with a WebView it shows some errors.
the xib file:
class popUpView: UIView, UITextFieldDelegate {
#IBOutlet weak var payNowPopUp: UIButton!
#IBOutlet weak var priceTextFieldPopUp: UITextField!
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup(frame:CGRect(x: 0, y: 0, width: frame.width, height: frame.height))
}
func xibSetup(frame: CGRect){
let view = loadXib()
view.frame = frame
addSubview(view)
popUpViewInterface.layer.cornerRadius = 10
}
func loadXib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "popUpView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil).first as? UIView
return view!
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
priceTextFieldPopUp.resignFirstResponder()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
priceTextFieldPopUp.resignFirstResponder()
return true
}
#IBAction func payNowToWebView(_ sender: Any) {
guard let mainTabBarController = self.storyboard?.instantiateViewController(withIdentifier: "paymentWebView")
else {
return
}
mainTabBarController.modalPresentationStyle = .custom
self.present(mainTabBarController, animated: true, completion: nil)
}
}
Inside the #IBAction func payNowToWebView(_ sender: Any) { } I want to present a viewController when the button is clicked but it's showing some errors like: Value of type 'popUpView' has no member 'storyboard' and Value of type 'popUpView' has no member 'present'

Do it like this (Remember to replace 'Main' with your storyboard name):
#IBAction func payNowToWebView() {
let storyboard: UIStoryboard = UIStoryboard (name: "Main", bundle: nil)
let vc: paymentWebView = storyboard.instantiateViewControllerWithIdentifier("paymentWebView") as! PaymentWebView
let currentController = self.getRootVC()
currentController?.presentViewController(vc, animated: false, completion: nil)
}
func getRootVC() -> UIViewController? {
if let rootController = UIApplication.shared.keyWindow?.rootViewController {
var currentController: UIViewController! = rootController
while( currentController.presentedViewController != nil ) {
currentController = currentController.presentedViewController
}
return currentController
}
return nil
}

present is a method and storyboard is a property both of them inside a UIViewController instance not a UIView instance
You can replace self.storyboard with UIStoryboard(name:"YourName",nil)but for present you need a vc to show the needed one from it most commonly to have a delegate weak variable inside that view that references the vc where the view is inside or you may get the app's window root vc then vc.present(.....

Related

Delegate in UIView

I present modally UIViewController with workout. When user dismiss this VC I need to hold unfinished workout and present small UIView with size exactly the same as UITabBar in all others view controllers. This UIView has a button which should instantiate unfinished workout. The problem is that delegate doesn't work.
class BackToWorkoutButton: UIView {
weak var delegate: BackButtonWorkoutDelegate?
var routine: Routine!
init(frame: CGRect, routine: Routine) {
self.routine = routine
super.init(frame: frame)
customInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func customInit() {
let xibView = Bundle.main.loadNibNamed("BackToWorkoutButton", owner: self, options: nil)?.first as! UIView
xibView.frame = self.bounds
addSubview(xibView)
}
#IBAction func backButtonTapped(_ sender: UIButton) {
// THIS DELEGATE DOESN'T WORK
delegate?.backToWorkout()
}
}
class WorkoutCoordinator {
func instantiateUnfinishedWorkout() {
let window = UIApplication.shared.keyWindow!
guard let tabBar = navigationController.tabBarController else { return print("Tabbar nil") }
let height = tabBar.tabBar.frame.height
let child = NewWorkoutCoordinator(routine: temporaryRoutine, navigationController: navigationController, removeCoordinatorWith: removeChild, workoutViewBarProtocol: self)
let view2 = BackToWorkoutButton(frame: CGRect(x: 0, y: (tabBar.tabBar.frame.origin.y) - height, width: window.frame.width, height: height), coordinator: child, routine: routine)
view2.delegate = self
tabBar.view.addSubview(view2)
}
}

Adding UIView with data to the window

I need to add a UIView with size of tabbar but exactly above tabbar. This view allow user to come back to a started workout. My idea is holding data inside UIView and instantiate a View Controller with unfinished data when user clicks button. The problem is that when I want to instantiate VC my data in UIView become nil.
class BeforeRoutineClass {
// HERE I CREATE the UIView
func showWorkoutView(temporaryRoutine: Routine) {
guard let tabBar = navigationController.tabBarController else { return print("Tabbar is nil") }
let window = UIApplication.shared.keyWindow!
let height = tabBar.tabBar.frame.height
view2 = BackToWorkoutButton(frame: CGRect(x: 0, y: (tabBar.tabBar.frame.origin.y) - height, width: window.frame.width, height: height), routine: temporaryRoutine)
window.addSubview(view2!)
}
}
class BackToWorkoutButton: UIView {
var routine: Routine?
init(frame: CGRect, routine: Routine?, bobo: String?) {
self.routine = routine
super.init(frame: frame)
customInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func customInit() {
let xibView = Bundle.main.loadNibNamed("BackToWorkoutButton", owner: self, options: nil)?.first as! UIView
xibView.frame = self.bounds
addSubview(xibView)
}
#IBAction func backButtonTapped(_ sender: UIButton) {
// THERE IS NIL ALWAYS
guard let routine = routine else { return print("Routine is nil") }
let routineVC = RoutineFactory.startWorkoutScene(routine: routine)
let navBarOnModal = UINavigationController(rootViewController: routineVC)
navBarOnModal.modalPresentationStyle = .fullScreen
guard let nav = UIApplication.shared.windows.last?.rootViewController else { return print("there is no nav")}
nav.present(navBarOnModal, animated: true, completion: nil)
self.removeFromSuperview()
}
}

Display overlay view between ViewControllers

I am trying to display an overlay view on top of every ViewController after successfully logging in to show loading status of the app and load some data while this view is being displayed.
This view comes from a custom NIB file.
I display the OverlayLoadingView after a successful login request to the server, then I want to present a new ViewController on another Storyboard using:
self.present(vc, animated: true, completion: nil)
And finally hide the OverlayLoadingView in the next ViewController (This is why I handle it as a Singleton).
But when I present the next ViewController, the OverlayLoadingView disappears. Is there a way to display this loading view on top of everything while presenting a new view controller on the back at the same time?
Something like this:
LoginViewController -> Successfully logged in -> display OverlayView -> present MainViewController -> MainViewController presented below OverlayView -> Load initial data -> Hide OverlayView after initial data has been loaded.
#IBDesignable
public class OverlayLoadingView: UIView {
#IBOutlet weak var headerImageView: UIImageView!
#IBOutlet weak var detailTextLabel: UILabel!
class var sharedInstance: OverlayLoadingView {
struct Static {
static let instance: OverlayLoadingView = OverlayLoadingView(frame: UIScreen.main.bounds)
}
return Static.instance
}
// Custom view from the XIB file
fileprivate var view: UIView!
// Set header image directly by giving a value to headerImageURL variable from server response
/**
Initialiser method
*/
override init(frame: CGRect) {
super.init(frame: frame)
setupNib()
}
/**
Initialiser method
*/
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupNib()
}
fileprivate func loadNib() -> UIView? {
return Bundle.main.loadNibNamed("OverlayLoadingView", owner: self, options: nil)?.first as? UIView
}
func setupNib() {
if let overlayLoadingView = loadNib() {
self.view = overlayLoadingView
let screenWidth = Util.screenSize().width
let screenHeight = Util.screenSize().height
self.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
view.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
self.addSubview(view)
}
}
public func showOverlay() {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let window = appDelegate.window {
self.alpha = 1.0
window.addSubview(self)
activityIndicator.startAnimating()
}
}
public func hideOverlayView() {
activityIndicator.stopAnimating()
self.removeFromSuperview()
}
}
EDIT:
To display the overlay view I am using:
DispatchQueue.main.async {
let storyboard = UIStoryboard(name: "TabMenu", bundle: nil)
let vc = storyboard.instantiateInitialViewController()!
OverlayLoadingView.sharedInstance.showOverlay()
self.present(vc, animated: true, completion: {
// Dismiss current VC?
})
}
The views hierarchy is shown like the following image. The overlay view should be over everything.
If your requirement is to display the loading view above all controllers you can add the view to the window.
if let window = UIApplication.shared.keyWindow {
let overlay = OverlayLoadingView(frame:UIScreen.main.bounds)
overlay.tag = 1001
window.addSubView(overlay)
// remove it like this
window.viewWithTag(overlay.tag)?.removeFromSuperView()
}
By the way this is not a Singleton as I'm able to use init and create another instance mark the init as private. Make the class final to avoid reflection and replace class with static As class can be overided but static can't.

Remove nib from superview

I am simply trying to remove a view from it's superview when a respective button is pressed, but am failing to do so.
The view is a custom nib I created, called RequestLocationView
Called from viewDidLayoutSubviews:
func initBeaconServices() {
locationManager = CLLocationManager()
if locationManager.responds(to: #selector(CLLocationManager.requestWhenInUseAuthorization)) {
requestView = RequestLocationView(frame: UIScreen.main.bounds)
requestView.setupView()
requestView.alpha = 0
requestView.delegate = self
self.navigationController?.view.addSubview(requestView)
UIView.animate(withDuration: 0.5, delay : 1, options: .curveEaseOut, animations: {
self.requestView.alpha = 1
}, completion: nil)
}
locationManager.delegate = self
locationManager.pausesLocationUpdatesAutomatically = true
let uuid = UUID(uuidString: "869A6E2E-AE14-4CF5-8313-8D6976058A7A")
// Create the beacon region to search for with those values.
beaconRegion = CLBeaconRegion(proximityUUID: uuid!, identifier: "com.dejordan.Capiche")
}
Here's the code for the RequestView:
class RequestLocationView: UIView {
#IBOutlet weak var acceptButton: UIButton!
#IBOutlet weak var rejectButton: UIButton!
var lView: UIView!
weak var delegate: HandleLocationPermissionDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
private func xibSetup() {
lView = loadViewFromNib()
lView.frame = bounds
lView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
lView.translatesAutoresizingMaskIntoConstraints = true
addSubview(lView)
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
let nibView = nib.instantiate(withOwner: self, options: nil).first
return nibView as! UIView
}
func setupView() {
acceptButton.layer.cornerRadius = acceptButton.frame.height / 2
}
func fadeIn(completion: #escaping (Bool) -> Void) {
UIView.animate(withDuration: 1.3, animations: {
self.alpha = 1
}, completion: completion)
}
func fadeOut(completion: ((Bool) -> Void)?) {
UIView.animate(withDuration: 1.3, animations: {
self.alpha = 0
}, completion: completion)
}
#IBAction func acceptPressed(_ sender: UIButton) {
delegate?.allowPermissions()
}
#IBAction func rejecttPressed(_ sender: UIButton) {
delegate?.denyPermissions()
}
}
Here are the delegate methods in the parent viewController:
func allowPermissions() {
requestView.fadeOut {(finished) in
if finished {
self.locationManager.requestWhenInUseAuthorization()
self.requestView.removeFromSuperview()
}
}
}
func denyPermissions() {
requestView.fadeOut {(didFinish) in
self.requestView.removeFromSuperview()
}
}
And this is what happens when you click reject... You can see the background slightly fading but that's it, and the view stays where it is.
I've been scratching my head for a while on this one, and have scoured many Stack Overflow posts to no avail...
It's possible the CLLocationManager code is interacting in a not-so-friendly manner.
If moving the RequestLocationView initialization into viewDidLoad corrects the issue, that's likely the cause.
To get the proper radius on your acceptButton, try adding a layoutSubviews handler to your custom view:
override func layoutSubviews() {
acceptButton.layer.cornerRadius = acceptButton.bounds.height / 2
}

Swift4 ViewController not fired event in subView

I have 1 MainViewController from storyboard and 1 ModalUIView from xib.
In ModalUIView has present function for displaying modal and dismiss function for closing modal.
Step:
MainViewController -> OpenModal -> ModalUIView -> CloseModal
Here are my code:
UIViewUtil.swift
import Foundation
import UIKit
extension UIView {
// Load xib as the same name of CustomView that want to use xib
func loadXib() -> UIView{
let bundle = Bundle(for: type(of: self))
let nibName = type(of: self).description().components(separatedBy: ".").last!
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as! UIView
}
}
MainViewController.swift is subclass of UIViewController
#IBAction func guideButton(_ sender: Any) {
let modal = ModalUIView()
modal.present(targetView: self.view)
}
ModalUIView.swift is subclass of UIView
var view : UIView?
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
setup()
}
func setup() {
view = loadXib()
}
func present(targetView: UIView) {
view!.layer.cornerRadius = 10.0
view!.clipsToBounds = true
targetView.addSubview(view!)
// Set size
let popupWidth: CGFloat = targetView.frame.width - (targetView.frame.width * 0.04)
let popupHeight: CGFloat = targetView.frame.height - (targetView.frame.height * 0.08)
view!.frame = CGRect(x: targetView.frame.origin.x, y: targetView.frame.origin.y,
width: popupWidth, height: popupHeight)
view!.center = targetView.center
view!.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
view!.alpha = 0
UIView.animate(withDuration: 0.4){
self.view!.alpha = 1
self.view!.transform = CGAffineTransform.identity
}
}
#objc func dismiss(sender: UIButton!) {
print("dismiss")
}
My problem is mainViewController when I called present of modalUIView and then I tab on closeButton in modalUIView is not fired the action
I try with #IBAction but it not work:
#IBAction func CloseButtonAction(_ sender: UIButton) {
}
I also try with manual add action by programmatically but it not work too:
let closeButton: UIButton? = view?.viewWithTag(10) as! UIButton
closeButton!.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
Note:
I can see and tap on closeButton on modal.
I already add ModalUIView to xib File's Owner
OK, I have a solution now but not sure that is the best answer.
My solution is to add an action to closeButton of modalUIView via mainViewController not in modalUIView
If anyone have another best solution than this please suggest me.
You can also get an action in your viewcontroller following.
yourSubView.button.addTarget(self, action: #selector(buttonPress(sender:)), for: .touchUpInside)
and it will call your method.
func buttonPress(sender:UIButton){
print("Button pressed")
}

Resources