I am exploring protocol and got a probelem
ViewController1.swift
protocol filterApplied {
func appliedFiiler(isApplied: Bool)
}
class : UIViewController{
var delegate : filterApplied?
// on some button action
delegate?.appliedFiiler(isApplied: true)
}
ViewController2.swift
class ViewController2 : UIViewController,filterApplied {
func appliedFiiler(isApplied: Bool) {
if isApplied{
filterButton.imageView?.image = UIImage(named: "filter_applied")
}
}
}
now I know that this will not do anything
as I haven't assigned the delegate to self.
how and where would i Do that so the appliedFilterFunction in 2 swift file is working?
You have to connect delegate to self of ViewController2.
protocol FilterApplied {
func appliedFiiler(isApplied: Bool)
}
Class ViewController1: UIViewController {
var delegate: FilterApplied?
#IBaction func onClick() {
self.delegate?.appliedFiiler(isApplied: true)
}
In Viewcontroller2 you have to connect delegate to self. By either navigating controller or presenting controller.In this case i have connected on presenting controller.
Class ViewController2: UIViewController, FilterApplied {
func onPresent() {
let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController1") as! ViewController1
vc.delegate = self
self.present(vc, animated: false, completion: nil)
}
func appliedFilter(isApplied: Bool) {
if isApplied{
filterButton.imageView?.image = UIImage(named: "filter_applied")
}
}
}
You may want something like this
//ViewController1.swift
// definition
protocol FilterApplied: class {
var filterButton: UIButton! { get set }
func appliedFilter(isApplied: Bool)
}
// defaults protocol behaviour
extension FilterApplied {
//
func appliedFilter(isApplied: Bool) {
if isApplied{
filterButton.imageView?.image = UIImage(named: "filter_applied")
}
}
}
//ViewController2.swift
class ViewController2: UIViewController, FilterApplied {
#IBOutlet var filterButton: UIButton! {
didSet {
// used defaults
appliedFilter(isApplied: true)
}
}
// overrides defaults
func appliedFilter(isApplied: Bool) {
}
}
In the end you can choose to use default behaviour or add a new one.
Related
I have 2 UIViewControllers, ViewController, SecondViewController. I defined delegate function in VC, and using in Second VC. But delegate functions not calling in Second VC.
This is mu first VC code
import UIKit
//Step1:
protocol testDelegate {
func testFunction(string1: String, string2:String)
func math(a:Int, b:Int)
}
class ViewController: UIViewController {
//Step2:
var delegateVariable: testDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func moveToSecondVC(_ sender: Any) {
let nav = self.storyboard?.instantiateViewController(withIdentifier: "SVC") as! SecondViewController
//Step3:
delegateVariable?.testFunction(string1: "String1", string2: "String2")
delegateVariable?.math(a:30, b:10)
self.navigationController?.pushViewController(nav, animated: true)
}
}
My second VC code
import UIKit
//Step4:
class SecondViewController: UIViewController , testDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//Step5:
let svc = ViewController()
svc.delegateVariable = self
}
#IBAction func btn(_ sender: Any) {
//Step5:
let svc = ViewController()
svc.delegateVariable = self
}
//Step6:
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
print(a+b)
print(a-b)
print(a*b)
}
}
Here i'm just passing small amount of data for practice, but can any one please suggest some high level delegate example tutorial links for me.
This is why nothing is happening...
let svc = ViewController()
svc.delegateVariable = self
You are creating a NEW ViewController, not using the one that is actually in use.
It does not look like you are using the delegate pattern properly. Your ViewController should not be calling code on other view controllers.
SecondViewController should "do stuff" and then let ViewController know what it has done.
For the Math function you could just use a new class (not a view controller) and create and use this as needed. You do not need a ViewController for this.
An example of using a delegate might be something like:
protocol CreateProfileDelegate: class {
func didCreateProfile(profile: Profile?)
func didCancelCreateProfile()
}
class ViewController: UIViewController {
func showCreateProfile() {
let vc = CreateProfileViewController()
vc.delegate = self
present(vc, animated: true)
}
}
extension ViewController: CreateProfileDelegate {
func didCreateProfile(profile: Profile?) {
// show the profile?
}
func didCancelCreateProfile() {
// show an alert maybe?
}
}
This way the SecondViewController (CreateProfileViewController) basically tells the first that something has happened so that it can react to it.
in SecondViewController you are setting....
let svc = ViewController()
svc.delegateVariable = self
That just create an object of ViewController() class and then you set the delegate. So when the obj. of the scope is finished then the memory of the object will be increased automatically.
The flow should like below....
Create an object of the Viewcontroller in SecondViewController and set the delegate
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.delegateVariable = self
Then push the view controller in to the navigation stack.
self.navigationController?.pushViewController(svc, animated: true)
Implement the delegate method of testDelegate in SecondViewController
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
}
EDIT
The final code of the SecondViewController Will be...
import UIKit
class SecondViewController: UIViewController , testDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btn(_ sender: Any) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.delegateVariable = self
self.navigationController?.pushViewController(svc, animated: true)
}
//MARK:- TestDelegate Methods
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
print(a+b)
print(a-b)
print(a*b)
}
}
I want to use UISwitch from that when isON use it should be in ViewController1 isOFF then it has to go next ViewController. Can someone help me on this?
import Foundation
import Firebase
import FirebaseAuth
class HomeViewController: UIViewController {
#IBOutlet weak var citiPostSwitch:UISwitch!
#IBAction func switchToCitiPost (switchState: UISwitch){
if citiPostSwitch.isOn{
print("your in ViewConroller 1")
citiPostSwitch.setOn(false, animated: true)
} else{
citiPostSwitch.setOn(true, animated: true)
print("your in ViewController 2")
}
}
#IBAction func logOutAction(sender: AnyObject) {
if Auth.auth().currentUser != nil {
do {
try Auth.auth().signOut()
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SignUp")
present(vc, animated: true, completion: nil)
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
}
In your function , just off the switch of this and second controller, in which your are navigating.
#IBAction func switchToCitiPost (switchState: UISwitch){
if citiPostSwitch.isOn {
citiPostSwitch.setOn(false, animated: true)
let vc = SecondViewController()
vc.switch.setOn(false, animated: true)
self.navigationController?.pushViewController(vc, animated: true)
}
}
When your come to your controller you will get your switch in off state.
class SecondViewController: UIViewController {
#IBOutlet weak var `switch`: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
self.switch.addTarget(self, action: #selector(self.switchAction(_:)), for: .valueChanged)
}
#objc func switchAction(_ switchState: UISwitch) {
self.navigationController?.popViewController(animated: true)
}
}
In SecondView Controller , on switch action just dismiss or pop from Controller.
Hope it helps
I have a ViewController which have a button, it calls some method in MyClass.swift. I'm trying to navigate from MyClass.swift to UIViewController MainViewController when I press that button. I implemented this method and I get "navigating..." in the log but nothing happen.
class Myclass {
// some code ...
func someFunc() {
// some code ...
navigateToMainViewController()
}
func navigateToMainViewController() {
let storyboard: UIStoryboard? = UIApplication.shared.keyWindow?.rootViewController?.storyboard
if let myMVC = storyboard?.instantiateViewController(withIdentifier: "MainViewController") {
print("navigating....")
let navController = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController
navController?.pushViewController(myMVC, animated: true)
}else {
print("Something wrong..")
}
}
// some code ...
}
Thank your for your help.
Change your navigation Function and add a parameter of viewController to it, and take a variable in myClass of type UIViewController:
var viewController: UIViewController!
func someFunc(_ viewController: UIViewController) {
self.viewController = viewController
self.navigateToMainViewController()
}
func navigateToMainViewController() {
let storyboard: UIStoryboard? = UIApplication.shared.keyWindow?.rootViewController?.storyboard
if let myMVC = storyboard?.instantiateViewController(withIdentifier: "MainViewController") {
print("navigating....")
self.viewController.navigationController?.pushViewController(myMVC, animated: true)
}else {
print("Something wrong..")
}
}
Then From viewController, Call someFunc() with instance of ViewController:
class ViewController: UIViewController {
#IBAction func buttonPressed(_ sender: UIButton) {
myClassObject.someFunc(self)
}
}
I have two UIViewController, when I click a button, it goes from the first view controller to the second one. And before that, I animated a UIView to move to another place. After dismissing the second View Controller, I want to move the UIView in the first view controller back to where it originally was. However, when I call a function from the second View Controller to animate the UIview in the first view controller after dismissing the second one, It could not get the UIView's properties, and cannot do anything with it. I think because the first UIViewController is not loaded yet. Is that the problem? And How should I solve this?
There are two solutions you can either use swift closures
class FirstViewController: UIViewController {
#IBAction func start(_ sender: Any) {
guard let secondController = self.storyboard?.instantiateViewController(withIdentifier: "SecondController") as? SecondController else { return }
secondController.callbackClosure = { [weak self] in
print("Do your stuff")
}
self.navigationController?.pushViewController(secondController, animated: true)
}
}
//----------------------------
class SecondController: UIViewController {
var callbackClosure: ((Void) -> Void)?
override func viewWillDisappear(_ animated: Bool) {
callbackClosure?()
}
}
or you can use protocols
class FirstViewController: UIViewController {
#IBAction func start(_ sender: Any) {
guard let secondController = self.storyboard?.instantiateViewController(withIdentifier: "SecondController") as? SecondController else { return }
secondController.delegate = self
self.navigationController?.pushViewController(secondController, animated: true)
}
}
extension ViewController : ViewControllerSecDelegate {
func didBackButtonPressed(){
print("Do your stuff")
}
}
//--------------------------
protocol SecondControllerDelegate : NSObjectProtocol {
func didBackButtonPressed()
}
class SecondController: UIViewController {
weak var delegate: SecondControllerDelegate?
override func viewWillDisappear(_ animated: Bool) {
delegate?.didBackButtonPressed()
}
}
You can try to use a closure. Something like this:
class FirstViewController: UIViewController {
#IBOutlet weak var nextControllerButton: UIButton!
private let animatableView: UIView = UIView()
private func methodsForSomeAnimation() {
/*
perform some animation with 'animatableView'
*/
}
#IBAction func nextControllerButtonAction() {
// you can choose any other way to initialize controller :)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let secondController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else { return }
secondController.callbackClosure = { [weak self] in
self?.methodsForSomeAnimation()
}
present(secondController, animated: true, completion: nil)
}
}
class SecondViewController: UIViewController {
#IBOutlet weak var dismissButton: UIButton!
var callbackClosure: ((Void) -> Void)?
#IBAction func dismissButtonAction() {
callbackClosure?()
dismiss(animated: true, completion: nil)
/*
or you call 'callbackClosure' in dismiss completion
dismiss(animated: true) { [weak self] in
self?.callbackClosure?()
}
*/
}
}
When you present your second view controller you can pass an instance of the first view controller.
The second VC could hold an instance of the first VC like such:
weak var firstViewController: NameOfController?
then when your presenting the second VC make sure you set the value so it's not nil like so:
firstViewController = self
After you've done this you'll be able to access that viewControllers functions.
iOS 11.x Swift 4.0
In calling VC you put this code ...
private struct Constants {
static let ScannerViewController = "Scan VC"
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == Constants.ScannerViewController {
let svc = destination as? ScannerViewController
svc?.firstViewController = self
}
}
Where you have named the segue in my case "Scan VC", this is what it looks like in Xcode panel.
Now in scan VC we got this just under the class declaration
weak var firstViewController: HiddingViewController?
Now later in your code, when your ready to return I simply set my concerned variables in my firstViewController like this ...
self.firstViewController?.globalUUID = code
Which I have setup in the HiddingViewController like this ...
var globalUUID: String? {
didSet {
startScanning()
}
}
So basically when I close the scanning VC I set the variable globalUUID which in term starts the scanning method here.
When you are saying it could not get the UIView's properties it's because you put it as private ? Why you don't replace your UIView in the first controller when it disappears before to go to your secondViewController. I think it's a case where you have to clean up your view controller state before to go further to your second view controller.
Check IOS lifecycle methods : viewWillDisappear or viewDidDisappear through Apple documentation and just do your animation in one of these methods.
Very simple solution actually... Just put your animation in the viewDidAppear method. This method is called every time the view loads.
class firstViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// insert animation here to run when FirstViewController appears...
}
}
I am having troubles showing the Game Center leaderboard when the user presses a button on my SecondViewController (game over screen with score/top score). The UIbutton is ShowLeaderboard which you'll see below.
The error I get is:
Warning: Attempt to present <GKGameCenterViewController: 0x7fb1c88044a0> on <UIViewController: 0x7fb1c2624e90> whose view is not in the window hierarchy!
I have tried dismissing the view first but no matter what I do I can't just get the leaderboard view to show. Below is my SecondViewController code:
import UIKit
import GameKit
class SecondViewController: UIViewController, GKGameCenterControllerDelegate {
#IBOutlet var scoreLabel: UILabel!
#IBOutlet var HighScoreLabel: UILabel!
var receivedString: String = ""
var receivedHighScore: String = ""
override func viewDidLoad() {
super.viewDidLoad()
scoreLabel.text = receivedString
HighScoreLabel.text = receivedHighScore
}
#IBAction func ShowLeaderboard(sender: UIButton) {
dismissViewControllerAnimated(true, completion:nil)
showLeader()
}
func showLeader() {
var vc = self.view?.window?.rootViewController
var gc = GKGameCenterViewController()
gc.gameCenterDelegate = self
vc?.presentViewController(gc, animated: true, completion: nil)
}
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController!)
{
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
override func prefersStatusBarHidden() -> Bool {
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func retryButton(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
EDIT Got it working! All I had to do was change
var vc = self.view?.window?.rootViewController
to
var vc = self
You are probably seeing this warning because you are displaying the Leaderboard before dismissViewControllerAnimated has finished the animation. You should place the showLeader() inside the completion argument of dismissViewControllerAnimated.
Here is my Code. Hope it helps!
if (self.levelGameAttemptCount == 3)
{
self.canRestart = false
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewControllerWithIdentifier("gameOverControllerID") as! GameOverController
self.view!.window!.rootViewController!.dismissViewControllerAnimated(false, completion: nil)
UIApplication.sharedApplication().keyWindow!.rootViewController!.presentViewController(vc, animated: true, completion: nil)
}
else
{
self.canRestart = true
}