How to make view the size of another view in SwiftUI

后端 未结 7 1889
忘掉有多难
忘掉有多难 2020-11-29 18:31

I\'m trying to recreate a portion of the Twitter iOS app to learn SwiftUI and am wondering how to dynamically change the width of one view to be the width of another view. I

相关标签:
7条回答
  • 2020-11-29 18:55

    Let me modestly suggest a slight modification of this bright answer: Version without using preferences:

    import SwiftUI
    
    extension HorizontalAlignment {
        private enum UnderlineLeading: AlignmentID {
            static func defaultValue(in d: ViewDimensions) -> CGFloat {
                return d[.leading]
            }
        }
    
        static let underlineLeading = HorizontalAlignment(UnderlineLeading.self)
    }
    
    
    struct GridViewHeader : View {
    
        @State private var activeIdx: Int = 0
        @State private var w: [CGFloat] = [0, 0, 0, 0]
    
        var body: some View {
            return VStack(alignment: .underlineLeading) {
                HStack {
                    Text("Tweets").modifier(MagicStuff(activeIdx: $activeIdx, widths: $w, idx: 0))
                    Spacer()
                    Text("Tweets & Replies").modifier(MagicStuff(activeIdx: $activeIdx, widths: $w, idx: 1))
                    Spacer()
                    Text("Media").modifier(MagicStuff(activeIdx: $activeIdx, widths: $w, idx: 2))
                    Spacer()
                    Text("Likes").modifier(MagicStuff(activeIdx: $activeIdx, widths: $w, idx: 3))
                    }
                    .frame(height: 50)
                    .padding(.horizontal, 10)
                Rectangle()
                    .alignmentGuide(.underlineLeading) { d in d[.leading]  }
                    .frame(width: w[activeIdx],  height: 2)
                    .animation(.linear)
            }
        }
    }
    
    struct MagicStuff: ViewModifier {
        @Binding var activeIdx: Int
        @Binding var widths: [CGFloat]
        let idx: Int
    
        func body(content: Content) -> some View {
            var w: CGFloat = 0
            return Group {
                if activeIdx == idx {
                    content.alignmentGuide(.underlineLeading) { d in
                        w = d.width
                        return d[.leading]
                    }.onTapGesture { self.activeIdx = self.idx }.onAppear(perform: {self.widths[self.idx] = w})
    
                } else {
                    content.onTapGesture { self.activeIdx = self.idx }
                }
            }
        }
    }
    

    Version using preferences and GeometryReader:

    import SwiftUI
    
    extension HorizontalAlignment {
        private enum UnderlineLeading: AlignmentID {
            static func defaultValue(in d: ViewDimensions) -> CGFloat {
                return d[.leading]
            }
        }
    
        static let underlineLeading = HorizontalAlignment(UnderlineLeading.self)
    }
    
    struct WidthPreferenceKey: PreferenceKey {
        static var defaultValue = CGFloat(0)
    
        static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
            value = nextValue()
        }
    
        typealias Value = CGFloat
    }
    
    
    struct GridViewHeader : View {
    
        @State private var activeIdx: Int = 0
        @State private var w: [CGFloat] = [0, 0, 0, 0]
    
        var body: some View {
            return VStack(alignment: .underlineLeading) {
                HStack {
                    Text("Tweets")
                        .modifier(MagicStuff(activeIdx: $activeIdx, idx: 0, widthStorage: $w))
    
                    Spacer()
    
                    Text("Tweets & Replies")
                        .modifier(MagicStuff(activeIdx: $activeIdx, idx: 1, widthStorage: $w))
    
                    Spacer()
    
                    Text("Media")
                        .modifier(MagicStuff(activeIdx: $activeIdx, idx: 2, widthStorage: $w))
    
                    Spacer()
    
                    Text("Likes")
                        .modifier(MagicStuff(activeIdx: $activeIdx, idx: 3, widthStorage: $w))
    
                    }
                    .frame(height: 50)
                    .padding(.horizontal, 10)
                Rectangle()
                    .frame(width: w[activeIdx],  height: 2)
                    .animation(.linear)
            }
        }
    }
    
    struct MagicStuff: ViewModifier {
        @Binding var activeIdx: Int
        let idx: Int
        @Binding var widthStorage: [CGFloat]
    
        func body(content: Content) -> some View {
            Group {
    
                if activeIdx == idx {
                    content.background(GeometryReader { geometry in
                        return Color.clear.preference(key: WidthPreferenceKey.self, value: geometry.size.width)
                    })
                    .alignmentGuide(.underlineLeading) { d in
                        return d[.leading]
                    }.onTapGesture { self.activeIdx = self.idx }
                        .onPreferenceChange(WidthPreferenceKey.self, perform: { self.widthStorage[self.idx] = $0 })
    
    
                } else {
                    content.onTapGesture { self.activeIdx = self.idx }.onPreferenceChange(WidthPreferenceKey.self, perform: { self.widthStorage[self.idx] = $0 })
                }
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题