#ios #swift #swiftui
#iOS #swift #swiftui
Вопрос:
Я хочу изменить цвет выбранной строки.
Как вы можете видеть, по умолчанию он имеет этот светло-серый цвет.
Я понятия не имею, как это сделать, поскольку я вообще не нашел способа получить доступ к этой строке. Есть ли какой-нибудь способ сделать это?
Демонстрационный код:
struct ContentView: View {
var data = Array(0...20).map { "($0)" }
@State private var selected = 0
var body: some View {
VStack {
Picker("", selection: $selected) {
ForEach(0 ..< data.count) {
Text(data[$0])
}
}
}
}
}
Ответ UIKit также приветствовался бы, поскольку я тоже не нашел способа.
Спасибо!
Комментарии:
1. Это UIPickerView — прочитайте особые соображения в developer.apple.com/documentation/uikit/uipickerview /…
2. К сожалению, его здесь больше нет. Только для iOS с 7 по 13.
3. Вот что я имел в виду — вы не можете изменить выбор по умолчанию в этом элементе управления — создайте пользовательский, если вам не нравится стандартный.
4. Ха-ха, все в порядке, я смог немного настроить его, а также нашел обходной путь для некоторой настройки цвета.
Ответ №1:
Использование пакета Introspect Swift
Это нетривиально сделать в SwiftUI. Однако я провел некоторое исследование.
Базовый класс, стоящий за SwiftUI, Picker
восходит к UIKit
, и это UIPickerView
. Чтобы настроить его поведение, вам нужен доступ к UIPickerView
классу, а документация несколько отсутствует.
И, глядя на другие сообщения на SO, кажется, что способ настройки средства выбора проходит весь путь до ObjC, например, с setValue(_ value: Any?, forKey key: String)
помощью функции. Я даже не смог найти список этих ключей! Однако попытка настроить цвет текста не сработала… Я обнаружил, что могу получить доступ к вложенным представлениям и мог бы решить эту проблему с цветом фона.
Это просто сказать, что я думаю, что лучший способ, который я нашел, — это использовать этот пакет Swift под названием Introspect.
Эти парни (и девушки) проделали действительно потрясающую работу.
-
Сначала установите пакет Swift в свой проект.
-
Затем добавьте код для самоанализа UIPickerView. Это не входит в настройки по умолчанию, поэтому вам нужно создать пользовательское расширение, например:
import Introspect
extension View {
public func introspectUIPickerView(customize: @escaping (UIPickerView) -> ()) -> some View {
return inject(UIKitIntrospectionView(
selector: { introspectionView in
guard let viewHost = Introspect.findViewHost(from: introspectionView) else {
return nil
}
return Introspect.previousSibling(containing: UIPickerView.self, from: viewHost)
},
customize: customize
))
}
}
- Теперь в вашем коде у вас есть доступ к
UIPickerView
базовому классу UIKit вашего SwiftUIPicker
. Добавьте это после средства выбора:
Picker("Flavor", selection: $selectedFlavor) {
ForEach(Flavor.allCases) { flavor in
Text(flavor.rawValue.capitalized)
}
}
.pickerStyle(WheelPickerStyle()
.introspectUIPickerView { picker in
picker.subviews[1].backgroundColor = UIColor.clear
}
Я использовал пример средства выбора из документов Apple и добавил .introspectUIPickerView
модификатор.
Результат: больше не выделяется серый цвет.
Не все будет работать, потому что UIPickerView действительно старый, и некоторые вещи нелегко настроить. Так что, если вы хотите настроить другие параметры, ваш пробег может отличаться 🙂
Здесь красным цветом с :
.introspectUIPickerView { picker in
picker.subviews[1].backgroundColor = UIColor.red
.withAlphaComponent(0.2)
}
Ответ №2:
Здесь был бы способ несколько настроить его. Обходной путь заключается в том, чтобы просто поместить под ним округленный прямоугольник с выбранным вами цветом.
Это не идеально, поскольку наложение непрозрачности по умолчанию серого цвета находится поверх этого цвета, и нужно определить ограничения, основанные на фрейме.
struct ContentView: View {
var data = Array(0...20).map { "($0)" }
@State private var selected = 0
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 5)
.frame(width: UIScreen.main.bounds.size.width-18, height: 32)
.foregroundColor(.green)
Picker("", selection: $selected) {
ForEach(0 ..< data.count) {
Text(data[$0])
}
}
}
}
}
Ответ №3:
Я реализовал чистую реализацию средства выбора SwiftUI
struct MyPickerView<T: Equatable>: View {
let options: [T]
@Binding var selection: T
@SwiftUI.State private var drag: Double = 0
let cellAngles = 20.0
let presentation: (T) -> String
init(_ options: [T], _ selection: Binding<T>, _ presentation: @escaping (T) -> String = {"($0)"}) {
self.options = options
self._selection = selection
self.presentation = presentation
}
var body: some View {
let selectedIndex = getSelectedIndex()
HStack {
ZStack {
ForEach(0..<options.count, id: .self) { index in
let item: T = options[index]
MyPickerCell(title: presentation(item), angle: Double(index - selectedIndex) * cellAngles drag)
}
Color.gray.opacity(0.1)
.frame(height: 1)
.offset(y: 15)
Color.gray.opacity(0.1)
.frame(height: 1)
.offset(y: -15)
}
.frame(width: 100, height: 200)
.gesture(
DragGesture()
.onChanged { gesture in
drag = gesture.translation.height
}
.onEnded { _ in
var newIndex = selectedIndex - Int(round(drag / cellAngles))
if newIndex < 0 {
newIndex = 0
}
if newIndex >= options.count - 1 {
newIndex = options.count - 1
}
selection = options[newIndex]
drag = 0
}
)
}
}
func getSelectedIndex() -> Int {
return options.firstIndex(of: selection) ?? 0
}
}
private struct MyPickerCell: View {
let title: String
let angle: Double
var body: some View {
if abs(angle) > 90 {
EmptyView()
}
else {
let sinValue = sin(rad(angle))
let cosValue = cos(rad(angle))
Text(title)
.foregroundColor(.primary)
.scaleEffect(y: cosValue)
.offset(y: sinValue * 85)
.opacity(abs(angle) < 5 ? 1.0 : cosValue/2)
}
}
func rad(_ number: Double) -> Double {
return number * .pi / 180
}
}
Пример использования:
struct ContentView: View {
@State var selectedNumber: Int = 3
var body: some View {
MyPickerView(Array(0...20), $selectedNumber)
}
}
Ответ №4:
Более простое использование:
.accentColor (ваш цвет)
Picker("", selection: $selected) {
ForEach(0 ..< data.count) {
Text(data[$0])
}
}
.accentColor(.black)