ARTICLE AD BOX
Here are some possible ways to fix.
1. Apply maxHeight: .infinity to the Button and .fixedSize to the HStack
A .frame modifier can be applied to the Button with maxHeight: .infinity and alignment: .bottom. Then, .fixedSize(horizontal: false, vertical: true) is aplied to the HStack. This stops the button from consuming any more space than it actually needs to.
HStack(spacing: 8) { // default alignment TextField("Chat", text: $textInput, axis: .vertical) // ... Button(action: send) { // ... } .frame(maxHeight: .infinity, alignment: .bottom) } .fixedSize(horizontal: false, vertical: true) // + padding and other modifiersThis is perhaps the simplest solution in this particular case. However, the modifier .fixedSize can sometimes have undesirable side effects, especially when text is concerned. So this technique may not be suitable in other cases.
2. Use an .alignmentGuide
For an alignment guide to work, you need to know the height of either the text or the button. Since the height of the text is variable, this technique is probably only useful if the height of the button is known or can be guessed. The alignment guide is then applied to the other element in the HStack, this being the text.
If you have to guess the size of the button then you might like to give it a .frame with minWidth and minHeight, before adding the background. Apple's guidelines for buttons recommend a minimum size of 44 points.
Note that the alignment used for the .alignmentGuide must match the alignment used for the HStack. Since the alignment guide is being applied to the text and not the button, .bottom should be used. This then takes effect for elements inside the HStack that don't have their own alignment guide (in other words, the button):
private let minButtonSize: CGFloat = 44 HStack(alignment: .bottom, spacing: 8) { TextField("Chat", text: $textInput, axis: .vertical) // + previous modifiers .alignmentGuide(.bottom) { dim in dim.height + max(0, (minButtonSize - dim.height) / 2) } Button(action: send) { Image(systemName: "paperplane.fill") // ... .frame(minWidth: minButtonSize, minHeight: minButtonSize) .background(Color(.label), in: .circle) } } // + padding and other modifiers3. Use a placeholder to reserve space, then show the button as an overlay
If you know the size of the button (re. point 2), you can also use a placeholder to reserve space for it. Then show the actual button as an overlay over the HStack, with alignment .bottomTrailing.
HStack(spacing: 8) { // default alignment TextField("Chat", text: $textInput, axis: .vertical) // ... // Placeholder Color.clear .frame(width: minButtonSize, height: minButtonSize) } .overlay(alignment: .bottomTrailing) { Button(action: send) { // ... .frame(minWidth: minButtonSize, minHeight: minButtonSize) .background(Color(.label), in: .circle) } } // + padding and other modifiersAlternatively, a copy of the Button can be used as the placeholder. This works too and it's a good way to get the size right, but you need to take a few extra measures:
be sure to apply .hidden() and .accessibilityHidden(true) to the copy, to keep it hidden apply .disabled(true) too, to make quite sure it cannot receive focus.4. Use .matchedGeometryEffect to determine the position for the button
Another way to solve is to use .matchedGeometryEffect. This technique works when you don't know the size of the button and don't want to guess it.
The source for the matched geometry can be the HStack itself, before padding is applied. The button is matched to the position of the source using anchor: .bottomTrailing. @Namespace private var ns HStack(spacing: 8) { // default alignment TextField("Chat", text: $textInput, axis: .vertical) // ... Button(action: send) { // ... } .matchedGeometryEffect( id: 0, in: ns, properties: .position, anchor: .bottomTrailing, isSource: false ) } .matchedGeometryEffect( id: 0, in: ns, anchor: .bottomTrailing, isSource: true ) // + padding and other modifiersAll techniques give a similar result:


