>source

첫 번째를 제외한 모든 UI 열로 구성된 항목 목록이 있습니다.수평으로(즉, 콘텐츠가 요구하는만큼 짧거나 길다). 일관된 크기의 첫 번째 열로이를 달성하기 위해 프레임 수정 자 너비를 설정할 수 있다는 것을 알고 있지만 원하는 동작을 얻을 수있는 더 좋고 유연한 방법이 있기를 바랐습니다. 그 이유는 솔루션이 사용자의 표시 크기 나 열의 실제 최대 콘텐츠 너비를 고려하도록 최적화되어 있지 않다고 생각하기 때문입니다. 즉, 디스플레이 크기를 가장 크게 설정하면 너비 설정이 충분히 넓지 않거나, 더 작거나 일반 디스플레이 크기에서 불필요하게 넓어집니다.

이것이 나의 현재 최선의 시도입니다.

       GeometryReader { geometry in
            
        VStack {
        
            HStack {
                HStack {
                    Text("9am")
                    
                    Image(systemName: "cloud.drizzle").font(Font.title2)
                        .offset(y: 4)
                }.padding(.all)
                .background(Color.blue.opacity(0.2))
                .cornerRadius(16)
                            
                VStack {
                    HStack {
                        Text("Summary")
                            .padding(.trailing, 4)
                            .background(Color.white)
                            .layoutPriority(1)
                        VStack {
                            Spacer()
                            Divider()
                            Spacer()
                        }
                        VStack {
                            Text("12°")
                            Text("25%")
                                .foregroundColor(Color.black)
                                .background(Color.white)
                        }.offset(y: -6)
                        Spacer()
                    }.frame(width: geometry.size.width/1.5)
                }
                
                Spacer()
            }
            
            HStack {
                HStack {
                    Text("10am")
                        .customFont(.subheadline)
                    
                    Image(systemName: "cloud.drizzle").font(Font.title2)
                        .offset(y: 4)
                        .opacity(0)
                }
                .padding(.horizontal)
                .padding(.vertical,4)
                .background(Color.blue.opacity(0.2))
                .cornerRadius(16)
                            
                VStack {
                    HStack {
                        ZStack {
                            Text("Mostly cloudy")
                                .customFont(.body)
                                .padding(.trailing, 4)
                                .background(Color.white)
                                .opacity(0)
                            VStack {
                                Spacer()
                                Divider()
                                Spacer()
                            }
                        }
                        VStack {
                            Text("13°")
                            Text("25%")
                                .foregroundColor(Color.black)
                                .background(Color.white)
                        }.offset(y: -6)
                        Spacer()
                    }.frame(width: geometry.size.width/1.75)
                }
                
                Spacer()
            
            } 
      } 
}

나를 위해 이것은 다음과 같이 보입니다.

아시다시피 오전 10 시가 오전 9 시보 다 약간 넓습니다. 가능한 한 가깝게 유지하기 위해 불투명도는 0이지만 구름 아이콘도 포함합니다. 이상적으로는 투명한 구름 아이콘없이 오전 10 시가 오전 9시와 같은 크기가됩니다. 더 일반적으로 말하면이 열에서 가장 넓은 HStack이 식별되고 너비가 다른 모든 열에 적용된다는 것이 의미가 있습니다. 위의 코드는 데모 목적으로 정적입니다. 행 컬렉션을 반복하여 렌더링되는 뷰입니다.

  • 답변 # 1

    frame (.maxWidth : .infinity) 수정 자와 같은 동적 프레임 수정자를 사용하여 프레임이 동적 인 경우에도 전체 프레임을 채우도록 뷰를 확장 할 수 있습니다. 다음은 시작하는 데 도움이되는 예입니다.

    struct CustomContent: View {
        
        var body: some View {
            VStack {
                VStack {
                    CustomRow(timeText: "9am", systemIcon: "cloud.drizzle", centerText: "Summary", temperature: "12°", percent: "25%")
                    CustomRow(timeText: "10am", systemIcon: nil, centerText: nil, temperature: "13°", percent: "25%")
                }
                
                VStack {
                    CustomRow(timeText: "9am", systemIcon: "cloud.drizzle", centerText: "Summary", temperature: "12°", percent: "25%")
                    CustomRow(timeText: "10am", systemIcon: nil, centerText: nil, temperature: "13°", percent: "25%")
                }
                .frame(width: 300)
                
            }
        }
    }
    struct CustomContent_Previews: PreviewProvider {
        static var previews: some View {
            CustomContent()
        }
    }
    struct CustomRow: View {
        
        let timeText: String
        let systemIcon: String?
        let centerText: String?
        let temperature: String
        let percent: String
        
        var body: some View {
            HStack {
                
                //Left column
                HStack(alignment: .center) {
                    Text(timeText)
                    
                    if let icon = systemIcon {
                        Image(systemName: icon)
                            .font(.title2)
                    }
                }
                .padding(.all)
                .frame(width: 105, height: 60)
                .background(Color.blue.opacity(0.2))
                .cornerRadius(16)
                // Center column
                ZStack(alignment: .leading) {
                    Capsule()
                        .fill(Color.black.opacity(0.3))
                        .frame(height: 0.5)
                    if let text = centerText {
                        Text(text)
                            .lineLimit(1)
                            .background(Color.white)
                    }
                }
                
                // Right column
                VStack {
                    Text(temperature)
                    Text(percent)
                        .foregroundColor(Color.black)
                }
                
            }
        }
    }
    
    

  • 답변 # 2

    https://www.wooji-juice.com/blog/stupid-swiftui-tricks-equal-sizes.html의 안내에 따라이 작업을 수행했습니다.

    이것은 너비가 가장 높은 값으로 설정된 모든 행에서 가로로 동일한 크기인지 확인하려는 UI 부분입니다.

               HStack {
                    VStack {
                        Spacer()
                        Text("9am")
                        Spacer()
                    }
                }.frame(minWidth: self.maximumSubViewWidth)
                .overlay(DetermineWidth())
    
    

    위에 포함 된 스택에는 OnPreferenceChange 수정자가 있습니다.

              .onPreferenceChange(DetermineWidth.Key.self) {
                    if $0 > maximumSubViewWidth {
                        maximumSubViewWidth = $0
                    }
                }
    
    

    마법은 여기에서 발생합니다.

    struct MaximumWidthPreferenceKey: PreferenceKey
    {
        static var defaultValue: CGFloat = 0
        static func reduce(value: inout CGFloat, nextValue: () -> CGFloat)
        {
            value = max(value, nextValue())
        }
    }
    struct DetermineWidth: View
    {
        typealias Key = MaximumWidthPreferenceKey
        var body: some View
        {
            GeometryReader
            {
                proxy in
                Color.clear
                    .anchorPreference(key: Key.self, value: .bounds)
                    {
                        anchor in proxy[anchor].size.width
                    }
            }
        }
    }
    
    

    상단의 링크는 각각의 목적을 가장 잘 설명합니다.

    MaximumWidthPreferenceKey

    This defines a new key, sets the default to zero, and as new values get added, takes the widest

    결정 폭

    This view is just an empty (Color.clear) background, but with our new preference set to its width. We’ll get back to that clear background part in a moment, but first: there are several ways to set preferences, here, we’re using anchorPreference. Why?

    Well, anchorPreference has “No Overview Available” so I don’t actually have a good answer for that, other than it seems to be more reliable in practice. Yeah, cargo-cult code. Whee! I have a hunch that, what with it taking a block and all, SwiftUI can re-run that block to get an updated value when there are changes that affect layout.

    Another hope I have is that this stuff will get better documented, so that we can better understand how these different types are intended to be used and new SwiftUI developers can get on board without spending all their time on Stack Overflow or reading blog posts like this one.

    Anyway, an anchor is a token that represents a dimension or location in a view, but it doesn’t give you the value directly, you have to cash it in with a GeometryProxy to get the actual value, so, that’s what we did — to get the value, you subscript a proxy with it, so proxy[anchor].size.width gets us what we want, when anchor is .bounds (which is the value we passed in to the anchorPreference call). It’s kind of twisted, but it gets the job done.

    maximumSubViewWidth는 각 하위보기가 참조하는 maximumSubViewWidth가 항상 최신 최대 값임을 보장하기 위해 상위보기에서 전달 된 바인딩 변수입니다.

               ForEach(self.items) { item, in
                    ItemSubview(maximumSubViewWidth: $maximumSubViewWidth, item: item)
                }
    
    

    이것의 한 가지 문제는 최대 너비로 크기가 조정되는 모든 UI가있는 전체 행에 원하지 않는 미묘하지만 여전히 눈에 띄는 애니메이션이 있다는 것입니다. 이 문제를 해결하기 위해 내가 한 것은 명시 적 트리거 후 .default로 다시 전환하는 것으로 시작하지 않는 부모 컨테이너에 애니메이션 수정자를 추가하는 것입니다.

    .animation(self.initialised ? .default : nil)
    
    

    사용자가 행과 명시 적으로 상호 작용 한 후 self.initialised를 true로 설정했습니다 (내 경우에는 행을 탭하여 추가 정보를 표시 함). 이렇게하면 초기 애니메이션이 잘못 발생하지 않고 애니메이션이 정상으로 돌아갑니다. 그 후. 내 원래 시도는 .onAppear 수정 자에서 초기화 상태를 전환하여 변경이 자동으로 이루어 지지만 초기 모양 이후에 크기 조정이 발생할 수 있다고 가정하고 있기 때문에 작동하지 않았습니다.

    주목해야 할 또 다른 사항은 (이 솔루션이 작동하지만 최선의 방법이 아니라는 것을 암시 할 수 있음) 모든 항목 또는 크기 조정이 필요한 항목에 대해 콘솔에서이 메시지가 반복되는 것을 볼 수 있다는 것입니다 (불분명하지만 총 경고 수 = 항목 수) :

    Bound preference MaximumWidthPreferenceKey tried to update multiple times per frame.

    누구든지이 경고를 피하면서 위의 사항을 달성 할 수있는 방법을 생각할 수 있다면 좋습니다!

    최신 정보: 나는 위의 것을 알아 냈다.

    이 문제를 해결하지 않고 이후 화면을 방문 할 때 열이 계속 넓어지는 것을 보았 기 때문에 실제로 중요한 변화입니다.

    뷰에는 false로 설정되고 .onAppeared 내에서 true가되는 새로운 widthDetermined @State 변수가 있습니다.

    그런 다음 widthDetermined가 false 즉 설정되지 않은 경우 뷰의 너비 만 결정합니다. https://fivestars.blog/swiftui/conditional-modifiers.html에서 제안 된 조건부 수정자를 사용하여이를 수행합니다.

       func `if`<Content: View>(_ conditional: Bool, content: (Self) -> Content) -> TupleView<(Self?, Content?)> {
        if conditional { return TupleView((nil, content(self))) }
        else { return TupleView((self, nil)) }
    }
    
    

    보기에서 :

               .if(!self.widthDetermined) {
                    $0.overlay(DetermineWidth())
                }
    
    

관련 자료

  • 이전 python - 전역 변수의 값을 대체하는 더 좋은 방법
  • 다음 WordPress 테이블의 SQL 쿼리로 JavaScript를 통해 양식 필드를 채우는 방법