I have the following HStack:
HStack {
Text("Test text")
Spacer()
Picker("Test picker", selection: $selectedAccessType, content: {
Text("option1").tag(0)
Text("option2").tag(1)
})
.pickerStyle(SegmentedPickerStyle())
}
It shows up like this:
I would like to have it so the segment control's width (the Picket) is just enough wide so that the text specifying the options in each segment can fit, and not expand more than needed taking way more width than necessary. So I want its width to hug to the text of the options and not more than that.
I have put that Spacer() in there but it gets compressed all the way by the Picker.
How can I make it so the Picker's width is sized so fit its content and not more than that? I want the "Test text" on the left, space in the middle and the segment control on the right. I don't want the segment control to take the entire width of the cell.
try something like this to put "...the "Test text" on the left, space in the middle and the segment control on the right.":
HStack {
Text("Test text")
Spacer()
Picker("Test picker", selection: $selectedAccessType, content: {
Text("option1").tag(0)
Text("option2").tag(1)
})
.pickerStyle(SegmentedPickerStyle())
.fixedSize(horizontal: true, vertical: false) // <-- here
// .fixedSize() // <-- or this
}
Related
I've build a ScrollView which contains 0-3 images and a multiline text field in a VStack. I also added a ScrollViewReader inside the scrollview and use it to scroll to the bottom of the text field upon certain events (user starts typing, image collection changes).
The point is: sometimes it works, sometimes it doesn't. When it does not work I realized, that when I scroll a little bit by hand and then try again (e.g. typing) it works.
Not sure if this is relevant, but ImageOrPlaceholderComponent first shows a placeholder as long as the image within currentEntryImages is nil, and the image after that (both states imply a change to currentEntryImages and should thus result in scrolling to the bottom of the text field).
NavigationStack {
ScrollView {
ScrollViewReader { scrollview in
VStack {
// Attached images.
AnyLayout(VStackLayout(spacing: 2.5)) {
ForEach(values: currentEntryImages) { entryImage in
ImageOrPlaceholderComponent(image: entryImage)
.clipped()
}
}
// Text field for the entry with toolbar.
TextField("...", text: $entryDTO.text, axis: .vertical)
.id(entryTextFieldAnchor)
.multilineTextAlignment(.leading)
.padding()
.focused($mainTextFieldFocused)
.onAppear { mainTextFieldFocused = true }
// Scroll to the bottom of the text field, when the user is typing ...
.onChange(of: entryDTO.text) { _ in
withAnimation {
scrollview.scrollTo(entryTextFieldAnchor, anchor: .bottom)
}
}
// ... or the entry images have changed.
.onChange(of: currentEntryImages) { _ in
withAnimation {
scrollview.scrollTo(entryTextFieldAnchor, anchor: .bottom)
}
}
}
}
}
}
I want to create a complication rendered by a SwiftUI View that contains a label and a timer value.
I want the label to be on the complication background layer, and the timer value to be on the complication foreground layer so that they get tinted separately.
I would like this line of text, comprised of 2 parts, to be centered.
The trouble is, when using Text.DateStyle.timer, the Text behaves differently within a complication vs in a normal view.
In a normal view the Text frame behaves as any other text, only taking the space it needs.
When displayed in a complication, the Text frame expands to fill all the space it can, and the text within is left aligned.
This makes it so I cannot find a way to center the group of 2 Texts.
I tried a somewhat hacky approach with infinite spacers to try to steal the extra space from the Text that has the expanding frame. This works to center the content, but it causes the Text to truncate.
HStack {
Text("T:")
.foregroundColor(.accentColor)
Text(Date(), style: .timer)
.complicationForeground()
}
HStack {
Spacer()
.frame(maxWidth: .infinity)
HStack {
Text("T:")
.foregroundColor(.accentColor)
Text(Date(), style: .timer)
.complicationForeground()
}
Spacer()
.frame(maxWidth: .infinity)
}
A normal preview:
A preview of rendering within complication:
CLKComplicationTemplateGraphicExtraLargeCircularView(
ExtraLargeStack()
)
.previewContext(faceColor: .multicolor)
Edit to show full code
import ClockKit
import SwiftUI
struct ExtraLargeStack: View {
var body: some View {
VStack(alignment: .center) {
HStack {
Text("T:")
.foregroundColor(.accentColor)
Text(Date(), style: .timer)
.complicationForeground()
}
HStack {
Spacer()
.frame(maxWidth: .infinity)
HStack {
Text("T:")
.foregroundColor(.accentColor)
Text(Date(), style: .timer)
.complicationForeground()
}
Spacer()
.frame(maxWidth: .infinity)
}
}
.font(.system(size: 18, weight: .regular))
.lineLimit(1)
}
}
struct ExtraLargeStack_Previews: PreviewProvider {
static var previews: some View {
/// Preview normal view
// ExtraLargeStack()
/// Preview as Complication
CLKComplicationTemplateGraphicExtraLargeCircularView(
ExtraLargeStack()
)
.previewContext(faceColor: .multicolor)
}
}
Edit: Another partial solution
Based on suggestions from #Yrb, an overlay provides a partial solution that may be good enough for my use case.
The following does not fully center the 2 part line, but it is pretty close.
HStack {
// Use placeholder text to create a view with the appropriate size for _most_ timer values that I need to support
Text("L: 00:00 ").hidden()
}
.overlay(
// overlay the real content, which is constrained to the frame created by the hidden placeholder.
HStack(spacing: 5) {
Text("L:")
.foregroundColor(.accentColor)
Text(Date() - 3599, style: .timer)
.complicationForeground()
}
)
So, I figured out what the issue with aligning Text(Date(), style: .timer) is. The timer format from hours on down. The document give this as an example: 2:59 36:59:01. It appears that .timer reserves all of the possible space it needs and then is formatted on that possible space, not the space actually used. There does not appear to be any way to change this behavior, even if your goal is a 5 minute countdown timer.
I think you need to consider slight UI change. I did find that you can change the alignment of the displayed Text with a .timer by using .multilineTextAlignment(), but that is about all you can do. The following code demonstrates this:
struct ExtraLargeStack: View {
var body: some View {
// I removed (alignment: .center) as it is redundant. VStacks default to center
VStack {
// I put the negative spacing to tighten the T: with the timer
VStack(spacing: -6) {
Text("T:")
.foregroundColor(.accentColor)
Text(Date(), style: .timer)
// If you center with .multilineTextAlignment the timer
// will be centered
.multilineTextAlignment(.center)
.complicationForeground()
}
HStack {
HStack {
Text(Date(), style: .timer)
.multilineTextAlignment(.center)
.complicationForeground()
.overlay(
Text("T:")
.foregroundColor(.accentColor)
// This offset would need to be computed
.offset(x: -30, y: 0)
)
}
}
}
.font(.system(size: 18, weight: .regular))
}
}
I left the second timer as an HStack, but I put your Text("T") as an .overlay() with a .offset(). I don't particularly like this as it will be fragile if you attempt to adjust the offset for the additional time units, but if you have a limited range, it may work well enough. Also, if you use .monospaced on the timer text, the computation should be a linear amount.
Very novice to the app development game. I am trying to put this toolbar above the .decimalPad and I cannot get this large gap to go away.
VStack {
Rectangle()
.foregroundColor(Color(UIColor.systemBackground))
.frame(height: 35)
.overlay {
HStack {
Spacer()
Button(action: {
isTextFieldFocused = false
}) { Text("Done")}
}
.offset(y: -3)
.padding(.trailing)
}
.opacity(isTextFieldFocused ? 1 : 0)
.ignoresSafeArea(.keyboard) //This makes sure the bottom tab bar stays below the keyboard.
}
I initially thought it was something in another view causing the spacing, but I managed to parse through the views in the canvas and it does it regardless.
Here is what I'd like it to look like, for reference.
What I want
To add a Button onto your keyboard, you use a .toolbar with the locations to .keyboard like this:
TextField("Enter Text", text: $text)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button(action: {
isTextFieldFocused = false
}) { Text("Done")}
// If you want it leading, then use a Spacer() after
Spacer()
}
}
You were overthinking it by adding the Rectangle. This is why we look for minimal reproducible examples. We can dial in the fix for your specific code.
Given a basic List with Text, how can i make the whole "cell" from left side of the screen to right, tappable in a List, not just the "Hello world" text?
List {
VStack {
Text("Hello world")
}
.contentShape(Rectangle())
.onTapGesture {
print("Tapped cell") // This only triggers when you directly tap the Text
}
}
Add a Button and entire cell is tappable now:
VStack {
Button(action: {
print("Tapped")
}) {
Text("Hello world")
}
}
Actually, all you really need to do is make sure the entire cell is filled with content. Changing the VStack to an HStack and adding a Spacer() will give you what you want.
List {
HStack {
Text("Hello world")
Spacer()
}
.contentShape(Rectangle())
.onTapGesture {
print("Tapped cell") // This triggers when you tap anywhere in the cell
}
}
I am developing a MacOS app using SwiftUI and would like a Picker to shrink to only show its label and content, and a Spacer to take up the rest of the view, but I'm not sure what the correct way to do this. The code looks something like this:
HStack{
Text("hello")
Text("more text")
Spacer()
Picker(selection: self.$stuff, label: Text("some stuff:")) {
ForEach(0 ..< self.getStuff().count) {
Text(self.getStuff()[$0])
}
}
}
and the Picker is expanded to take up as much room as possible. I would prefer that the picker take up the minimum amount of space and the Spacer to take up the remaining space.
Edit: photo:
You can use .scaledToFit to fit it to content as below
Picker(selection: self.$stuff, label: Text("some stuff:")) {
ForEach(0 ..< self.getStuff().count) {
Text(self.getStuff()[$0])
}
}
.scaledToFit()
// .frame(width: 160) // < alternate approach - give explicit desired width