RjC
RjC

Reputation: 847

Swift: How to re-use view controller

I am working on a simple custom converter app. The problem is that I have created a separate view controller for each (!) conversion (fahrenheit to celsius, ounces to kilograms and so on) and I would like to know how I could re-use only one converter view controller and just change the calculations done in the background?

This is what I have done so far:

Each converter VC contains a name label (eg. ounce to kilogram), a number pad and an input/output label. When the user touches a number the conversion is done on the fly with the right math formula from MathLib.swift. I just use the corresponding formula with a return. This is an example of a formula:

static func stoneToKilo(stone: Double) -> Double {
        let kilo = stone / 0.15747
        return kilo
    }

Now what I want to do is just having one(!) converter VC and depending on the button pressed in mainVC the right formula is used in MathLib and the name of the label is changed to the right conversion type.

  1. All buttons should point to just one VC. Now when a button is pressed and the view controller is presented how can I check from which button the user comes? For example the button name or tag?

  2. When I know the button name or tag how can I use this to use the right formula in MathLib.swift or change the name label? Do I have to use a switch case? If so: how can I set up the switch case to check for the button used and then point to the right math function and change the name label?

In short: Using just one VC, checking for which button brought the user to this VC and then using something (switch case?) to change some things like labels on the VC and use the right function in a library.swift file.

This is quite difficult for me and if you could help me I'd appreciate this.

Upvotes: 2

Views: 211

Answers (2)

TheFuquan
TheFuquan

Reputation: 1797

You are on the right track.

Now that you can get which button was clicked, like you said by name or the tag (i prefer the second, think of when you'll be making your app available in different languages, i.e localization).

Your main view controller should now only contain an input, and a button, and may be a title for the screen, to know which conversion is being used.

As for the calculation (the business logic), i would recommend that you define a protocol that has one method called convert that takes one argument, being the value the user would want to convert.

protocol Converter {
    func convert( _ value : Double) -> Double
}

and then create as much classes as there are conversions to be made, let me take only one example and keep it as simple as possible, consider the converter meters to kilometers.

let's call it DistanceConverter that should implement the Converter protocol

class DistanceConverter : Converter {
    func convert( _ value : Double) -> Double {
         return value / 1000.0
    }
}

Now when you tap the button from the first screen called meter to kilometers, in your handler you would :

1) instantiate an instance of the DistanceConverter.

2) instatiante an instance of MainViewController. and give it the DistanceConverter instance you created in step 1.

3) keep a reference of the DistanceConverter as an iVar in your mainViewController.

the key point here is to keep it in an iVar of type Converter, so it can hold all the instances of any class that would implement the Converter protocol you will be creating.

4) in the handler of the button 'convert' of the mainViewController you call the method convert on the mainViewController 's iVar that you made in step 3.

And so now in order for you to create another converter, let's say TemperatureConverter that handles Celcius to Fahrenheit, you create a new class :

class TemperatureConverter : Converter {
    func convert( _ value : Double) -> Double {
         // return the calculation, 
    }
}

When you tap the button temerature on the first screen: 1) instantiate an instance of the DistanceConverter. and then keep repeat all the remaining steps as before (you can easily refactore all the rest of the steps).

This is the Strategy pattern out of the box, so i recommend you read about it and it's application with swift.

Upvotes: 3

erik_m_martens
erik_m_martens

Reputation: 491

You should pass a delegate to the view controller where the conversion happens, which implements the function you want to use. When you instantiate the view controller before you push or present it, inject the delegate.

Define a protocol with a protocol-method for each method you want to use and then let the delegate implement them via an extensions. The delegate can be even be the ViewController that allows to select a conversion type.

You can then define an enum with your conversion type and also pass it to the new view controller. On your view controller just switch over the type and run the correct delegate method.

enum ConversionType: Int {
    case fahrenheitToCelsius
    // other cases
}

protocol ConversionDelegate {
    func convertToCelsius(fromFahrenheitDegrees fDegrees: Double) → Double
    // other protocol functions
}

// Other protocols here

class SelectionViewController: UIViewController {
    // your usual stuff like viewDidLoad

    func presentConversionViewController(forConversion type: ConversionType) {
        let destinationVC = ... // instantiate your VC from storyboard here
        destinationVC.delegate = self
        destinationVC.conversionType = type
        // present/push your VC
}

extension SelectionViewController: ConversionDelegate {
    func convertToCelsius(fromFahrenheitDegrees fDegrees: Double) → Double {
        // you Math.lib func here
        return (fDegrees - 32) / 1.8 // example
    }
    // implement the other functions
}

On your destination view controller:

class DestinationViewController: UIViewController {
    var delegate: ConversionDelegate!
    var type: ConversionType!

    // your usual stuff like viewDidLoad

    @IBAction func calculateButtonPressed(_ sender: UIButton) {
        switch type {
            case .fahrenheitToCelsius:
            // read the input value from somewhere like a UITextField
            // most likely you will have to convert a String to Double in the example 
            let result = delegate.convertToCelsius(fromFahrenheitDegrees: yourInputValue) 
            // output the result to some label or whatever you like
        }
    }
}

Upvotes: -1

Related Questions