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

SOLVED: Picker and SegmentedPickerStyle

Forums > SwiftUI

Hey there

i'm try to change my url from a Picker but i can't get it to work. Can anyone please help me?

Here is the code:

@State private var selectedCity = 0
var cities = ["copenhagen", "arhus", "odense" , "malmo"]

 Section {
        Picker("Choose a City", selection: $selectedCity) {
                  ForEach(0 ..< cities.count) { index in
                                 Text(self.cities[index]).tag(index)
                       }

 }.pickerStyle(SegmentedPickerStyle())

guard let url = URL(string: "http://salah.dk/ws.php?city=\(cities[selectedCity])") else {
        print("Invalid URL")
     return

3      

Hi Lamin, if I add a Text below your Picker the City is changed accordingly. So could you please specify what does not work?

    Section {
        Picker("Choose a City", selection: $selectedCity) {
            ForEach(0 ..< cities.count) { index in
                Text(self.cities[index]).tag(index)
            }
        }.pickerStyle(SegmentedPickerStyle())
        Text("\(cities[selectedCity])")
    }

3      

Please see the image below: The right side of the app has time and the time should change according to which city the user chooses. It only works with a normal Picker that shows a NavigationView with a list of the cities.

![](https://www.dropbox.com/s/sa9uy1yiu2u3vb4/Simulator%20Screen%20Shot%20-%20iPhone%2011%20Pro%20-%202020-09-22%20at%2020.35.12.png?dl=0

3      

With your provided code example I can't see how the list is actually created and where the possible error is.

4      

This is the whole code:

import SwiftUI

struct ContentView: View {
    @State private var selectedCity = 0
    @State private var prayerTimes = [String: String]()

    @State private var date = Date()

    var cities = ["copenhagen","arhus", "odense" , "malmo"]

    var body: some View {
        NavigationView {
            Form {
                Section {
                    Picker("Choose a city", selection: $selectedCity) {
                        ForEach(0 ..< cities.count) {
                            Text("\(self.cities[$0].capitalizingFirstLetter())")

                        }

                    }.pickerStyle(SegmentedPickerStyle())
                    //Text(" \(cities[selectedCity].capitalizingFirstLetter())" )
                  ``

                }

                Section {

                    VStack(alignment: .leading) {
                        List(prayerTimes.sorted(by: {$0.1 < $1.1 }), id: \.key) { prayerName, time in
                            VStack(alignment: .trailing) {
                                switch prayerName {
                                case PrayerSymbols.fajr.rawValue:
                                    PrayerIconView(prayerIconName: "sun.haze.fill", backgroundColor: Color(#colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1)))

                                case PrayerSymbols.shuruk.rawValue:
                                    PrayerIconView(prayerIconName: "sunrise.fill", backgroundColor: Color(#colorLiteral(red: 0.2588235438, green: 0.7568627596, blue: 0.9686274529, alpha: 1)))

                                case PrayerSymbols.dhuhr.rawValue:
                                    PrayerIconView(prayerIconName: "sun.max.fill", backgroundColor: Color(#colorLiteral(red: 0.2588235438, green: 0.7568627596, blue: 0.9686274529, alpha: 1)))

                                case PrayerSymbols.asr.rawValue:
                                    PrayerIconView(prayerIconName: "cloud.sun.fill", backgroundColor: Color(#colorLiteral(red: 0.7254902124, green: 0.4784313738, blue: 0.09803921729, alpha: 1)))

                                case PrayerSymbols.hanafi.rawValue:
                                    PrayerIconView(prayerIconName: "cloud.sun.fill", backgroundColor: Color(#colorLiteral(red: 0.7254902124, green: 0.4784313738, blue: 0.09803921729, alpha: 1)))

                                case PrayerSymbols.maghrib.rawValue:
                                    PrayerIconView(prayerIconName: "sunset.fill", backgroundColor: Color(#colorLiteral(red: 0.2196078449, green: 0.007843137719, blue: 0.8549019694, alpha: 1)))

                                case PrayerSymbols.isha.rawValue:
                                    PrayerIconView(prayerIconName: "moon.stars.fill", backgroundColor: Color(#colorLiteral(red: 0.09019608051, green: 0, blue: 0.3019607961, alpha: 1)))

                                default:
                                    PrayerIconView(prayerIconName: "", backgroundColor: Color.black)
                                }
                                Spacer()
                            }

                            Spacer()

                            VStack(alignment: .center) {
                                Text(prayerName.capitalizingFirstLetter())

                            }
                            Spacer()
                            VStack(alignment: .trailing) {
                                Text(time)

                            }

                        }

                    }

                }

            }
            .navigationBarTitle("Salahtider")
            .onAppear(perform: loadData)

        }
    }

    func loadData() {
        guard let url = URL(string: "http://salah.dk/ws.php?city=\(cities[selectedCity])") else {
            print("Invalid URL")
            return
        }

        let request = URLRequest(url: url)

        let config = URLSessionConfiguration.default
        config.waitsForConnectivity = true

        URLSession(configuration: config).dataTask(with: request) { data, response, error in
            if let data = data {
                if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
                    // we have good data - go back to the main thread
                    DispatchQueue.main.async {
                        self.prayerTimes = decodedResponse.prayerTimes
                        //self.date = decodedResponse.date
                    }
                    // everything is good, so we can exit
                    return
                }
                print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
            }
            config.timeoutIntervalForResource = 60
        }.resume()
    }
}

enum PrayerSymbols: String {
    case fajr = "fajr"
    case shuruk = "shuruk"
    case dhuhr = "dhuhr"
    case asr = "asr"
    case hanafi = "hanafi asr"
    case maghrib = "maghrib"
    case isha = "isha"
}

extension String {
    func capitalizingFirstLetter() -> String {
        return prefix(1).capitalized + dropFirst()
    }
    mutating func capitalizeFirstLetter() {
        self = self.capitalizingFirstLetter()
    }
}

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

struct PrayerIconView: View {
    var prayerIconName: String
    var backgroundColor: Color

    var body: some View {
        VStack(alignment: .leading) {
            Image(systemName: prayerIconName)
                .renderingMode(.original)
                .font(.system(size: 15))
                .padding(.all)
                .background(backgroundColor)
                .clipShape(Circle())
        }
    }
}

3      

It seems you copied this code from somewhere but you forgot to implement your data structure for the JSON you want to decode:

if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {

In this line the Response should be a struct matching your JSON data format. I can't find it in your code.

3      

I do get data from my JSON and the times change with a normal Picker without changing the style to segmented. The code is from Pauls example.

My here the model:

struct Response: Codable {
    var date: String
    var prayerTimes: [String: String]

}

3      

Hi Lamin, I've found a solution and an explanation why these Picker styles work different. You load the data onAppear. This fires only if the view appears. With the default picker the view changes, you select the city, go back and the view changes again. With the segmented picker the view appears only once and therefore the view loads the data only once. Regardless your selection in the picker. I wasn't able to use the .onTapGesture for the picker because it seems it interferes with the built in gesture recognizer for the picker.


import SwiftUI
import Combine //needed for CityManager

/** This class replaces your selectedCity state variable
 Basically it informs everyone llistening to the class of changes to the selected index.
 */
class CityManager: ObservableObject {
    @Published var index = 0 {
        didSet {
            publisher.send(index)
        }
    }
    let publisher = PassthroughSubject<Int, Never>()
}

struct ContentView: View {
    // Changed variable
    @ObservedObject private var selectedCity = CityManager()
    @State private var prayerTimes = [String: String]()

    @State private var date = Date()

    var cities = ["copenhagen","arhus", "odense" , "malmo"]

    var body: some View {
        NavigationView {
            Form {
                Section {
                    Picker("Choose a city", selection: $selectedCity.index) {
                        ForEach(0 ..< cities.count) {
                            Text("\(self.cities[$0].capitalizingFirstLetter())")

                        }
                    }.pickerStyle(SegmentedPickerStyle())
                    // This fires everytime you change the selection of the segmented picker
                    .onReceive(selectedCity.publisher) { _ in
                        loadData()
                    }

                    // rest is untouched
       }
}

//Changed variable in URL
guard let url = URL(string: "http://salah.dk/ws.php?city=\(cities[selectedCity.index])") else {
            print("Invalid URL")
            return
        }

3      

Hey Hatsushira, wow it makes sense, thanks very much for your time. I appreciate it very 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.