I have ViewController1 that goes to ViewModel and then to Coordinator to present ViewController2.
The problem is: I need to know when VC2 was dismissed on VC1.
What I need to do: When VC2 is dismissed, I need to reload my table from VC1.
I can not use Delegate since I cant communicate between then (because of Coordinator).
Any help please?
Adding some code: My Coordinator:
public class Coordinator: CoordinatorProtocol {
public func openVC1() {
let viewModel = ViewModel1(coordinator: self)
guard let VC1 = ViewControllerOne.instantiate(storyboard: storyboard, viewModel: viewModel) else {
return
}
navigationController?.pushViewController(VC1, animated: true)
}
public func openVC2() {
let viewModel = ViewModel2()
guard let alertPriceDeleteVC = ViewControllerTwo.instantiate(storyboard: storyboard, viewModel: viewModel) else {
return
}
let nav = UINavigationController(rootViewController: VC2)
navigationController?.present(nav, animated: true, completion: nil)
}
CoordinatorProtocol:
public protocol CoordinatorProtocol {
func openVC1()
func openVC2()
}
My ViewModel1 calling VC2 through coordinatorDelegate:
func openVC2() {
coordinator.openVC2()
}
What I do when I finish ViewController2 and send user back do VC1:
navigationController?.dismiss(animated: true, completion: nil)
You need to to assign delegate value from prepare. Or you can assign delegate with initialize RedScreenVC(self) from your ViewController if u don't want to use storyboard/xib.
import UIKit
class ViewController: UIViewController, NavDelegate {
func navigate(text: String, isShown: Bool) {
print("text: \(text) isShown: \(isShown)")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "RedScreenVC") {
let redScreenVC = segue.destination as? RedScreenVC
redScreenVC?.delegate = self
}
}
#IBAction func nextPageButtonEventLustener(_ sender: Any) {
performSegue(withIdentifier: "RedScreenVC", sender: sender)
}
}
import UIKit
protocol NavDelegate {
func navigate(text: String, isShown: Bool)
}
class RedScreenVC: UIViewController {
weak var delegate: NavDelegate?
var redView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height))
var navigateButton: UIButton = {
let button = UIButton(frame: CGRect(x: 200, y: 350, width: 150, height: 50))
button.setTitle("Navigate", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button.backgroundColor = .blue
return button
}()
#objc func buttonAction(){
if self.redView.backgroundColor == .gray {
self.redView.backgroundColor = .systemPink
}
self.delegate.navigate(text:"", isShown: true)
}
override func viewDidLoad() {
navigateButton.layer.cornerRadius = 25
redView.backgroundColor = UIColor.gray
delegate.navigate(text: "Navigation Success", isShown: true)
view.addSubview(redView)
view.addSubview(navigateButton)
}
}
If you do not want to use storyboard.
let redScreenVC = RedScreenVC()
redScreenVC.delegate = self
class RedScreenVC: UIViewController {
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
init() {
super.init(nibName: nil, bundle: nil)
self.initialize()
}
func initialize() {
self.view.backgroundColor=CustomColor.PAGE_BACKGROUND_COLOR_1
//From here you need to create your email and password textfield
}
}
Related
I have a simple push segue from one view controller to another. I want both segues (original and unwind to be unanimated).
In the attached playground, specifying false for the segue does, indeed remove the PUSH animation, but not the UNWIND animation.
Is there a way to remove the implicit animation in the UNWIND segue?
import UIKit
import PlaygroundSupport
class SourceViewController : UIViewController {
#objc func goDestination(_: Any) {
navigationController?.pushViewController(DestinationViewController(), animated: false)
}
override func loadView() {
view = UIView()
navigationItem.title = "SOURCE"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(goDestination(_:)))
}
}
class DestinationViewController : UIViewController {
override func loadView() {
view = UIView()
view.backgroundColor = .yellow
navigationItem.title = "DESTINATION"
}
}
PlaygroundPage.current.liveView = UINavigationController(rootViewController: SourceViewController())
Those animations are fully customizable from the UINavigationControllerDelegate, check out the first "Supporting Custom Transition Animations" method
https://developer.apple.com/documentation/uikit/uinavigationcontrollerdelegate
Some details here https://www.youtube.com/watch?v=jWckfDNUJVY
at 23 minutes.
I'm greenchecking #glotcha's answer. It led me to this Medium post, which led me to this solution (I really wanted a fade transition):
import UIKit
import PlaygroundSupport
class TransitioningAnimatorInOut: NSObject, UIViewControllerAnimatedTransitioning {
var presenting: Bool = false
func transitionDuration(using: UIViewControllerContextTransitioning?) -> TimeInterval { 0.75 }
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromView = transitionContext.view(forKey: .from),
let toView = transitionContext.view(forKey: .to) else { return }
let container = transitionContext.containerView
if presenting {
container.addSubview(toView)
toView.alpha = 0.0
} else {
container.insertSubview(toView, belowSubview: fromView)
}
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
if self.presenting {
toView.alpha = 1.0
} else {
fromView.alpha = 0.0
}
}) { _ in
let success = !transitionContext.transitionWasCancelled
if !success {
toView.removeFromSuperview()
}
transitionContext.completeTransition(success)
}
}
init(presenting inPresenting: Bool) { presenting = inPresenting }
}
extension UINavigationController: UINavigationControllerDelegate {
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == .push {
return TransitioningAnimatorInOut(presenting: true)
} else {
return TransitioningAnimatorInOut(presenting: false)
}
}
}
class SourceViewController : UIViewController {
#objc func goDestination(_: Any) {
navigationController?.pushViewController(DestinationViewController(), animated: true)
}
override func loadView() {
view = UIView()
view?.backgroundColor = .yellow
navigationItem.title = "SOURCE"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(goDestination(_:)))
}
}
class DestinationViewController : UIViewController {
override func loadView() {
view = UIView()
view?.backgroundColor = .red
navigationItem.title = "DESTINATION"
}
}
PlaygroundPage.current.liveView = UINavigationController(rootViewController: SourceViewController())
Title is not showing in Simulator
import UIKit
class WelcomeSpotifyViewController: UIViewController {
private let signInButton: UIButton = {
let button = UIButton()
button.backgroundColor = .white
button.setTitle("Sign In with Spotify", for: .normal)
button.setTitleColor(.blue, for: .normal)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
title = "Spotify"
view.backgroundColor = .systemGreen
view.addSubview(signInButton)
signInButton.addTarget(self, action: #selector(didTapSignIn), for: .touchUpInside)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
signInButton.frame = CGRect(x:20, y: view.height-50-view.safeAreaInsets.bottom, width: view.width-40, height: 50)
}
#objc func didTapSignIn() {
let vc = SpotifyAuthViewController()
vc.navigationItem.largeTitleDisplayMode = .never
navigationController?.pushViewController(vc, animated: false)
}
}
When I launch the simulator, the title does not show. And when I click the button "Sign In with Spotify," I do not transfer to the SpotifyAuthViewController.
Here is the code for the SpotifyAuthViewController
import UIKit
import WebKit
class SpotifyAuthViewController: UIViewController, WKNavigationDelegate {
private let webView: WKWebView = {
let prefs = WKWebpagePreferences()
prefs.allowsContentJavaScript = true
let config = WKWebViewConfiguration()
config.defaultWebpagePreferences = prefs
let webView = WKWebView(frame: .zero, configuration: config)
return webView
}()
override func viewDidLoad() {
super.viewDidLoad()
title = "Sign In"
view.backgroundColor = .systemBackground
webView.navigationDelegate = self
view.addSubview(webView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
webView.frame = view.bounds
}
}
After the user is registered, I want them to be directed to a standard login/register page where they create a username, register with email, and create a password.
import UIKit
class LoginViewController: UIViewController {
private let usernameEmailField: UITextField = {
return UITextField()
}()
private let passwordField: UITextField = {
let field = UITextField()
field.isSecureTextEntry = true
return field
}()
private let loginButton: UIButton = {
return UIButton()
}()
private let createAccountButton: UIButton = {
return UIButton()
}()
private let headerView: UIView = {
return UIView()
}()
override func viewDidLoad() {
super.viewDidLoad()
addSubviews()
view.backgroundColor = .systemBackground
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
handleNotAuthenticated()
//Check auth status
}
private func handleNotAuthenticated() {
if SpotifyAuthManager.shared.isSignedIn == false {
let loginVC = WelcomeSpotifyViewController()
loginVC.modalPresentationStyle = .overCurrentContext
present(loginVC, animated: false)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
//assign frames
}
private func addSubviews() {
view.addSubview(usernameEmailField)
view.addSubview(createAccountButton)
view.addSubview(passwordField)
view.addSubview(loginButton)
view.addSubview(headerView)
}
#objc private func didTabLoginButton(){}
#objc private func didTapCreateAccountButton(){}
}
If the user is not connected with Spotify, then they are redirected to the WelcomeSpotifyViewController. Once they enter their username and pass, they are taken to the home page.
import UIKit
import FirebaseAuth
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
handleNotAuthenticated()
//Check auth status
}
private func handleNotAuthenticated() {
if Auth.auth().currentUser == nil {
let loginVC = LoginViewController()
loginVC.modalPresentationStyle = .fullScreen
present(loginVC, animated: false)
}
}
}
I have been tinkering with this for days now. I can't seem to figure out the issue. Any help would be really appreciated.
I think your problem is that your WelcomeSpotifyViewController don't have navigaiton controller. You should debug and check value of navigation controller
I have two screens. The first one (firstViewController) has a mapView with a UITapGestureRecognizer. When the user taps the screen, an annotations is added to the map and the second screen (secondViewController) is presented.
When the user dismisses the secondViewController and comes back to the first one, the annotation should be removed. I know I have to use delegation, but I just can't make it to work.
This is the code I have now:
class firstViewController: UIViewController, AnnotationDelegate {
let mapView = MKMapView()
var temporaryPinArray = [MKPointAnnotation]()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mapView
let gesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
mapView.addGestureRecognizer(gesture)
secondVC.annotationDelegate = self
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
mapView.frame = view.bounds
}
#objc func handleTap(_ gestureReconizer: UILongPressGestureRecognizer) {
let location = gestureReconizer.location(in: mapView)
let coordinates = mapView.convert(location, toCoordinateFrom: mapView)
mapView.removeAnnotations(mapView.annotations)
let pin = MKPointAnnotation()
pin.coordinate = coordinates
temporaryPinArray.removeAll()
temporaryPinArray.append(pin)
mapView.addAnnotations(temporaryPinArray)
// Present secondViewController
let secondVC = SecondViewController()
panel.set(contentViewController: secondVC)
panel.addPanel(toParent: self)
}
func didRemoveAnnotation(annotation: MKPointAnnotation) {
mapView.removeAnnotation(annotation)
}
}
Second View Controller
protocol AnnotationDelegate {
func didRemoveAnnotation(annotation: [MKPointAnnotation])
}
class SecondViewController: UIViewController {
var annotationDelegate: AnnotationDelegate!
let mainVC = firstViewController()
let closeButton: UIButton = {
let button = UIButton()
button.backgroundColor = .grey
button.layer.cornerRadius = 15
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(closeButton)
closeButton.addTarget(self, action: #selector(dismissPanel), for: .touchUpInside)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
closeButton.frame = CGRect(x: view.frame.width-50, y: 10, width: 30, height: 30)
}
#objc func dismissPanel() {
self.dismiss(animated: true, completion: nil)
annotationDelegate.didRemoveAnnotation(annotation: mainVC.temporaryPinArray)
}
}
Thank you so much for your help!
You created a new instance of firstViewController inside SecondViewController. This instance is unrelated to the actual first one:
let mainVC = firstViewController()
This means that temporaryPinArray is different as well. So instead of passing in this unrelated array...
#objc func dismissPanel() {
self.dismiss(animated: true, completion: nil)
annotationDelegate.didRemoveAnnotation(annotation: mainVC.temporaryPinArray)
}
Just change the function to take no parameters instead:
protocol AnnotationDelegate {
func didRemoveAnnotation() /// no parameters
}
#objc func dismissPanel() {
self.dismiss(animated: true, completion: nil)
annotationDelegate.didRemoveAnnotation() /// no parameters
}
And inside firstViewController's didRemoveAnnotation, reference the actual temporaryPinArray.
func didRemoveAnnotation() {
mapView.removeAnnotations(temporaryPinArray) /// the current array
}
right now i'm experimenting with SceneKit DebugOptions.
i'm trying to update/ show Scenekits Debug Options in real time, using switch controllers from a Popover ViewController.
i've tried many things, like UserDefaults, Delegation and Protocols, but stil i wasn't able to see the result in real time, every time i have to kill the app en relaunch it to see the results.
so, i would be greatfull if someone would have an answer to my question :D
extension i added to my MainVC
extension ViewController: UIPopoverPresentationControllerDelegate, DebugOptions {
func wireFrameEnabled(enabled: Bool) {
if enabled == true {
print(enabled)
}
}
func showCameraEnabled(enabled: Bool) {
}
func showAllDebugOptions(enabled: Bool) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let popoverController = segue.destination.popoverPresentationController, let button = sender as? UIButton else { return }
popoverController.delegate = self
popoverController.sourceRect = button.bounds
let debugMenuVC = popoverController.presentedViewController as! DebugMenuVC
debugMenuVC.delegate? = self
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
}
Protocol
protocol DebugOptions {
func wireFrameEnabled(enabled: Bool)
func showCameraEnabled(enabled: Bool)
func showAllDebugOptions(enabled: Bool)
}
DebugMenuVC
class DebugMenuVC: UIViewController {
#IBOutlet weak var bgView: UIView!
#IBOutlet weak var showWireFrameSwitch: UISwitch!
#IBOutlet weak var showCameraSwitch: UISwitch!
#IBOutlet weak var showAllSwitch: UISwitch!
var delegate: DebugOptions?
override func viewWillLayoutSubviews() {
preferredContentSize = CGSize(width: 150, height: 300)
}
override func viewDidLoad() {
super.viewDidLoad()
buttonCheck()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
#IBAction func aSwitchBtnWasPressed( _ sender: UISwitch ) {
if (sender.tag == 0) && (sender.isOn == true) {
userDefaults.set(true, forKey: SHOW_WIRE_FRAME)
delegate?.wireFrameEnabled(enabled: true)
} else if (sender.tag == 0) && (sender.isOn == false) {
userDefaults.set(false, forKey: SHOW_WIRE_FRAME)
delegate?.wireFrameEnabled(enabled: false)
}
}
func buttonCheck() {
if userDefaults.bool(forKey: SHOW_WIRE_FRAME) == true{
showWireFrameSwitch.isOn = true
} else {
showWireFrameSwitch.isOn = false
}
}
}
in debubMenuVC.delegate shouldn't be an optional. thats the reason the delegation method always failed :D
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let popoverController = segue.destination.popoverPresentationController, let button = sender as? UIButton else { return }
popoverController.delegate = self
popoverController.sourceRect = button.bounds
let debugMenuVC = popoverController.presentedViewController as! DebugMenuVC
debugMenuVC.delegate? = self
}
I created a pop up, but nothing is showing up, I added UIViewControllers as the pop ups. Why is it not working? http://puu.sh/hreL9/75081b2a90.png
class interestViewController: UIViewController, UIPopoverPresentationControllerDelegate {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "popoverSegue" {
let interestViewController = segue.destinationViewController as! UIViewController
interestViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
interestViewController.popoverPresentationController!.delegate = self
}
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
I believe you don't need a segue in this case. You can use the example I listed below. It shows a Popover from the button when you press it.
import UIKit
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
var myPopover : UIPopoverPresentationController?
#IBAction func buttonPressed(sender: UIButton) {
// you can init your own View Controller here
var popOverView = self.storyboard?.instantiateViewControllerWithIdentifier("interestViewController‌​") as! interestViewController
popOverView.preferredContentSize = CGSize(width: 250, height: 200);
var destNav = UINavigationController(rootViewController: popOverView)
destNav.modalPresentationStyle = UIModalPresentationStyle.Popover
self.myPopover = destNav.popoverPresentationController
self.myPopover?.delegate = self
self.myPopover?.sourceView = self.view
var theFrame = sender.frame
self.myPopover?.sourceRect = theFrame
destNav.modalPresentationStyle = UIModalPresentationStyle.Popover
destNav.navigationBarHidden = true
self.presentViewController(destNav, animated: true, completion: nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
}