UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Modify attribute of the same view depending on the output value

Forums > SwiftUI

How do I modify a view's attribute based on certain output values?

For example: I have a Text(deriveVibration(string: profile.name)) that returns a calculated value(string) based on textfield input. If the result is 4, 6 or 8, I want the Text to have the attribute of .foregroundColor(Color.purple) If the result is 0, the same Text will then change the attribute to .foregroundColor(Color.white) Else, return Text with an attribute of .foregroundColor(Color.red)

I appreciate anyone's input on this. Thank you!

3      

Hi Sally,

So this might change the way you have your project set up but to see what you think copy and paste somewhere and run it in your preview -

import SwiftUI

struct ContentView: View {
    @ObservedObject var myText = MyText() // Observing below object 

    var body: some View {
        VStack {
            TextField("Name", text: myText.getValueBinding()) // Using the below function inside below class for textfield
            Text(myText.name)
                .foregroundColor(myText.getColor())

        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

class MyText: ObservableObject {
    @Published var name: String = ""

    func getValueBinding() -> Binding<String> {  // This function returns a Binding which can be used in a textfield.
         let binding = Binding<String>(get: { () -> String in
             return self.name
         }) { (newValue) in
             self.name = newValue
         }
         return binding
    }

    func getColor() -> Color { // This function just returns a color to where it is called
        switch name {
        case "0": return Color.red
        case "1": return Color.green
        default: return Color.blue
        }
    }

}

So if you type 0 the text will be red, if 1 then text is green and anything else will be blue. This is a very very basic example and hopefully what you are after. Now there are many ways you could implement this. The class is where you would probably put your derive functions as well. Then you could just call myText.derive for the textfields etc

Play around with it and see how you go.

Dave

3      

Thanks for the reply. I tried running this after pasting it but didn't seem to work.

The Text I need to modify is the text value after a function has been used to return a new value.

I can access the profile.name which is the observable object. But the one I need to modify is the Text that has been recalculated to return a new value.

Is there a way to get the value binding from this Text(deriveVibration(string: self.profile.name))?

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

you could try adding another @Published property to your observable object called color

@Published var color = Color.green // default value

then create a function for the observableobject called setColor

private func setColor(string: String) {
    // Put your switch statement or if/else here based on argument passed to it and set your self.color
}

Then in your derive function, call the above function passing to it the result you calculated in the derive function, so probably just before your return statement of the derive function. So all together you could distegard what i posted above and try.

Once above is done you can now put this in your foreground modifier

.foregroundColor(profile.color)

Okay.... what should happen.

  1. You have now made a @Published color property so any changes to this will reflect in the view using it.
  2. When user types name in textfield, your derive function is called and the return value from this function is displayed in your text view. So any time a character is typed the function is called.
  3. Now because we call the setColor func now from that derive func, this means that everytime derive is called then setColor is called and the color property is set
  4. Because the color property is @Published and we have used it in the foreground color then the text color will change.

Hopefully that all made sense Sally. Give it a go and let me know if it suits

Dave

3      

Hi Sally,

The modifier (.foregroundColor()) changes the View (Text()) and therefore you are changing the string of the View but not the View itself. you need to take the func (deriveVibration(string: profile.name)) out of Text View and replace is with an @State property so it know to redraw the Text view and apply the modiflier to it, which could be done with a computed var

Without seeing the code that you have make it difficult to see the flow.

3      

I appreciate you both for your suggestions. Nigelgee, How do I create a State var for the new calculated value?

I tried implementing what you suggested Dave but I'm a bit confused as to how to call this function in my derive function.

To give you both some clarification. I'll post the necessary code below...

class Profile: ObservableObject {
    @Published var name = ""
    @Published var color = Color.red

    convenience init (name: String) {
        self.init()
        self.name = name
    }
}
struct ContentView: View {
    @EnvironmentObject var viewRouter: ViewRouter
    @EnvironmentObject var profile: Profile

var body: some View {
//some code here not shown....
TextField("Enter a Name", text: self.$profile.name)
            .multilineTextAlignment(TextAlignment.center)
            .colorInvert()
            .font(.custom("Poppins-Light", size: 15))
            .foregroundColor(Color.black)

GeometryReader { proxy in
    HStack(spacing: 0) {
        Image(self.wealthVibe(string: self.profile.name))
            .resizable()
            .frame(maxWidth: 134, maxHeight: 134)
        VStack {
            HStack {
                Text(self.deriveVowels(string: self.profile.name))
                Text(self.deriveConsonants(string: self.profile.name))
                Text(self.deriveRawTotal(string: self.profile.name))
            .font(.headline)
            .foregroundColor(Color.white)
                    }
            Text(self.deriveVibration(string: self.profile.name))
            .font(.largeTitle)
            .tracking(3)
            .foregroundColor(self.profile.color)
                }
                private func setColor(string: String) -> Color { // I moved all my functions one level up to be in the contentView so I could access profile.name
        switch profile.name {    // <- Did I do this right?
    case "4": return Color.purple
    case "6": return Color.purple
    case "8": return Color.purple
    case "1": return Color.red
    case "3": return Color.red
    case "5": return Color.red
    case "7": return Color.red
    case "9": return Color.red
    default: return Color.white
            }
        }
        func deriveVibration(string: String) -> String {
      if var V = Int(deriveVowels(string: string)), var C = Int(deriveConsonants(string: string)) {
         if V > 9 { V = (1 + ((V-1) % 9)) }
         if C > 9 { C = (1 + ((C-1) % 9)) }

         var T = V + C
         if T > 9 { T = (1 + ((T-1) % 9)) }
        let SetColor = setColor(string: string) // This is where I'm confused as to how to code this as it was never used here
        return String("\(V)\(C)\(T)")
            }
          return "0"
        }

When I run this, it fails to build... maybe I wasn't allowed to have the functions in the body

3      

Hi Sally,

My last example was if you had your derive functions and setColor functions inside your Profile Observable Ojbect. Since you have put them in the Content View struct you will have to change the following. The reason why it doesn;t work at the moment is because of this line -

let SetColor = setColor(string: string) 

You are declaring a constant SetColor (just on a side note be careful of your naming conventions for variables, constants etc - For classes, structs etc it starts with capital let but for vars and lets it starts with lower case. In the above line it should of been setColor not SetColor. But because your func is called that it can get confusing so probably stick with just 'color')

So back to that line of code. All you are doing here is creating a constant which is a Color which is returned by calling that function. Because you haven;t assigned it to any view property then no changes will be refelcted etc. So based on what you have showed me lets delete that line. Lets also delete the color property in your Profile object. So now you will just have your deriveVibration and setColor functions in your content view. Make sure they are placed outside the var body property. So the functions should be placed after this.

Now with your setColor func and the switch statement. If you have a number of cases in a switch statement that return the same Color you can combine them, so change the func to the following -

func setColor(string: String) -> Color { // I moved all my functions one level up to be in the contentView so I could access profile.name
        switch string {    // So here you are switching the string that was passed to this function. We shall get to this after.
              case "4", "6", "8": // Combinind like case statements by separating with commas
                  return Color.purple
              case "1", "3", "5","7", "9":
                  return Color.red
              default:
                  return Color.black
         }
}

You can keep your deriveVibration func the same as it was before (remember we now just delete that let SetColor statement). -

func deriveVibration(string: String) -> String {
      if var V = Int(deriveVowels(string: string)), var C = Int(deriveConsonants(string: string)) {
           if V > 9 { V = (1 + ((V-1) % 9)) }
           if C > 9 { C = (1 + ((C-1) % 9)) }

           var T = V + C
           if T > 9 { T = (1 + ((T-1) % 9)) }

            return String("\(V)\(C)\(T)")
      }
      return "0"
 }

Now you can leave your Text views how you had it before -

Text(self.deriveVibration(string: self.profile.name))

And now all we do is call the setColor func, passing in the result of the deriveVibration func like so -

.foregroundColor(setColor(string: deriveVibration(string: profile.name)))

So like the text view when the user types in the TextField, the profile.name changes. By calling setColor passing the return result of calling deriveVibration on profile.name, this will ensure that every time user types into TextField the color will change.

Now one important thing to remember, the order of modifiers really do matter. This is a subject on its own so i dont wont to get into it too much here. It is not an issue here with your code because .foregroundColor is your last modifier on that text view, but if you had a number of other modifiers mixed in with it then you may not see the desired results. As I said shouldn;t be an issue with your code as it is.

So after all that make the changes and see how you go

Dave

3      

Hi Sally

Dave solution looks good and should work. My only corcern is that you are calling the func twice when you could return the string.

Thanks Dave for pointing that what I said was correct but think you misinterpreted when I said a computed property was a @State this is what I meant

struct ContentView: View {
    @State private var formattedString = ""

    var color: Color {
        switch formattedString {
            case "4", "6", "8":
                return .purple
            case "1", "3", "5","7", "9":
                return .red
            default:
                return .white
        }
    }

    var body: some View {
        Text(formattedString)
            .foregroundColor(color)
    }
}

PS you know that you are missing "2"

3      

Hey Nigel, yeah my bad i did misinterpret :( Sorry about that. I realised and deleted that reply.

Sally, Nigels solution also looks good and maybe better. This is whats good about talking with others and throwing around solutions. Youll find heaps of different ways to tackle a problem and at the end of the day we all learn something.

3      

Dave, I'm so happy it works! Thanks again! I just forgot to mention the value i switched is just the last character of the string but I added it to the function now.

Thank you so much!

4      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.