-1

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.

enter image description here

here is an example of what i am asking enter image description here

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

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
        }
    }
}
  • Can you provide a [mre] for interface you’re referencing? Otherwise, it’s going to be challenging to provide a solution that is guaranteed to work with your setup. – jnpdx Mar 18 '22 at 17:39
  • I watched the video, and there is not a small code, I can not send it here –  Mar 18 '22 at 17:42
  • pls share ur code, it will be best to help u –  Mar 18 '22 at 17:52
  • If you have time watch the video, I can't post this code here because it's big –  Mar 18 '22 at 19:18
  • You don't need to post *all* of the code from the video -- just enough for a [mre] as linked to above. It is extremely unlikely for anyone to go and watch the video and reproduce *all* of the code, which as you've mentioned, may be quite a bit, to try to answer this question. – jnpdx Mar 18 '22 at 19:54
  • I edited my question and added the code, please have a look –  Mar 18 '22 at 21:08
  • duplicate, see this SO post: https://stackoverflow.com/questions/60803755/change-color-of-image-icon-in-tabitems-in-swiftui – workingdog support Ukraine Mar 18 '22 at 23:14
  • What duplicate?! These are two different questions. –  Mar 19 '22 at 01:31
  • @jnpdx I wrote the full code –  Mar 19 '22 at 13:01

1 Answers1

0

This code is not built in a way that is easily changeable. The primary issue is that it uses ViewBuilders 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. So, I had to add a somewhat ugly hack of passing the number of child views by hand. I also had to add foregroundColor modifiers explicitly for each label, which is another result of shortcomings of the way the existing code works.

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.

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
        }
    }
}
jnpdx
  • 45,847
  • 6
  • 64
  • 94