Convertisseur binaire/décimal et décimal/binaire

Bonjour,

J’ai posé il y a de ça quelques jours une question sur le forum à fin de créer une application qui convertit des nombres décimal en binaire et inversement. J’ai pu réussi à faire cela avec les playground via cette fonction:

let binaryString = Int("15", radix: 10).map { String($0, radix: 2) }
let decimalString = Int("1111", radix: 2).map { String($0, radix: 10) }

La première fonction me donne bien 1111 et la deuxième 15. Cependant maintenant j’aimerais l’intégrer à une application de convertisseur comme celle réalisé de la formation iOS et que cela marche avec n’importe quel nombre!
Voilà le code de l’application iOS que on avait fait pendant la formation:

import UIKit
import Foundation

class ViewController: UIViewController {
    
    @IBOutlet weak var ui_segmentedcontrol: UISegmentedControl!
    @IBOutlet weak var ui_inputUserValue: UITextField!
    @IBOutlet weak var ui_meters: UILabel!
    @IBOutlet weak var ui_centimeters: UILabel!
    @IBOutlet weak var ui_inches: UILabel!
    
    func getInputMeterValue () -> Double? {
        let inputMeter:Double?
        if let inputString:String = ui_inputUserValue.text,
        let inputDouble:Double = Double(inputString) {
        switch ui_segmentedcontrol.selectedSegmentIndex {
        case 0: //m
            inputMeter = inputDouble
        case 1: //CM
            inputMeter = UnitLength.centimeters.converter.baseUnitValue(fromValue: inputDouble)
        case 2: //Inches
            inputMeter = UnitLength.centimeters.converter.baseUnitValue(fromValue: inputDouble)
        default:
            inputMeter = nil
        }
        }else {
            inputMeter = nil
        }
            return inputMeter
    }
    func convert () {
        if let inputMeter = getInputMeterValue() {
            ui_meters.text = "\(inputMeter)mètre"
            ui_centimeters.text = "\(UnitLength.centimeters.converter.value(fromBaseUnitValue: inputMeter))centimètre"
            ui_inches.text = "\(UnitLength.inches.converter.value(fromBaseUnitValue: inputMeter))pouces"
        } else {
            ui_inches.text = " "
            ui_centimeters.text = " "
            ui_meters.text = " "
            
        }
        
    }
    
    @IBAction func segmentedControle() {
        convert()
    }
    
    @IBAction func inputUserValue() {
        convert()
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }


}

PS : je vous joins le visuel de l’application convertisseur que l’on avait fait pendant la formation si jamais vous vous en rappelez plus :

J’aimerais donc modifier la fonction qui permet de convertir des mètres centimètres pouces… et la remplacer par une fonction qui permet de convertir des nombres décimaux en binaire et inversement.
Je vais donc faire une nouvelle application qui convertit juste des nombres décimal en binaire et inversement.
Cependant je ne vois pas comment mettre en fonction ce que j’ai fait via le playground…

Pourriez-vous m’aider s’il vous plaît?

Salut!

A quel moment tu bloques exactement ?

Au moment de rajouter et connecter les outlets ? Ou bien au moment ou tu dois transformer le String en Int ou Double ? Ou au moment ou tu dois rajouter ta fonction dans le switch ?

Nicolas.

Ça fait un peu formule magique.

Voilà comment j’aurais fait :

import UIKit

enum BaseNumerique:Int {
    case binaire     = 2
    case decimal     = 10
    case hexadecimal = 16
}

func extractionValeurNumerique(_ string:String, _ baseNum:BaseNumerique) -> Int? {
    return Int(string, radix: baseNum.rawValue)
}

func creerRepresentationNumerique(_ valeur:Int, _ baseNum:BaseNumerique) -> String {
    return String(valeur, radix:baseNum.rawValue)
}


class ViewController: UIViewController {

    @IBOutlet weak var uiLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Simulation d'une saisie utilisateur
        // en mode Decimal
        let saisieUtilisateur = "125"
        let baseCourante = BaseNumerique.decimal
        
        if let nombre = extractionValeurNumerique(saisieUtilisateur, baseCourante) {
            let strBinaire = creerRepresentationNumerique(nombre, .binaire)
            let strDecimal = creerRepresentationNumerique(nombre, .decimal)
            let strHexa = creerRepresentationNumerique(nombre, .hexadecimal)
            print ("Binaire : ", strBinaire)
            print ("Décimal : ", strDecimal)
            print ("Hexadécimal : ", strHexa)
        }

    }
}

Affichage :

Binaire : 1111101
Décimal : 125
Hexadécimal : 7d


C’est basé sur deux fonctions :

La première permet d’extraire la valeur numérique contenue dans une chaîne, dans différentes bases numériques.

func extractionValeurNumerique( _ string:String, _ baseNum:BaseNumerique) -> Int? {

return Int(string, radix: baseNum.rawValue)

}

Pour avoir un code lisible, j’ai créé un type de données personnalisé (enum) renfermant trois bases numériques : binaire, décimal et hexadécimal.

enum BaseNumerique:Int {
    case binaire     = 2
    case decimal     = 10
    case hexadecimal = 16
}

Utilisation pour lire une valeur en mode « saisie binaire » :

// Lecture d'une chaîne contenant du binaire
let saisieUtilisateur = "011001"
let valeur = extractionValeurNumerique(saisieUtilisateur, .binaire)

Même chose pour la lecture d’une saisie décimal :

// Lecture d'une chaîne contenant du décimal
let saisieUtilisateur = "172"
let valeur = extractionValeurNumerique(saisieUtilisateur, .decimal)

La fonction retourne une valeur Int? car la conversion peut échouer, si la chaîne ne contient pas les caractères attendus. Par exemple, la chaîne « 1001$G » n’est PAS un nombre !

Cela doit s’utiliser comme ça :

if let valeur = extractionValeurNumerique(saisieUtilisateur, .binaire) {
  .. faire quelque chose avec la variable valeur
}

Comme son nom l’indique, la seconde fonction fabrique une représentation numérique de n’importe quel nombre, sous différentes bases numériques. C’est l’inverse de la précédente.

func creerRepresentationNumerique(_ valeur:Int, _ baseNum:BaseNumerique) -> String {
    return String(valeur, radix:baseNum.rawValue)
}

Par exemple, comment fabriquer la représentation binaire de 1287 ?

let valeur = 1287
let strBinaire = creerRepresentationNumerique(valeur, .binaire)
print ("Binaire : ", strBinaire)

Voila le coeur du système de conversion. Il est facilement extensible. par exemple, on peut ajouter l’Octal (base 8)

Il ne reste plus qu’à reprendre l’interface du cours de Maxime et y insérer le convertisseur, pour avoir quelque chose comme ça :

La logique du mécanisme : chaque fois que l’utilisateur tape quelque chose dans le UITextField, l’interface appelle une fonction qui :

  • lit le contenu du TextField
  • lit l’état du segment pour savoir la base numérique sélectionnée par l’utilisateur
  • convertit le texte en une valeur numérique en fonction de cette base numérique
  • utilise la valeur numérique pour fabriquer une représentation en binaire, décimal et hexadécimal
  • affiche chaque représentation dans son label correspondant
1 « J'aime »

J’ai développé un prototype en SwiftUI pour tester le concept, profitant de chaque occasion pour m’exercer avec ce nouveau système
(l’avenir c’est maintenant).

A ma grande surprise, il n’y a pas de segment de contrôle dans la boîte à outil de SwiftUI. Dommage ! J’ai fait l’impasse sur la sélection de la base numérique de saisie. C’est du décimal par défaut.

Je verrai plus tard comment créer un équivalent (avec un picker ou plusieurs boutons).

Le convertisseur affiche « ??? » quand la conversion échoue. Exemple en tapant « 125K » à la place de « 125 ».


Le code :

import SwiftUI

enum BaseNumerique:Int {
    case binaire     = 2
    case decimal     = 10
    case hexadecimal = 16
}

// Classe Swift pouvant être utilisée par des contrôles SwiftUI
class Convertisseur:ObservableObject {
    
    private func extractionValeurNumerique(_ string:String,
                                   _ baseNum:BaseNumerique) -> Int? {
        return Int(string, radix: baseNum.rawValue)
    }
    
    private func creerRepresentationNumerique(_ valeur:Int,
                                      _ baseNum:BaseNumerique) -> String {
        return String(valeur, radix:baseNum.rawValue)
    }
    
    func convertir(_ saisie:String,
                   _ origine:BaseNumerique,
                   _ destination:BaseNumerique) -> String {
        
        // Si la chaîne d'entrée est vide, on retourne une chaîne vide
        // pour éviter d'afficher "???" quand le TextField est vide
        if saisie == "" {
            return ""
        }
        
        if let valeur = extractionValeurNumerique(saisie, origine) {
            return creerRepresentationNumerique(valeur, destination)
        } else {
           return "???"
        }
    }
}

struct ContentView: View {
    
    @State private var saisie:String = ""
    @State private var baseCourante = BaseNumerique.decimal
    @ObservedObject var convertisseur = Convertisseur()
    
    var body: some View {
        
        VStack {
            VStack (alignment : .leading) {
                TextField("Votre nombre ... ", text: $saisie)
                    .foregroundColor(Color.red)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                VStack (alignment: .leading) {
                    Text("Binaire : " + convertisseur.convertir(saisie, baseCourante, .binaire))
                        .font(.title)
                        .foregroundColor(.blue)
                    Text("Décimal : " + convertisseur.convertir(saisie, baseCourante, .decimal))
                        .foregroundColor(.blue)
                        .font(.title)
                    Text("Hexadécimal : " + convertisseur.convertir(saisie, baseCourante, .hexadecimal))
                        .foregroundColor(.blue)
                        .font(.title)
                } .padding(.horizontal, 10)
                    .padding(.vertical, 30)
                Spacer()
            
            } .padding()
        }
    }
}

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

C’est nettement plus facile à coder qu’avec ce @$% de Storyboard UIKit !

1 « J'aime »

@Draken Pour ton segment :wink:

struct ContentView: View {
    
@State private var favoriteColor = 0
    
var colors = ["Red", "Green", "Blue"]

var body: some View {
    
        VStack {
            Picker(selection: $favoriteColor, label: Text("What is your favorite color?")) {
                ForEach(0..<colors.count) { index in
                    Text(self.colors[index]).tag(index)
                }
            }.pickerStyle(SegmentedPickerStyle())

            Text("Value: \(colors[favoriteColor])")
        }
    }
}
1 « J'aime »

Oki. Merci je testerais demain. Là j’ai du vrai travail à faire (15 jours de vaisselle a terminer avant la fin du confinement …).

Au fait, puisque tu es plus avancé que moi dans la pratique de SwiftUI, que penses-tu de mon code ?

Bonjour draken,
Je tenais vraiment à vous remercier pour la générosité que vous avez et toutes l’aides que vous m’avez apporté pour réaliser cette application! C’est vraiment rare maintenant dans ce monde que des gens nous aide comme vous le faites il n’y a que sur purple girafe que je vois ça et c’est vraiment super!
Encore vraiment un grand merci à toi pour toute ton aide et tout ce que tu as fait je suis en train de tester l’application en Swift UI mais pour le moment je me suis pas trop attarder dessus j’essaye donc de la faire en Swift! Pour le moment j’ai déjà réussi faire apparaître ce que tu m’a donner dans les label de mon application. Je suis maintenant en train de voir pour que cela marche avec mon UITextField car pour le moment c’est que avec une valeur que l’on a rentré dans le code que cela fonctionne et donc j’essaye de faire pour que ce soit avec la valeur que l’utilisateur rentre dans UITextField! Je pense que je peux peut-être m’aider avec ce que tu as fait un SWIFT UI pour le faire! Dès que je l’ai fait je te mets mon code si tu veut! Encore merci pour ta générosité et pour ton aide :slight_smile:

@Draken plus avancé est en grand mot, je teste comme toi certes, peut-être des autres choses, mais rien de plus que toi. Et en plus, j’ai dû chercher à nouveau pour le picker, car ça fait un moment que je n’ai plus touché à SwiftUI.

J’attends la WWDC pour voir les nouveautés et me remettre sur ce sujet.

Donc pour répondre à ta question, ton code fonctionne et c’est le principal, après il y a peut-être de l’optimisation à faire pour éviter les répétitions.

Apple recommande de faire des petites vues et le plus générique possible pour pouvoir l’implémenter facilement ou on le désire.

@Lucas-BESSON Attention les UITextField ne sont pas des objets SwiftUI, mais UIKit, donc avec des storyboards et ce n’est pas la même procédure pour récupérer le texte saisi.

1 « J'aime »

Merci pour ta réponse, je vais donc faire attention mais c’est vrai que j’y avais pas pensé c’est pas le même framework que l’on appel!
Voila comment pour l’instant j’ai commencer mon code, mais j’ai encore pas mal d’erreur :
import UIKit

enum BaseNumerique:Int {
    case binaire     = 2
    case decimal     = 10
    case hexadecimal = 16
}

func extractionValeurNumerique(_ string:String, _ baseNum:BaseNumerique) -> Int? {
    return Int(string, radix: baseNum.rawValue)
}

func creerRepresentationNumerique(_ valeur:Int, _ baseNum:BaseNumerique) -> String {
    return String(valeur, radix:baseNum.rawValue)
}


class ViewController: UIViewController {

    @IBOutlet weak var ui_inputValueType: UISegmentedControl!
    @IBOutlet weak var ui_inputValueField: UITextField!
    
    @IBOutlet weak var ui_outputDecimalLabel: UILabel!
    @IBOutlet weak var ui_outputBinaireLabel: UILabel!
    @IBOutlet weak var ui_outputHexadecimalLabel: UILabel!
    
    
    func getDecimalValue () -> Int? {
        let inputDécimal:Int?
        if let inputString:String = ui_inputValueField.text,
            let inputInt:Int = Int(inputString) {
            switch ui_inputValueType.selectedSegmentIndex {
            case 0: 
                inputDécimal = inputInt
            case 1: 
                 inputDécimal = creerRepresentationNumerique(inputDécimal, .binaire) // j'ai cette erreur ici :Value of optional type 'Int?' must be unwrapped to a value of type 'Int' , Coalesce using '??' to provide a default when the optional value contains 'nil', Force-unwrap using '!' to abort execution if the optional value contains 'nil'
                
            default:
                inputDécimal = nil
            }
        }else {
            inputDécimal = nil
        }
        
        return inputDécimal
    }
    
    func convert () {
        if let inputDécimal = getDecimalValue() {
            ui_outputBinaireLabel.text = "Binaire : \(strBinaire)" // cette erreur ici : Use of unresolved identifier 'strBinaire'
            ui_outputDecimalLabel.text = "Décimal : \(strDecimal)" // cette erreur ici : Use of unresolved identifier 'strDecimal'
        } else {
            ui_outputBinaireLabel.text = " "
            ui_outputDecimalLabel.text = " "
            
        }
        
    }
    override func viewDidLoad() {
        super.viewDidLoad()

}

    @IBAction func inputValueTypeChanged() {
         convert()
    }
                
    
    @IBAction func inputValueChanged() {
         convert()
    }
}

Je vais essayer de comprendre les erreurs mais pour l’instant je n’ai pas trop trouvé… Je cherche je pense que ça doit être un truc tout bête!
Une fois si jamais j’y arrive que j’aurais fait cette application ensuite je regarderai ce qu’a fait Draken car le code ma l’aire plus simple est beaucoup mieux mais bon je n’ai encore jamais trop fait de SWIFT UI donc ce serait l’occasion de lui mettre! Mais j’aimerais quand même finir cette application avant en SWIFT!
Encore merci à vous!

@ThonyF,
je suis parti de ton code pour arriver à ça :

        VStack {
          Picker(selection: $baseNumerique, label: Text("")) {
            Text("Binaire").tag(BaseNumerique.binaire)
            Text("Décimal").tag(BaseNumerique.decimal)
            Text("Hexadécimal").tag(BaseNumerique.hexadecimal)
          } .pickerStyle(SegmentedPickerStyle())
        } . padding(20)

C’est impressionnant d’utiliser un type de variable personnalisé, pour indexer un Picker. On est bien loin de la complexité d’UIKIT !

1 « J'aime »

Bah, ça entraîne mes muscles pédagogiques et améliore ma connaissance du langage! La meilleure manière de maitriser un sujet est de l’enseigner.

Sinon, tu peux aussi trouver de l’aide sur le forum cocoacafe.fr.

Merci beaucoup, j’irai faire un tour sur ce forum! Merci beaucoup :slight_smile: J’avais une petite question sinon du coup ensuite SWIFT UI, tu as donc pu réaliser un segment, À quel niveau de ton code l’as-tu mis pour qu’il fonctionne? J’ai essayé de le rajouter sur l’application que tu avais partager le code au dessus est j’ai une erreur : Use of unresolved identifier ‹ $baseNumerique ›…
A quel niveau du code l’a tu ajouter? Moi je l’est ajouté à ce niveau :
var body: some View {

        VStack {
            VStack (alignment : .leading) {
                TextField("Votre nombre ... ", text: $saisie)
                    .foregroundColor(Color.red)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                VStack (alignment: .leading) {
                    Text("Binaire : " + convertisseur.convertir(saisie, baseCourante, .binaire))
                        .font(.title)
                        .foregroundColor(.blue)
                    Text("Décimal : " + convertisseur.convertir(saisie, baseCourante, .decimal))
                        .foregroundColor(.blue)
                        .font(.title)
                    Text("Hexadécimal : " + convertisseur.convertir(saisie, baseCourante, .hexadecimal))
                        .foregroundColor(.blue)
                        .font(.title)
                } .padding(.horizontal, 10)
                    .padding(.vertical, 30)
                Spacer()
            
            } .padding()
            VStack {
                Picker(selection: $baseNumerique, label: Text("")) {
                    Text("Binaire").tag(BaseNumerique.binaire)
                    Text("Décimal").tag(BaseNumerique.decimal)
                    Text("Hexadécimal").tag(BaseNumerique.hexadecimal)
               } .pickerStyle(SegmentedPickerStyle())
             } . padding(20)
        }
    }
}

Et ça me met l’erreur… C’est la tout premier fois que je fait du SWIFT UI au moins grâce à toi je m’y serais mis! Tu l’a mis a quel niveau toi?
Encore merci pour ton aide

Voici le code complet. Certaines parties sont un peu différentes de la première version. J’ai aussi raccourci quelques noms de variables pour avoir des lignes plus courtes
.

C’est un prototype en cours d’évolution.

import SwiftUI

enum BaseNumerique:Int {
    case binaire     = 2
    case decimal     = 10
    case hexadecimal = 16
}

// Classe Swift pouvant être utilisée par des contrôles SwiftUI
class Convertisseur:ObservableObject {
    
    private func extractionValeurNumerique(_ string:String,
                                   _ baseNum:BaseNumerique) -> Int? {
        return Int(string, radix: baseNum.rawValue)
    }
    
    private func creerRepresentationNumerique(_ valeur:Int,
                                      _ baseNum:BaseNumerique) -> String {
        return String(valeur, radix:baseNum.rawValue)
    }
    
    func convertir(_ saisie:String,
                   _ origine:BaseNumerique,
                   _ destination:BaseNumerique) -> String {
        
        // Si la chaîne d'entrée est vide, on retourne une chaîne vide
        // pour éviter d'afficher "???" quand le TextField est vide
        if saisie == "" {
            return ""
        }
        
        if let valeur = extractionValeurNumerique(saisie, origine) {
            return creerRepresentationNumerique(valeur, destination)
        } else {
           return "???"
        }
    }
  
  func binaire(_ saisie:String, _ origine:BaseNumerique) -> String {
    return convertir(saisie, origine , .binaire)
  }
  
  func decimal(_ saisie:String, _ origine:BaseNumerique) -> String {
    return convertir(saisie, origine, .decimal)
  }
  
  func hexadecimal(_ saisie:String, _ origine:BaseNumerique) -> String {
    return convertir(saisie, origine, .hexadecimal)
  }
  
}

struct Panneau: View {
    var nom : String
    var contenu : String
    var body: some View {
        Text(nom + " : " + contenu )
            .font(.title)
            .foregroundColor(.blue)
    }
}

struct ContentView: View {
    
  @State private var saisie:String = ""
  @State private var baseNumerique = BaseNumerique.decimal
  @ObservedObject var convert = Convertisseur()

    var body: some View {
      VStack {
            VStack {
              Picker(selection: $baseNumerique, label: Text("")) {
                Text("Binaire").tag(BaseNumerique.binaire)
                Text("Décimal").tag(BaseNumerique.decimal)
                Text("Hexadécimal").tag(BaseNumerique.hexadecimal)
              } .pickerStyle(SegmentedPickerStyle())
            } . padding(20)
        
        VStack (alignment : .leading) {
          TextField("Votre nombre ... ", text: $saisie)
            .foregroundColor(Color.red)
            .textFieldStyle(RoundedBorderTextFieldStyle())
          VStack (alignment: .leading) {
            Panneau(nom:     "Binaire",
                    contenu:  convert.binaire(saisie, baseNumerique))
            Panneau(nom:      "Décimal",
                    contenu:  convert.decimal(saisie, baseNumerique))
            Panneau(nom:      "Hexadécimal",
                    contenu:  convert.hexadecimal(saisie, baseNumerique))
          } .padding(.horizontal, 10)
            .padding(.vertical, 30)
          Spacer()
      } .padding()
    }
  }
}

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

En fait ma question portait surtout sur la manière dont je connecte une class Swift avec SwiftUI. Pour les répétions, je présume que tu parlais du réglage des couleurs des Labels. J’ai ajouté ça au dernier moment, juste pour améliorer l’aspect tristounet noir sur blanc de l’application.

J’ai créé une View Panneau() pour centraliser ça dans la nouvelle version.

Ok, alors après avoir regardé ce détail.

Je pense, à première vue que la property wrapper @ObservedObject sur ta variable convert et le protocol ObservableObject sur ta class Convertisseur ne servent à rien, car il n’y a rien a observé à l’intérieur de celle-ci.

Il y a uniquement des fonctions (appel/renvoi), donc rien ne change.

Pour moi, les property wrapper sont uniquement pour des variables, car il n’y a qu’elle qui change.

Si, tu veux en savoir plus et t’amuser, il y a ce site (Anglais) :
https://www.hackingwithswift.com

Dans l’onglet Learn, il y a 100 days of SwiftUI et SwiftUI by example.