Is there a workaround to bring the method "present" in a UIimageView class? - swift4

I created a class for my Logo UIimageView which is on every ViewController in my app. Now I want to create a tapGesture to jump back to the Home ViewController when it is tapped like Logos on Homepages back to index. How can I get the present method to work in the class UIimageView?
import Foundation
import UIKit
class LogoImageView: UIImageView {
override func awakeFromNib() {
super.awakeFromNib()
self.isUserInteractionEnabled = true
let TapGesture = UITapGestureRecognizer(target: self, action: #selector(self.imageTapped))
self.addGestureRecognizer(TapGesture)
}
#objc func imageTapped() {
let mainStoryboad = UIStoryboard(name: "Main", bundle: Bundle.main)
guard let destinationViewController = mainStoryboad.instantiateViewController(withIdentifier: "ViewController") as? ViewController else {
return
}
present(destinationViewController, animated: true, completion: nil)
}
}

I´ve got the solution for my problem. I added an extension to check the current ViewController to use the present method. Now I´m able to add every UIimageview this class and it brings me back to the root viewcontroller.
import Foundation
import UIKit
class LogoImageView: UIImageView {
override func awakeFromNib() {
super.awakeFromNib()
self.isUserInteractionEnabled = true
let TapGesture = UITapGestureRecognizer(target: self, action: #selector(self
.imageTapped))
self.addGestureRecognizer(TapGesture)
}
#objc func imageTapped() {
let mainStoryboad = UIStoryboard(name: "Main", bundle: Bundle.main)
guard let destinationViewController = mainStoryboad.instantiateViewController(withIdentifier: "ViewController") as? ViewController else {
return
}
guard let currentViewController = UIApplication.shared.keyWindow?.topMostViewController() else {
return
}
destinationViewController.modalTransitionStyle = .flipHorizontal
currentViewController.present(destinationViewController, animated: true, completion: nil)
}
}
extension UIWindow {
func topMostViewController() -> UIViewController? {
guard let rootViewController = self.rootViewController else {
return nil
}
return topViewController(for: rootViewController)
}
func topViewController(for rootViewController: UIViewController?) -> UIViewController? {
guard let rootViewController = rootViewController else {
return nil
}
guard let presentedViewController = rootViewController.presentedViewController else {
return rootViewController
}
switch presentedViewController {
case is UINavigationController:
let navigationController = presentedViewController as! UINavigationController
return topViewController(for: navigationController.viewControllers.last)
case is UITabBarController:
let tabBarController = presentedViewController as! UITabBarController
return topViewController(for: tabBarController.selectedViewController)
default:
return topViewController(for: presentedViewController)
}
}
}

Related

screen freezes if using modalPresentationStyle = .overCurrentContext swift 5

I am presenting a navigation controller with modalPresentationStyle as overCurrentContext. After dismissing controller screen freezes.
I am presenting a FirstViewController with NAvigationController.
let firstVC = FirstViewController.controller()
let nvc = UINavigationController(rootViewController: firstVC)
nvc.modalPresentationStyle = .overCurrentContext
present(nvc, animated: true)
Then inside FirstViewController, I am passing navigationController to push SecondViewController
override func viewDidLoad() {
super.viewDidLoad()
guard let nav = navigationController else { return }
showSecondViewController(parentController: nav)
}
func showSecondViewController(parentController: UINavigationController) {
let secondVC = SecondViewController.controller()
parentController.pushViewController(secondVC, animated: true)
}
Now first I am popping SecondViewcontroller on click action from SecondViewController
navigationController?.popViewController(animated: animated)
Then with some call back I am dismissing FirstViewController and NavigationControoler (nvc)
self.controller?.dismiss(animated: true)
self.nvc?.dismiss(animated: true)
Now after dismissing as above I am facing screen freeze issue.
I need help to resolve this issue. Please help. Why screen is freezing.
Please let me know if I am missing anything here?
Thanks
Did you see any errors in the console? I'm not clear about "some call back" as you mentioned above. Can you elaborate?
I created a small project to replicate your issue. The approach below works fine in my case
ViewController is the root view controller
FirstViewController is the first controller presented on top of ViewController
SecondViewController is pushed from the first view controller after the event "ViewDidLoad" happens in FirstViewController
I also created 1 onViewControllerDissmied callback in each ViewControllers (especially FirstViewController and SecondViewController)
In ViewController - I created a touch up inside event as below
#objc func onButtonClicked() {
let firstVC = FirstViewController()
nvc = UINavigationController(rootViewController: firstVC)
guard let nvc = nvc else { return }
nvc.modalPresentationStyle = .overCurrentContext
present(nvc, animated: true)
mycontroller = firstVC
firstVC.onViewControllerDimissed = { [weak self] in
self?.mycontroller?.dismiss(animated: true)
self?.nvc?.dismiss(animated: true)
}
}
FirstViewController
class FirstViewController: UIViewController {
var label: UILabel = {
let button = UILabel()
button.translatesAutoresizingMaskIntoConstraints = false
button.text = "First View Controller"
button.textColor = .white
return button
}()
var onViewControllerDimissed: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .purple
guard let nav = navigationController else { return }
showSecondViewController(parentController: nav)
}
func showSecondViewController(parentController: UINavigationController) {
let secondVC = SecondViewController()
parentController.pushViewController(secondVC, animated: true)
secondVC.onViewControllerDimissed = { [weak self] in
self?.onViewControllerDimissed?()
}
}
}
SecondViewController
class SecondViewController: UIViewController {
var label: UILabel = {
let button = UILabel()
button.translatesAutoresizingMaskIntoConstraints = false
button.text = "Second View Controller"
return button
}()
var onViewControllerDimissed: (() -> Void)?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: view)
let pnt: CGPoint = CGPoint(x: position.x, y: position.y)
if (view.bounds.contains(pnt)) {
onScreenTouch()
}
}
}
func onScreenTouch() {
navigationController?.popViewController(animated: true)
onViewControllerDimissed?()
}
}

delegate to Storyboard created View Controller

i created two projects to learn how the delegate method is working..
one project created WITHOUT storyboard, just via code and my delegate is working just fine.
i built the other Project WITH storyboard, which means all ViewControllers are visible in the Interfacebuilder..
i am sure the issue lays in the definition of the ViewControllers in the code file:
let homeVC = HomeViewController()
Can someone please tell what is wrong here?
import UIKit
protocol HomeViewControllerDelegate: AnyObject {
func showMenu()
}
class HomeViewController: UIViewController {
var delegate: HomeViewControllerDelegate?
override func viewDidLoad() {
title = "App"
super.viewDidLoad()
configureNaviBar()
}
func configureNaviBar() {
// Left Bar Button Item
let burgerButton = UIImage(systemName: "line.horizontal.3")
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: burgerButton, style: .plain, target: self, action: #selector(showMenu))
}
#objc func showMenu(sender: AnyObject) {
print("show Menu (home)")
// homeDelegate is nil?
delegate!.showMenu() // throws an error!
}
}
import UIKit
class MainViewController: UIViewController {
let naviVC:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NaviVC") as! NaviVC
let menuVC:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SideMenuID") as! SideMenuViewController
let homeVC = HomeViewController()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
setupContainerView()
}
func setupContainerView() {
// menu
addChild(menuVC)
self.view.addSubview(menuVC.view)
menuVC.view.frame = CGRect(x: 0, y: 0, width: 200, height: 896)
menuVC.didMove(toParent: self)
// Home
homeVC.delegate = self
addChild(naviVC)
self.view.addSubview(naviVC.view)
naviVC.view.frame = self.view.bounds
naviVC.didMove(toParent: self)
}
}
extension MainViewController: HomeViewControllerDelegate {
func showMenu() {
// does not get called
print("did tap menu")
}
}
Error:
Debug_project/HomeViewController.swift:49: Fatal error: Unexpectedly found nil while unwrapping an Optional value
i am already searching for days now, and just can't find the solution for this...
please help me out guys
I found the solution!
Tanks to Phillip Mills and all others for helping me find this..
the solution is:
change
let homeVC = HomeViewController()
to
override func viewDidLoad() {
super.viewDidLoad()
let homeVC = naviVC.viewControllers.first as! HomeViewController // working: this is it!
}
class MainViewController: UIViewController {
let naviVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NaviVC") as! NaviVC
let menuVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SideMenuID") as! SideMenuViewController
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
setupContainerView()
}
func setupContainerView() {
// menu
addChild(menuVC)
self.view.addSubview(menuVC.view)
menuVC.view.frame = CGRect(x: 0, y: 0, width: 200, height: 896)
menuVC.didMove(toParent: self)
// Home
let homeVC = naviVC.viewControllers.first as! HomeViewController // working: this is it!
homeVC.delegate = self
addChild(naviVC)
self.view.addSubview(naviVC.view)
naviVC.view.frame = self.view.bounds
naviVC.didMove(toParent: self)
}
}
extension MainViewController: HomeViewControllerDelegate {
func showMenu() {
// does not get called
print("did tap menu")
}
}

passing value with protocol: ViewController to .xib file in swift

So i have a class "CalendarioViewController" with one protocol "DateSelectedDelegate".
Protocol:
protocol DateSelectedDelegate {
func dateSelected (date: String)}
I declare a variable protocol type:
var dateDelegate: DateSelectedDelegate? = nil
Into my class "CalendarioViewController" i have a #IBAction button:
#IBAction func gotoHotelVC(_ sender: Any) {
if dateDelegate != nil {
if fechaSelected.text != nil {
let data = fechaSelected.text
dateDelegate?.dateSelected(date: data!)
}
}
}
In my secondVC or "FechasViewController" i use the protocol define early, i call the CalendarioViewController with two button. The FechasViewController is a xib file:
#IBAction func fechaArribo(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc:CalendarioViewController = storyboard.instantiateViewController(withIdentifier: "calendario") as! CalendarioViewController
vc.dateDelegate = self
self.window!.rootViewController = vc
}
#IBAction func fechaSalida(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc:CalendarioViewController = storyboard.instantiateViewController(withIdentifier: "calendario") as! CalendarioViewController
vc.dateDelegate = self
self.window!.rootViewController = vc
}
my #IBOutlet:
#IBOutlet weak var fechaArriboLabel: UILabel!
Use protocol:
extension FechasViewController: DateSelectedDelegate {
func dateSelected(date: String) {
fechaArriboLabel.text = date
print(date)
print(fechaArriboLabel.text!)
}
}
The line print(date) or print(fechaArriboLabel.text!) is work just fine... but my xib label don't not change anything:
here my xib:
and this is my Storyboard:
Put here my entire code:
enter image description here
Please...help!

loading childViewControllers first time logging in

I have been struggling with an issue for loading child ViewControllers. The first time I log in it shows the containerView but without the childViewControllers loaded. When I close the app and re-open the app with the logged in state saved the childViews in the containerView are displayed. I know it has something to do with my window hierarchy and rootviewController. I have researched this a bunch but still am having issues solving. Thanks in advance!
// app delegate
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = MainNavigationController()
return true
}
// mainNavigationController - RootViewController
class MainNavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
let vc1 = TravelersFeedVC()
let vc2 = ProfileVC()
if isLoggedIn() {
// assume user is logged in
let homeController = HomeController()
homeController.firstViewController = vc1
homeController.secondViewController = vc2
viewControllers = [homeController]
} else {
perform(#selector(showLoginController), with: nil, afterDelay: 0.01)
}
}
fileprivate func isLoggedIn() -> Bool {
return UserDefaults.standard.isLoggedIn()
}
func showLoginController() {
let loginController = LoginController()
present(loginController, animated: true, completion: {
// perhaps do something here later
})
}
}
// log in function
func finishLoggingIn() {
let rootViewController = UIApplication.shared.keyWindow?.rootViewController
guard let mainNavigationController = rootViewController as? MainNavigationController else { return }
mainNavigationController.viewControllers = [HomeController()]
let vc1 = TravelersFeedVC()
let vc2 = ProfileVC()
let homeController = HomeController()
homeController.firstViewController = vc1
homeController.secondViewController = vc2
UserDefaults.standard.setIsLoggedIn(value: true)
dismiss(animated: true, completion: nil)
}
// HomeController
class HomeController: UIViewController, FBSDKLoginButtonDelegate {
// child view controllers to put inside content view
var firstViewController: TravelersFeedVC?
var secondViewController: ProfileVC?
private var activeViewController: UIViewController? {
didSet {
removeInactiveViewController(inactiveViewController: oldValue)
updateActiveViewController()
}
}
private func removeInactiveViewController(inactiveViewController: UIViewController?) {
if let inActiveVC = inactiveViewController {
// call before removing child view controller's view from hierarchy
inActiveVC.willMove(toParentViewController: nil)
inActiveVC.view.removeFromSuperview()
// call after removing child view controller's view from hierarchy
inActiveVC.removeFromParentViewController()
}
}
private func updateActiveViewController() {
if let activeVC = activeViewController {
// call before adding child view controller's view as subview
addChildViewController(activeVC)
activeVC.view.frame = contentView.bounds
contentView.addSubview(activeVC.view)
// call before adding child view controller's view as subview
activeVC.didMove(toParentViewController: self)
}
}
// UI elements
lazy var contentView: UIView = {
let tv = UIView()
tv.backgroundColor = UIColor.blue
tv.translatesAutoresizingMaskIntoConstraints = false
tv.layer.masksToBounds = true
return tv
}()
var segmentedController: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
activeViewController = firstViewController
checkIfUserIsLoggedIn()
view.addSubview(contentView)
setupProfileScreen()
let items = ["Travelers", "Me"]
segmentedController = UISegmentedControl(items: items)
navigationItem.titleView = segmentedController
segmentedController.tintColor = UIColor.black
segmentedController.selectedSegmentIndex = 0
// Add function to handle Value Changed events
segmentedController.addTarget(self, action: #selector(HomeController.segmentedValueChanged(_:)), for: .valueChanged)
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Sign Out", style: .plain, target: self, action: #selector(handleSignOut))
navigationItem.leftBarButtonItem?.tintColor = UIColor.black
}
// reference to collectionViewController
var travelersFeedVC: TravelersFeedVC!
func segmentedValueChanged(_ sender:UISegmentedControl!)
{
switch segmentedController.selectedSegmentIndex {
case 0:
activeViewController = firstViewController
case 1:
activeViewController = secondViewController
default: // Do nothing
break
}
}
In your finishLoggingIn() it looks like you are using 2 separate HomeController instances.
Something like this should fix it.
func finishLoggingIn() {
let rootViewController = UIApplication.shared.keyWindow?.rootViewController
guard let mainNavigationController = rootViewController as? MainNavigationController else { return }
// create it
let homeController = HomeController()
// use it
mainNavigationController.viewControllers = [homeController]
let vc1 = TravelersFeedVC()
let vc2 = ProfileVC()
// use it again
homeController.firstViewController = vc1
homeController.secondViewController = vc2
UserDefaults.standard.setIsLoggedIn(value: true)
dismiss(animated: true, completion: nil)
}

How do i load a viewcontroller from a xib file?

I tried to load a viewcontroller from a custom cell using delegates.But i get nil from the set delegate!
Here is a sample if anyone can help!
1. In Cell
protocol hotelFindDelegate{
func modalDidFinished(modalText: String)
}
class hotelFindCell: UITableViewCell {
var delegate:hotelFindDelegate?
#IBAction func findButton(sender: AnyObject) {
self.delegate!.modalDidFinished("HELLO")
print("Damn nothing works")
}
2. In Main View
class MainViewController:hotelFindDelegate {
let modalView = hotelFindCell()
override func viewDidLoad() {
super.viewDidLoad()
self.modalView?.delegate = self
}
func modalDidFinished(modalText: String){
let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("HotelListVC") as UIViewController
self.presentViewController(viewController, animated: false, completion: nil)
self.modalView.delegate = self
print(modalText)
}
To load view controller from XIB do the following steps.
let settingVC : SettingsViewController = SettingsViewController(nibName :"SettingsViewController",bundle : nil)
later on you can push the same view controller object like
self.navigationController?.pushViewController(settingsVC, animated: true)
NSBundle.mainBundle().loadNibNamed("viewController", owner: self, options: nil)