如何在SwiftUI视图中重构几乎相同的条件代码

62o28rlo  于 2023-01-29  发布在  Swift
关注(0)|答案(1)|浏览(190)

我将以下代码用于输入字段,用户可以在其中输入API令牌。默认情况下,它显示为SecureField,但用户可以单击“眼睛”图标并更改为常规字段

struct PasswordField : View {
        @Binding var value : String
        @State var showToken: Bool = false
        
        var body : some View {
            if(showToken){
                ZStack(alignment: .trailing) {
                    TextField(text: $value) {
                        Text("API Token").bold().padding(EdgeInsets(top: 0, leading: 44, bottom: 0, trailing: 0))
                     }
                    Image(systemName: "eye").onTapGesture {
                        showToken.toggle()
                    }.padding(.trailing,5)
                }
            } else {
                ZStack(alignment: .trailing) {
                    SecureField(text: $value) {
                        Text("API Token").bold().padding(EdgeInsets(top: 0, leading: 44, bottom: 0, trailing: 0))
                     }
                    Image(systemName: "eye.slash").onTapGesture {
                        showToken.toggle()
                    }.padding(.trailing,5)
                }
            }
        }
        
    }

它可以像我写的那样工作,但是我讨厌if和else块中几乎完全相同的代码。唯一的区别是一个是TextField,另一个是SecureField,以及显示的图标,eye与eye.slash。我如何重构它以减少代码重复?
我尝试创建另一个只包含重复代码的视图,但是我不知道如何指定是使用TextField还是SecureField。

protocol MyProtocol {}
extension SecureField : MyProtocol {}
extension TextField: MyProtocol {}

struct InnerView<T> : View where T:MyProtocol {
  @Binding var value : String
  @Binding var showToken: Bool
  var icon : String

  var body : some View {
    ZStack(alignment: .trailing) {
      T(text: $value) {
        Text("API Token").bold().padding(EdgeInsets(top: 0, leading: 44, bottom: 0, trailing: 0))
      }
      Image(systemName: icon).onTapGesture {
        showToken.toggle()
      }.padding(.trailing,5)
    }
  }
}

问题是MyProtocol没有正确的init,所以我添加了与TextField和SecureField中相同的init

protocol MyProtocol {
  init(text: Binding<String>, prompt: Text?, @ViewBuilder label: () -> Label)
}

但是Label本身是一个泛型,这就是我陷入死胡同的地方。
我对Swift相当陌生,但我确实有很多编程经验。虽然我来自使用PHP,Javascript等的Web开发世界。

wgeznvg7

wgeznvg71#

使用上面lorem ipsum链接到的答案中的示例,我得出了下面的解决方案,该解决方案非常符合我的喜好

struct PasswordFieldLabel : View {
  var body:some View {
    Text("API Token").bold().padding(EdgeInsets(top: 0, leading: 44, bottom: 0, trailing: 0))
  }
}
    
struct PasswordField : View {
  @Binding var value : String
  @State var showToken: Bool = false
        
  var body : some View {
    ZStack(alignment: .trailing) {
      if(showToken){
        TextField(text: $value){
          PasswordFieldLabel()
        }
      } else {
        SecureField(text: $value){
          PasswordFieldLabel()
        }
      }
      Button(action: {
          showToken.toggle()
        }, label: {
          Image(systemName: showToken ? "eye" : "eye.slash")
        }
      ).padding(.trailing, 7).buttonStyle(.plain)
    }
  }
}

相关问题