[英]Change color on click on icon image (SwiftUI)
我无法弄清楚如何在单击后更改图像图标的颜色,例如,如果 window 处于活动状态,则图标的颜色为蓝色,否则为灰色。
如果您知道解决方案,请帮助我
这是完整的代码这段代码完全可以工作,你可以检查一下
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)
}
}
选项卡视图
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
}
}
}
}
}
TabPreference键
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
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
}
}
}
此代码不是以易于更改的方式构建的。 主要问题是它使用ViewBuilder
作为标签和页面,但我们的(不是 Apple 的)SwiftUI 代码无法了解有多少元素像这样传递到ViewBuilder
中。 所以,我不得不添加一个有点难看的 hack 来手动传递子视图的数量。 我还必须为每个 label 显式添加foregroundColor
修饰符,这是现有代码工作方式存在缺陷的另一个结果。
原始代码的currentSelection
逻辑被完全破坏(即根本没有 function),但是一旦显式传递子元素的数量就很容易修复。
查看更新的代码,包括对更改位置的内联注释。
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
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.