[英]Change color on click on icon image (SwiftUI)
I can't figure out how to change the color of the image icon after clicking on it, for example, if the window is active, then the color of the icon is blue, if not, then it's gray.我无法弄清楚如何在单击后更改图像图标的颜色,例如,如果 window 处于活动状态,则图标的颜色为蓝色,否则为灰色。
here is an example of what i am asking这是我要问的一个例子
If you know the solution please help me如果您知道解决方案,请帮助我
Here is the full code This code is fully working, you can check it out这是完整的代码这段代码完全可以工作,你可以检查一下
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 TabPreference键
struct TabPreferenceKey: PreferenceKey {
static var defaultValue: CGRect = .init()
static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
value = nextValue()
}
}
pageLabel - pageView页面标签 - 页面视图
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
}
}
OffsetPage偏移页
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
}
}
}
This code is not built in a way that is easily changeable.此代码不是以易于更改的方式构建的。 The primary issue is that it uses
ViewBuilder
s for the labels and pages, but our (not Apple's) SwiftUI code doesn't have insight into how many elements get passed into a ViewBuilder
like this.主要问题是它使用
ViewBuilder
作为标签和页面,但我们的(不是 Apple 的)SwiftUI 代码无法了解有多少元素像这样传递到ViewBuilder
中。 So, I had to add a somewhat ugly hack of passing the number of child views by hand.所以,我不得不添加一个有点难看的 hack 来手动传递子视图的数量。 I also had to add
foregroundColor
modifiers explicitly for each label, which is another result of shortcomings of the way the existing code works.我还必须为每个 label 显式添加
foregroundColor
修饰符,这是现有代码工作方式存在缺陷的另一个结果。
The original code's currentSelection
logic was completely broken (ie didn't function at all), but was easily fixable once explicitly passing the number of child elements.原始代码的
currentSelection
逻辑被完全破坏(即根本没有 function),但是一旦显式传递子元素的数量就很容易修复。
See updated code including inline comments of where changes were made.查看更新的代码,包括对更改位置的内联注释。
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.