最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ios - How to make 4 views in HStack align 1st to the left, 2nd at ⅓ horizontal distance, 3rd at ⅔ horizontal distance and 4th at

programmeradmin1浏览0评论

There is a similar question here on stackoverflow for how to do it when they are all the same size but not when they are different sizes,I have 4 views in a HStack that I want to evenly space out but since the Picker is larger than the other 3 its impossible to do it using Spacer and since there are now 4 instead of 3 I cannot use alignment anymore either. What is the solution for ensuring they are all evenly spaced out?

import SwiftUI



    
    
    @EnvironmentObject var captureDelegate: CaptureDelegate
    let images = ["person.slash.fill", "person.fill", "person.2.fill", "person.2.fill", "person.2.fill"]
    @Binding var timerPress: Bool
    
    private var rotationAngle: Angle {
            switch captureDelegate.orientationLast {
            case .landscapeRight:
                return .degrees(90)
            case .landscapeLeft:
                return .degrees(-90)  // Fixes the upside-down issue
            default:
                return .degrees(0)
            }
        }
    
    var body: some View {
        HStack() {
            
            Image(systemName: "gearshape")
                .resizable()
                .frame(width: 25, height: 25)
                .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                .disabled(captureDelegate.cameraPressed)
                .rotationEffect(rotationAngle)
                .frame(maxWidth: .infinity, alignment: .leading)
                .onTapGesture {
                    if !captureDelegate.cameraPressed {
                        
                    }
                    
                }
            
            Image(systemName: "timer")
                .resizable()
                .frame(width: 25, height: 25)
                .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                .disabled(captureDelegate.cameraPressed)
                .rotationEffect(rotationAngle)
                .frame(maxWidth: .infinity)
                .onTapGesture {
                    if !captureDelegate.cameraPressed {
                        timerPress.toggle()
                    }
                    
                }
            

            
            Image(systemName: "timer")
                .resizable()
                .frame(width: 25, height: 25)
                .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                .disabled(captureDelegate.cameraPressed)
                .rotationEffect(rotationAngle)
                .frame(maxWidth: .infinity)
                .onTapGesture {
                    if !captureDelegate.cameraPressed {
                        timerPress.toggle()
                    }
                    
                }
            
              


            Picker("Pick a number of people", selection: $captureDelegate.userSelectedNumber) {
                    ForEach(0...4, id: \.self) { i in
                        HStack(spacing: 70) {
                            Image(systemName: self.images[i])
                                .resizable()
                                .frame(width: 20, height: 20)
                                .rotationEffect(rotationAngle)
 
                            Text("\(i)")
                                .font(.system(size: 42))
                                .rotationEffect(rotationAngle)
                            
                        }.tag(i)
                            .rotationEffect(rotationAngle)
                        
                    }
                    
                }
                .tint(.white)
                .clipped()
                .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                .disabled(captureDelegate.cameraPressed)
                .rotationEffect(rotationAngle)
                .animation(.easeInOut(duration: 0.5), value: rotationAngle)
                .frame(maxWidth: .infinity, alignment: .trailing)

        }
        .font(.system(size: 24))
        .padding([.leading,.trailing], 15)
    }
}

I have tried using Spacer but it doesnt work because they are not all the same size, when I had 3 views I had success using alignment leading center and trailing but now that there are 4 views it no longer works.

Here I have attempted to use a ZStack to position them putting the two center views in the same HStack:

 ZStack {
            
            HStack {
                Image(systemName: "gearshape")
                    .resizable()
                    .frame(width: 25, height: 25)
                    .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                    .disabled(captureDelegate.cameraPressed)
                    .rotationEffect(rotationAngle)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .onTapGesture {
                        if !captureDelegate.cameraPressed {
                            
                        }
                        
                    }
                
                
                
                Picker("Pick a number of people", selection: $captureDelegate.userSelectedNumber) {
                    ForEach(0...4, id: \.self) { i in
                        HStack(spacing: 70) {
                            Image(systemName: self.images[i])
                                .resizable()
                                .frame(width: 20, height: 20)
                                .rotationEffect(rotationAngle)
                            
                            Text("\(i)")
                                .font(.system(size: 42))
                                .rotationEffect(rotationAngle)
                            
                        }.tag(i)
                            .rotationEffect(rotationAngle)
                        
                    }
                    
                }
                .tint(.white)
                .clipped()
                .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                .disabled(captureDelegate.cameraPressed)
                .rotationEffect(rotationAngle)
                .animation(.easeInOut(duration: 0.5), value: rotationAngle)
                .frame(maxWidth: .infinity, alignment: .trailing)
            }
            
            HStack {
               
                Text("\(captureDelegate.totalPhotosToTake)")
                    .font(.system(size: 15))
                    .foregroundStyle(.white)
                    .fontWeight(.bold)
                    .padding(.horizontal, 9)
                    .padding(.vertical, 5)
                    .overlay(
                        RoundedRectangle(cornerRadius: 5).stroke(.white, lineWidth: 2)
                    )
                    .frame(maxWidth: .infinity, alignment: .center)
                
                Image(systemName: "timer")
                    .resizable()
                    .frame(width: 25, height: 25)
                    .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                    .disabled(captureDelegate.cameraPressed)
                    .rotationEffect(rotationAngle)
                    .onTapGesture {
                        if !captureDelegate.cameraPressed {
                            timerPress.toggle()
                        }
                        
                    }
                    .frame(maxWidth: .infinity, alignment: .center)

              
            }
        
        }
        .font(.system(size: 24))
        .padding([.leading,.trailing], 15)

And changing the two center views alignments from center to trialing and leading produces this affect:

There is a similar question here on stackoverflow for how to do it when they are all the same size but not when they are different sizes,I have 4 views in a HStack that I want to evenly space out but since the Picker is larger than the other 3 its impossible to do it using Spacer and since there are now 4 instead of 3 I cannot use alignment anymore either. What is the solution for ensuring they are all evenly spaced out?

import SwiftUI



    
    
    @EnvironmentObject var captureDelegate: CaptureDelegate
    let images = ["person.slash.fill", "person.fill", "person.2.fill", "person.2.fill", "person.2.fill"]
    @Binding var timerPress: Bool
    
    private var rotationAngle: Angle {
            switch captureDelegate.orientationLast {
            case .landscapeRight:
                return .degrees(90)
            case .landscapeLeft:
                return .degrees(-90)  // Fixes the upside-down issue
            default:
                return .degrees(0)
            }
        }
    
    var body: some View {
        HStack() {
            
            Image(systemName: "gearshape")
                .resizable()
                .frame(width: 25, height: 25)
                .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                .disabled(captureDelegate.cameraPressed)
                .rotationEffect(rotationAngle)
                .frame(maxWidth: .infinity, alignment: .leading)
                .onTapGesture {
                    if !captureDelegate.cameraPressed {
                        
                    }
                    
                }
            
            Image(systemName: "timer")
                .resizable()
                .frame(width: 25, height: 25)
                .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                .disabled(captureDelegate.cameraPressed)
                .rotationEffect(rotationAngle)
                .frame(maxWidth: .infinity)
                .onTapGesture {
                    if !captureDelegate.cameraPressed {
                        timerPress.toggle()
                    }
                    
                }
            

            
            Image(systemName: "timer")
                .resizable()
                .frame(width: 25, height: 25)
                .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                .disabled(captureDelegate.cameraPressed)
                .rotationEffect(rotationAngle)
                .frame(maxWidth: .infinity)
                .onTapGesture {
                    if !captureDelegate.cameraPressed {
                        timerPress.toggle()
                    }
                    
                }
            
              


            Picker("Pick a number of people", selection: $captureDelegate.userSelectedNumber) {
                    ForEach(0...4, id: \.self) { i in
                        HStack(spacing: 70) {
                            Image(systemName: self.images[i])
                                .resizable()
                                .frame(width: 20, height: 20)
                                .rotationEffect(rotationAngle)
 
                            Text("\(i)")
                                .font(.system(size: 42))
                                .rotationEffect(rotationAngle)
                            
                        }.tag(i)
                            .rotationEffect(rotationAngle)
                        
                    }
                    
                }
                .tint(.white)
                .clipped()
                .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                .disabled(captureDelegate.cameraPressed)
                .rotationEffect(rotationAngle)
                .animation(.easeInOut(duration: 0.5), value: rotationAngle)
                .frame(maxWidth: .infinity, alignment: .trailing)

        }
        .font(.system(size: 24))
        .padding([.leading,.trailing], 15)
    }
}

I have tried using Spacer but it doesnt work because they are not all the same size, when I had 3 views I had success using alignment leading center and trailing but now that there are 4 views it no longer works.

Here I have attempted to use a ZStack to position them putting the two center views in the same HStack:

 ZStack {
            
            HStack {
                Image(systemName: "gearshape")
                    .resizable()
                    .frame(width: 25, height: 25)
                    .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                    .disabled(captureDelegate.cameraPressed)
                    .rotationEffect(rotationAngle)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .onTapGesture {
                        if !captureDelegate.cameraPressed {
                            
                        }
                        
                    }
                
                
                
                Picker("Pick a number of people", selection: $captureDelegate.userSelectedNumber) {
                    ForEach(0...4, id: \.self) { i in
                        HStack(spacing: 70) {
                            Image(systemName: self.images[i])
                                .resizable()
                                .frame(width: 20, height: 20)
                                .rotationEffect(rotationAngle)
                            
                            Text("\(i)")
                                .font(.system(size: 42))
                                .rotationEffect(rotationAngle)
                            
                        }.tag(i)
                            .rotationEffect(rotationAngle)
                        
                    }
                    
                }
                .tint(.white)
                .clipped()
                .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                .disabled(captureDelegate.cameraPressed)
                .rotationEffect(rotationAngle)
                .animation(.easeInOut(duration: 0.5), value: rotationAngle)
                .frame(maxWidth: .infinity, alignment: .trailing)
            }
            
            HStack {
               
                Text("\(captureDelegate.totalPhotosToTake)")
                    .font(.system(size: 15))
                    .foregroundStyle(.white)
                    .fontWeight(.bold)
                    .padding(.horizontal, 9)
                    .padding(.vertical, 5)
                    .overlay(
                        RoundedRectangle(cornerRadius: 5).stroke(.white, lineWidth: 2)
                    )
                    .frame(maxWidth: .infinity, alignment: .center)
                
                Image(systemName: "timer")
                    .resizable()
                    .frame(width: 25, height: 25)
                    .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                    .disabled(captureDelegate.cameraPressed)
                    .rotationEffect(rotationAngle)
                    .onTapGesture {
                        if !captureDelegate.cameraPressed {
                            timerPress.toggle()
                        }
                        
                    }
                    .frame(maxWidth: .infinity, alignment: .center)

              
            }
        
        }
        .font(.system(size: 24))
        .padding([.leading,.trailing], 15)

And changing the two center views alignments from center to trialing and leading produces this affect:

Share Improve this question edited Mar 28 at 16:07 john smith asked Mar 28 at 14:12 john smithjohn smith 1516 bronze badges 3
  • Do you really want to space out the items evenly, that is, with an equal amount of space between each of them? If so, the duplicate post should help. However, if you actually want to position item 1 on the left, item 2 at ⅓ horizontal distance with center alignment, item 3 at ⅔ horizontal distance with center alignment and item 4 on the right then this is quite a different problem. If this is the case, you might want to edit the title and the question to make it clearer that this is the intended result. An image of what you are trying to achieve might help too. – Benzy Neez Commented Mar 28 at 15:47
  • Thank you for the advice, I have change the title – john smith Commented Mar 28 at 15:51
  • OK, so a ZStack might be one way to do it, or wrapping in a GeometryReader, or a custom Layout. The post How do I center these images in a straight line inside my HStack in SwiftUI might help. – Benzy Neez Commented Mar 28 at 15:53
Add a comment  | 

2 Answers 2

Reset to default 0

Using a ZStack is certainly one way to lay out the items. I am not sure if your solution using Spacer is precise (especially since an HStack adds spacing between the items), but it might still be fit for purpose.

If you want a precise solution then I think the width of the screen needs to be known.

  • When the width of the screen is known, the width of the middle items can be enlarged to two-thirds of the screen width (with center alignment). Then, these items can be aligned to the leading or trailing edge of the ZStack using a frame with maxWidth: .infinity (as for the first and last items).

  • Your items are spread across the full width of the screen. So one way of finding the screen width is to use .containerRelativeFrame. The container in this case is the screen.

  • Since the alignment of the middle items is based on the full screen width, you should not apply any horizontal padding to the container (now the ZStack). To keep the first and last items away from the sides, apply padding to these items individually.

ZStack {
    Image(systemName: "gearshape")
        .resizable()
        .scaledToFit()
        .frame(width: 25, height: 25)
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(.leading, 15)
    
    Image(systemName: "timer")
        .resizable()
        .scaledToFit()
        .frame(width: 25, height: 25)
        .containerRelativeFrame(.horizontal) { screenWidth, _ in
            2 * screenWidth / 3
        }
        .frame(maxWidth: .infinity, alignment: .leading)

    Image(systemName: "timer")
        .resizable()
        .scaledToFit()
        .frame(width: 25, height: 25)
        .containerRelativeFrame(.horizontal) { screenWidth, _ in
            2 * screenWidth / 3
        }
        .frame(maxWidth: .infinity, alignment: .trailing)
    
    Picker("Pick a number of people", selection: $userSelectedNumber) {
        ForEach(Array(images.enumerated()), id: \.offset) { i, imageName in
            HStack(spacing: 70) {
                Image(systemName: imageName)
                    .resizable()
                    .scaledToFit()
                    .frame(width: 20, height: 20)
                
                Text("\(i)")
                    .font(.system(size: 42))
            }
            .tag(i)
        }
    }
    .tint(.white)
    .clipped()
    .frame(maxWidth: .infinity, alignment: .trailing)
    .padding(.trailing, 15)
}
.foregroundStyle(.white)
.font(.system(size: 24))

Visually, this still doesn't look quite right to me. But you now have more control of the spacing, so the layout can be tweaked, either by changing the fraction of the screen width for the middle items, or by changing the padding. For example:

  • change the leading padding on the first item from 15 to 5
  • remove the trailing padding from the last item (the Picker)
  • add horizontal padding of 10 to the ZStack

The ZStack attempt can be made to work with the use of Spacers to achieve the 1/3 and 2/3 positioning

            HStack {
                Spacer()
                Spacer()
                Spacer()
                
                Text("\(captureDelegate.totalPhotosToTake)")
                    .font(.system(size: 15))
                    .foregroundStyle(.white)
                    .fontWeight(.bold)
                    .padding(.horizontal, 9)
                    .padding(.vertical, 5)
                    .overlay(
                        RoundedRectangle(cornerRadius: 5).stroke(.white, lineWidth: 2)
                    )
                   // .frame(maxWidth: .infinity, alignment: .center)
                Spacer()
                Spacer()
                Image(systemName: "timer")
                    .resizable()
                    .frame(width: 25, height: 25)
                    .foregroundStyle(captureDelegate.cameraPressed ? Color(white: 0.4) : .white  )
                    .disabled(captureDelegate.cameraPressed)
                    .rotationEffect(rotationAngle)
                    .onTapGesture {
                        if !captureDelegate.cameraPressed {
                            timerPress.toggle()
                        }
                        
                    }
                   // .frame(maxWidth: .infinity, alignment: .center)
                Spacer()
                Spacer()
                Spacer()
            }

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论