Не могу понять, как изменить цвет значка изображения после нажатия на него, например, если окно активно, то цвет значка синий, если нет, то серый.
вот пример того, что я спрашиваю
Если вы знаете решение, пожалуйста, помогите мне
Вот полный код Этот код полностью рабочий, можете проверить
import SwiftUI
struct AllLinesView: View {
@State var currentSelection: Int = 0
var body: some View {
PagerTabView(tint: .white, selection: $currentSelection) {
Image(systemName: "bolt.fill")
.pageLabel()
Image(systemName: "flame")
.pageLabel()
Image(systemName: "person.fill")
.pageLabel()
} content: {
Color.red
.pageView(ignoresSafeArea: true, edges: .bottom)
Color.green
.pageView(ignoresSafeArea: true, edges: .bottom)
Color.yellow
.pageView(ignoresSafeArea: true, edges: .bottom)
}
.ignoresSafeArea(.container, edges: .bottom)
}
}
TabView
struct PagerTabView<Content: View, Label: View>: View {
var content: Content
var label: Label
var tint: Color
@Binding var selection: Int
init(tint:Color,selection: Binding<Int>,@ViewBuilder labels: @escaping ()->Label,@ViewBuilder content: @escaping ()->Content) {
self.content = content()
self.label = labels()
self.tint = tint
self._selection = selection
}
@State var offset: CGFloat = 0
@State var maxTabs: CGFloat = 0
@State var tabOffset: CGFloat = 0
var body: some View {
VStack(alignment: .leading,spacing: 0) {
HStack(spacing: 0) {
label
}
.overlay(
HStack(spacing: 0) {
ForEach(0..<Int(maxTabs), id: \.self) { index in
Rectangle()
.fill(Color.black.opacity(0.01))
.onTapGesture {
let newOffset = CGFloat(index) * getScreenBounds().width
self.offset = newOffset
}
}
}
)
.foregroundColor(tint)
Capsule()
.fill(tint)
.frame(width: maxTabs == 0 ? 0 : (getScreenBounds().width / maxTabs), height: 2)
.padding(.top, 10)
.frame(maxWidth: .infinity, alignment: .leading)
.offset(x: tabOffset)
OffsetPageTabView(selection: $selection,offset: $offset) {
HStack(spacing: 0) {
content
}
.overlay(
GeometryReader { proxy in
Color.clear
.preference(key: TabPreferenceKey.self, value: proxy.frame(in: .global))
}
)
.onPreferenceChange(TabPreferenceKey.self) { proxy in
let minX = -proxy.minX
let maxWidth = proxy.width
let screenWidth = getScreenBounds().width
let maxTabs = (maxWidth / screenWidth).rounded()
let progress = minX / screenWidth
let tabOffset = progress * (screenWidth / maxTabs)
self.tabOffset = tabOffset
self.maxTabs = maxTabs
}
}
}
}
}
TabPreferenceKey
struct TabPreferenceKey: PreferenceKey {
static var defaultValue: CGRect = .init()
static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
value = nextValue()
}
}
pageLabel — просмотр страницы
extension View {
//IMAGE
func pageLabel()->some View {
self
.frame(maxWidth: .infinity, alignment: .center)
}
//PAGE
func pageView(ignoresSafeArea: Bool = false, edges: Edge.Set = [])->some View {
self
.frame(width: getScreenBounds().width, alignment: .center)
.ignoresSafeArea(ignoresSafeArea ? .container : .init(), edges: edges)
}
func getScreenBounds()->CGRect {
return UIScreen.main.bounds
}
}
Смещение страницы
struct OffsetPageTabView<Content: View>: UIViewRepresentable {
var content: Content
@Binding var offset: CGFloat
@Binding var selection: Int
func makeCoordinator() -> Coordinator {
return OffsetPageTabView.Coordinator(parent: self)
}
init(selection: Binding<Int>,offset: Binding<CGFloat>, @ViewBuilder content: @escaping ()->Content) {
self.content = content()
self._offset = offset
self._selection = selection
}
func makeUIView(context: Context) -> UIScrollView {
let scrollview = UIScrollView()
let hostview = UIHostingController(rootView: content)
hostview.view.translatesAutoresizingMaskIntoConstraints = false
let constraints = [
hostview.view.topAnchor.constraint(equalTo: scrollview.topAnchor),
hostview.view.leadingAnchor.constraint(equalTo: scrollview.leadingAnchor),
hostview.view.trailingAnchor.constraint(equalTo: scrollview.trailingAnchor),
hostview.view.bottomAnchor.constraint(equalTo: scrollview.bottomAnchor),
hostview.view.heightAnchor.constraint(equalTo: scrollview.heightAnchor)
]
scrollview.addSubview(hostview.view)
scrollview.addConstraints(constraints)
scrollview.isPagingEnabled = true
scrollview.showsVerticalScrollIndicator = false
scrollview.showsHorizontalScrollIndicator = false
scrollview.delegate = context.coordinator
return scrollview
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
let currentOffset = uiView.contentOffset.x
if currentOffset != offset {
print("updating")
uiView.setContentOffset(CGPoint(x: offset, y: 0), animated: true)
}
}
class Coordinator: NSObject, UIScrollViewDelegate {
var parent: OffsetPageTabView
init(parent: OffsetPageTabView) {
self.parent = parent
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset.x
let maxSize = scrollView.contentSize.width
let currentSelection = (offset / maxSize).rounded()
parent.selection = Int(currentSelection)
parent.offset = offset
}
}
}
Смотрел видео, а там не маленький код, не могу сюда отправить
Пожалуйста, поделитесь своим кодом, это будет лучше всего помочь вам
Если у вас есть время, посмотрите видео, я не могу разместить этот код здесь, потому что он большой.
Вам не нужно публиковать все кода из видео — достаточно минимальный воспроизводимый пример, как указано выше. Крайне маловероятно, что кто-то пойдет и посмотрит видео и воспроизведет все кода, которого, как вы упомянули, может быть совсем немного, чтобы попытаться ответить на этот вопрос.
Я отредактировал свой вопрос и добавил код, пожалуйста, посмотрите
дубликат, см. этот пост SO: stackoverflow.com/questions/60803755/…
Какой дубликат?! Это два разных вопроса.
@jnpdx я написал полный код
Этот код не построен таким образом, чтобы его можно было легко изменить. Основная проблема заключается в том, что он использует ViewBuilder
для меток и страниц, но наш (не Apple) код SwiftUI не имеет представления о том, сколько элементов передается в такой ViewBuilder
. Итак, мне пришлось добавить несколько уродливый хак для передачи числа дочерних просмотров вручную. Мне также пришлось добавить модификаторы foregroundColor
явно для каждой метки, что является еще одним результатом недостатков работы существующего кода.
Логика currentSelection
исходного кода была полностью нарушена (т. е. вообще не функционировала), но ее легко исправить после явной передачи количества дочерних элементов.
См. обновленный код, включая встроенные комментарии о том, где были внесены изменения.
struct AllLinesView: View {
@State var currentSelection: Int = 0
var body: some View {
PagerTabView(tint: .white, selection: $currentSelection, children: 3) { //<-- Here
Image(systemName: "bolt.fill")
.pageLabel()
.foregroundColor(currentSelection == 0 ? .blue : .white) //<-- Here
Image(systemName: "flame")
.pageLabel()
.foregroundColor(currentSelection == 1 ? .blue : .white) //<-- Here
Image(systemName: "person.fill")
.pageLabel()
.foregroundColor(currentSelection == 2 ? .blue : .white) //<-- Here
} content: {
Color.red
.pageView(ignoresSafeArea: true, edges: .bottom)
Color.green
.pageView(ignoresSafeArea: true, edges: .bottom)
Color.yellow
.pageView(ignoresSafeArea: true, edges: .bottom)
}
.ignoresSafeArea(.container, edges: .bottom)
.onChange(of: currentSelection) { newValue in
print(newValue)
}
}
}
struct PagerTabView<Content: View, Label: View>: View {
var content: Content
var label: Label
var tint: Color
var children: Int //<-- Here
@Binding var selection: Int
init(tint:Color,selection: Binding<Int>,children: Int, @ViewBuilder labels: @escaping ()->Label,@ViewBuilder content: @escaping ()->Content) {
self.children = children
self.content = content()
self.label = labels()
self.tint = tint
self._selection = selection
}
@State var offset: CGFloat = 0
@State var maxTabs: CGFloat = 0
@State var tabOffset: CGFloat = 0
var body: some View {
VStack(alignment: .leading,spacing: 0) {
HStack(spacing: 0) {
label
}
.overlay(
HStack(spacing: 0) {
ForEach(0..<Int(maxTabs), id: \.self) { index in
Rectangle()
.fill(Color.black.opacity(0.01))
.onTapGesture {
let newOffset = CGFloat(index) * getScreenBounds().width
self.offset = newOffset
}
}
}
)
.foregroundColor(tint)
Capsule()
.fill(tint)
.frame(width: maxTabs == 0 ? 0 : (getScreenBounds().width / maxTabs), height: 2)
.padding(.top, 10)
.frame(maxWidth: .infinity, alignment: .leading)
.offset(x: tabOffset)
OffsetPageTabView(selection: $selection,offset: $offset, children: children) { //<-- Here
HStack(spacing: 0) {
content
}
.overlay(
GeometryReader { proxy in
Color.clear
.preference(key: TabPreferenceKey.self, value: proxy.frame(in: .global))
}
)
.onPreferenceChange(TabPreferenceKey.self) { proxy in
let minX = -proxy.minX
let maxWidth = proxy.width
let screenWidth = getScreenBounds().width
let maxTabs = (maxWidth / screenWidth).rounded()
let progress = minX / screenWidth
let tabOffset = progress * (screenWidth / maxTabs)
self.tabOffset = tabOffset
self.maxTabs = maxTabs
}
}
}
}
}
struct TabPreferenceKey: PreferenceKey {
static var defaultValue: CGRect = .init()
static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
value = nextValue()
}
}
extension View {
//IMAGE
func pageLabel()->some View {
self
.frame(maxWidth: .infinity, alignment: .center)
}
//PAGE
func pageView(ignoresSafeArea: Bool = false, edges: Edge.Set = [])->some View {
self
.frame(width: getScreenBounds().width, alignment: .center)
.ignoresSafeArea(ignoresSafeArea ? .container : .init(), edges: edges)
}
func getScreenBounds()->CGRect {
return UIScreen.main.bounds
}
}
struct OffsetPageTabView<Content: View>: UIViewRepresentable {
var content: Content
@Binding var offset: CGFloat
@Binding var selection: Int
var children: Int //<-- Here
func makeCoordinator() -> Coordinator {
return OffsetPageTabView.Coordinator(parent: self)
}
init(selection: Binding<Int>,offset: Binding<CGFloat>, children: Int, @ViewBuilder content: @escaping ()->Content) {
self.content = content()
self._offset = offset
self._selection = selection
self.children = children
}
func makeUIView(context: Context) -> UIScrollView {
let scrollview = UIScrollView()
let hostview = UIHostingController(rootView: content)
hostview.view.translatesAutoresizingMaskIntoConstraints = false
let constraints = [
hostview.view.topAnchor.constraint(equalTo: scrollview.topAnchor),
hostview.view.leadingAnchor.constraint(equalTo: scrollview.leadingAnchor),
hostview.view.trailingAnchor.constraint(equalTo: scrollview.trailingAnchor),
hostview.view.bottomAnchor.constraint(equalTo: scrollview.bottomAnchor),
hostview.view.heightAnchor.constraint(equalTo: scrollview.heightAnchor)
]
scrollview.addSubview(hostview.view)
scrollview.addConstraints(constraints)
scrollview.isPagingEnabled = true
scrollview.showsVerticalScrollIndicator = false
scrollview.showsHorizontalScrollIndicator = false
scrollview.delegate = context.coordinator
return scrollview
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
let currentOffset = uiView.contentOffset.x
if currentOffset != offset {
//print("updating")
uiView.setContentOffset(CGPoint(x: offset, y: 0), animated: true)
}
}
class Coordinator: NSObject, UIScrollViewDelegate {
var parent: OffsetPageTabView
init(parent: OffsetPageTabView) {
self.parent = parent
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset.x
let maxSize = scrollView.contentSize.width
let currentSelection = (offset / maxSize) * CGFloat(parent.children) //<-- Here
print("max: ", maxSize, offset)
print("Set selection to: ", Int(currentSelection), currentSelection)
parent.selection = Int(currentSelection)
parent.offset = offset
}
}
}
Можете ли вы предоставить минимальный воспроизводимый пример для интерфейса, на который вы ссылаетесь? В противном случае будет сложно предоставить решение, которое гарантированно будет работать с вашей настройкой.