SwiftUI: Кнопка без метки/строки в инициализаторе, но со стилем кнопки

#ios #swift #button #swiftui

#iOS #быстрый #кнопка #свифтуи

Вопрос:

В SwiftUI есть несколько Button инициализаторов, но все они требуют либо a String , либо some View в качестве параметра наряду с action .

Однако внешний вид кнопки также можно настроить с помощью ButtonStyle s, которые могут добавлять к ней пользовательские представления.

Давайте рассмотрим кнопку копирования со следующим значком:

Копирование символов SF

Стиль, который я создал для кнопки, выглядит следующим образом:

 struct CopyButtonStyle: ButtonStyle {   init() {}   func makeBody(configuration: Configuration) -gt; some View {  let copyIconSize: CGFloat = 24  return Image(systemName: "doc.on.doc")  .renderingMode(.template)  .resizable()  .frame(width: copyIconSize, height: copyIconSize)  .accessibilityIdentifier("copy_button")  .opacity(configuration.isPressed ? 0.5 : 1)  } }  

Это работает отлично, однако я должен инициализировать Button пустую строку на сайте вызова:

 Button("") {  print("copy") } .buttonStyle(CopyButtonStyle())  

Итак, вопрос в том, как я могу избавиться от пустой строки в параметре инициализации кнопки?

Потенциальное Решение

Мне удалось создать простое расширение, которое выполняет необходимую мне работу:

 import SwiftUI  extension Button where Label == Text {  init(_ action: @escaping () -gt; Void) {  self.init("", action: action)  } }  

Позвоните на сайт:

 Button() { // Note: no initializer parameter  print("copy") } .buttonStyle(CopyButtonStyle())  

Но любопытно, правильно ли я использую Button структуру, и для этого уже есть вариант использования, чтобы я мог избавиться от этого расширения.

Ответ №1:

Более простой способ, чем создание ButtonStyle конфигурации, — это передать метку напрямую:

 Button {  print("copy") } label: {  Label("Copy", systemImage: "doc.on.doc")  .labelStyle(.iconOnly) }  

Это также дает некоторые преимущества:

  1. По умолчанию кнопка синего цвета указывает на то, что ее можно нажать
  2. Никакого странного растягивания изображения, которое у вас сейчас есть
  3. Нет необходимости реализовывать, как изменяется непрозрачность при нажатии

Вы также можете преобразовать это в собственное представление:

 struct CopyButton: View {  let action: () -gt; Void   var body: some View {  Button(action: action) {  Label("Copy", systemImage: "doc.on.doc")  .labelStyle(.iconOnly)  }  } }  

Называется так:

 CopyButton {  print("copy") }  

Что в целом выглядит намного чище.

Комментарии:

1. У меня есть палитра других ButtonStyle s, которые могут не использовать символы SF или использовать текст, так что это было бы слишком нестандартным решением. Обратите внимание, что я хотел бы избавиться от параметра инициализатора, а не от поддержки стиля или создать полностью пользовательский тип. Но спасибо за ваш ответ!

Ответ №2:

Вот правильный способ для того, что вы пытаетесь сделать, вам не нужно создавать новый стиль кнопок для каждого вида кнопок, вы можете создать только одну и повторно использовать ее для любых других кнопок, которые вы хотите. Также я решил вашу проблему с растяжением изображения .scaledToFit() .

 struct CustomButtonView: View {    let imageString: String  let size: CGFloat  let identifier: String  let action: (() -gt; Void)?    init(imageString: String, size: CGFloat = 24.0, identifier: String = String(), action: (() -gt; Void)? = nil) {  self.imageString = imageString  self.size = size  self.identifier = identifier  self.action = action  }   var body: some View {    return Button(action: { action?() } , label: {    Image(systemName: imageString)  .renderingMode(.template)  .resizable()  .scaledToFit()  .frame(width: size, height: size)  .accessibilityIdentifier(identifier)    })  .buttonStyle(CustomButtonStyle())   }   }  struct CustomButtonStyle: ButtonStyle {   func makeBody(configuration: Configuration) -gt; some View {  return configuration.label  .opacity(configuration.isPressed ? 0.5 : 1.0)  .scaleEffect(configuration.isPressed ? 0.95 : 1.0)  } }  

пример использования:

 struct ContentView: View {  var body: some View {    CustomButtonView(imageString: "doc.on.doc", identifier: "copy_button", action: { print("copy") })   } }  

Комментарии:

1. «Также я решил проблему растяжения вашего изображения» -gt; в этом нет необходимости, значок, который я использую, является пользовательским, с ним нет проблем с растяжением. Значок, представленный здесь, предназначен только для справки, вопрос только об API (программировании)

2. Я просто вижу код, о котором идет речь, в вопросе есть проблема растяжения! Ps: это мой стиль ответа, который я пытаюсь решить или переработать любую проблему, о которой идет речь. Ты можешь сделать это сам.

3. Никаких проблем, и спасибо, что решили ее и предложили свою версию. Для этого вопроса я только что заменил свой пользовательский значок (который идеально подходит) на значок по умолчанию, так что по-прежнему ясно, о чем идет речь.

4. Я уже ответил вам в своем последнем комментарии о моем стиле ответа! Так что не имеет значения, что вы смело говорите о том, о чем идет речь! И еще я упомянул: Ты делаешь это сам! Это значит, делай свой стиль! Я делаю свой стиль.

Ответ №3:

Вы можете использовать EmptyView для этикетки, например

 Button(action: { // Note: no initializer parameter  print("copy")  }, label: { EmptyView() })  .buttonStyle(CopyButtonStyle())  

но упаковка его в пользовательский тип кнопки (как показано в другом ответе) более предпочтительна с точки зрения повторного использования и удобочитаемости кода.

Комментарии:

1. Тем не менее, нужно добавить EmptyView сайт для звонков. Я бы просто предпочел избавиться от него. Похоже, расширение и метод инициализации являются здесь простейшими решениями.