Set width of TextField in SwiftUI - swiftui

When using TextField, I want to set it on a fixed width regardless to the shown text length, e.g. field width for "123456789", even if the content is only "1".
TextField("0", text: $username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.fixedSize(horizontal: true, vertical: false)
.frame(width: 120)
I didn't find anything to set the width. Using frame only affect the surrounding frame, but I want to set the width of the TextField itself. So the visible width should be wider than the content of it.
Any idea?

Just move the .frame() modifier before applying the .textFieldStyle() modifier.
import SwiftUI
struct ContentView: View {
#State private var username = ""
var body: some View {
TextField("0", text: $username)
.frame(width: 120)
.textFieldStyle(RoundedBorderTextFieldStyle())
.fixedSize(horizontal: true, vertical: false)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
There you go!

Related

Why is Text onTapGesture not working for widened frame in SwiftUI?

I have this testing file that is not working how I expect it to.
import SwiftUI
struct SwiftUIView: View {
#State var boolTest = false
var nums = ["1","2","3","4","5","6","7"]
var body: some View {
VStack {
ForEach(nums, id: \.self) { num in
Text("\(num)")
.frame(width: 400)
.font(.system(size: 70))
.foregroundColor(boolTest ? .red : .green)
.onTapGesture {
boolTest.toggle()
}
}
}
}
}
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
SwiftUIView()
}
}
When I tap on a number, the foreground color changes as expected. However, I want to be able to tap on the areas left and right of the Text("\(num)") so I expanded the frame modifier to test. However the color only changes when I tap directly on the number or text.
How do I tap on the space left or right of the number and have it change colors instead of it doing nothing?
The system treats the "invisible" (ie doesn't have visible drawn content) part of the view as unresponsive unless you set a contentShape on it.
//...
.frame(width: 400)
.contentShape(Rectangle())
//...

How to use scaleEffect with TextEditor in SwiftUI?

I want to use scaleEffect to scale a TextEditor contained in a VStack. When not scaled the text editor is very normal. But After scaling the editor can not be edited normally as the cursor can not be placed in the right place. Here is the code:
import SwiftUI
struct DemoTextView: View {
#State var text: String = ""
#State var scale: Double = 1
var body: some View {
VStack {
Spacer()
VStack {
TextEditor(text: $text)
.frame(width: 100, height: 100, alignment: .top)
.border(Color.red)
}
.scaleEffect(scale)
Spacer()
HStack {
Slider(value: $scale, in: 0.25...4)
Text("\(scale)")
}
.padding()
}
.frame(width: 500, height: 500, alignment: .center)
}
}
struct DemoTextView_Previews: PreviewProvider {
static var previews: some View {
DemoTextView()
}
}
And the screenshot of the wired behavior:
Anyone knows how to make the scaling of TextEditor works?

SwiftUI: popover to persist (not be dismissed when tapped outside)

I created this popover:
import SwiftUI
struct Popover : View {
#State var showingPopover = false
var body: some View {
Button(action: {
self.showingPopover = true
}) {
Image(systemName: "square.stack.3d.up")
}
.popover(isPresented: $showingPopover){
Rectangle()
.frame(width: 500, height: 500)
}
}
}
struct Popover_Previews: PreviewProvider {
static var previews: some View {
Popover()
.colorScheme(.dark)
.previewDevice("iPad Pro (12.9-inch) (3rd generation)")
}
}
Default behaviour is that is dismisses, once tapped outside.
Question:
How can I set the popover to:
- Persist (not be dismissed when tapped outside)?
- Not block screen when active?
My solution to this problem doesn't involve spinning your own popover lookalike. Simply apply the .interactiveDismissDisabled() modifier to the parent content of the popover, as illustrated in the example below:
import SwiftUI
struct ContentView: View {
#State private var presentingPopover = false
#State private var count = 0
var body: some View {
VStack {
Button {
presentingPopover.toggle()
} label: {
Text("This view pops!")
}.popover(isPresented: $presentingPopover) {
Text("Surprise!")
.padding()
.interactiveDismissDisabled()
}.buttonStyle(.borderedProminent)
Text("Count: \(count)")
Button {
count += 1
} label: {
Text("Doesn't block other buttons too!")
}.buttonStyle(.borderedProminent)
}
.padding()
}
}
Tested on iPadOS 16 (Xcode 14.1), demo video included below:
Note: Although it looks like the buttons have lost focus, they are still interact-able, and might be a bug as such behaviour doesn't exist when running on macOS.
I tried to play with .popover and .sheet but didn't found even close solution. .sheet can present you modal view, but it blocks parent view. So I can offer you to use ZStack and make similar behavior (for user):
import SwiftUI
struct Popover: View {
#State var showingPopover = false
var body: some View {
ZStack {
// rectangles only for color control
Rectangle()
.foregroundColor(.gray)
Rectangle()
.foregroundColor(.white)
.opacity(showingPopover ? 0.75 : 1)
Button(action: {
withAnimation {
self.showingPopover.toggle()
}
}) {
Image(systemName: "square.stack.3d.up")
}
ModalView()
.opacity(showingPopover ? 1: 0)
.offset(y: self.showingPopover ? 0 : 3000)
}
}
}
// it can be whatever you need, but for arrow you should use Path() and draw it, for example
struct ModalView: View {
var body: some View {
VStack {
Spacer()
ZStack {
Rectangle()
.frame(width: 520, height: 520)
.foregroundColor(.white)
.cornerRadius(10)
Rectangle()
.frame(width: 500, height: 500)
.foregroundColor(.black)
}
}
}
}
struct Popover_Previews: PreviewProvider {
static var previews: some View {
Popover()
.colorScheme(.dark)
.previewDevice("iPad Pro (12.9-inch) (3rd generation)")
}
}
here ModalView pops up from below and the background makes a little darker. but you still can touch everything on your "parent" view
update: forget to show the result:
P.S.: from here you can go further. For example you can put everything into GeometryReader for counting ModalView position, add for the last .gesture(DragGesture()...) to offset the view under the bottom again and so on.
You just use .constant(showingPopover) instead of $showingPopover. When you use $ it uses binding and updates your #State variable when you press outside the popover and closes your popover. If you use .constant(), it will just read the value from you #State variable, and will not close the popover.
Your code should look like this:
struct Popover : View {
#State var showingPopover = false
var body: some View {
Button(action: {
self.showingPopover = true
}) {
Image(systemName: "square.stack.3d.up")
}
.popover(isPresented: .constant(showingPopover)) {
Rectangle()
.frame(width: 500, height: 500)
}
}
}

How to prevent text compression when centering a View in SwiftUI?

See attached:
Squished Toggle in SwiftUI
... The code for this centered toggle is simply:
#State var rememberMe: Bool = false
HStack(alignment: .center) {
Spacer()
Toggle(
"Remember Me",
isOn: $rememberMe
)
Spacer()
}
Obviously, there's no need for ellipsis here, but by eliminating the Spacer() on each side, it'll grow to fill the width.
How do you center the Toggle without squishing its text? 🤔
fixedSize() seems to fix the issue on my end. We don't even need a HStack:
import SwiftUI
struct ContentView: View {
#State var rememberMe: Bool = false
var body: some View {
Toggle(
"Remember Me",
isOn: $rememberMe
).fixedSize()
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
Result
From Apple:
fixedSize() Fixes this view at its ideal size.

SwiftUI - switch toolbar states

Goal: a toolbar that switches states, according to object selected (image, text, shape)
What I did:
iimport SwiftUI
struct ToolbarMaster : View {
#State var showtoolbar = false
var toolbarmaster: [ToolbarBezier] = []
var body: some View {
HStack {
Spacer()
VStack {
Button(action: {self.showtoolbar.toggle() }) {
Image(systemName: "gear")
}
.padding(.leading)
Image("dog")
Text("Im a text")
.font(.largeTitle)
.color(.black)
Path(ellipseIn: CGRect(x: 0, y: 0, width: 100, height: 100))
.fill(Color.black)
}
NavigationView {
ZStack {
ToolbarBezier()
ToolbarArtwork()
}
.navigationBarTitle(Text("Toolbar Name"), displayMode: .inline)
}
.frame(width: 320.0)
}
}
}
My result:
How can I change the state, while selecting different objects?
I need to do in a dynamic way (not hardcoded), so that when any object that is an image, it will display Image Toolbar, and so on.
I would do it like this :
Add a #State boolean variable for each state of your toolbar
#State private var textSelected = false
Add an .onTap event to each of the "active" views on your scene :
Text("Tap me!")
.tapAction {
self.textSelected = true
}
Make the appearance of you toolbar change based on the "state" variable(s)
if self.textSelected {
self.showTextToolbarContent()
}
Of course, showTextToolbarContent() is a local function returning a some View with the toolbar content tailored for text selection.
This is obviously only a bare-bone example. In your actual implementation you'll probably use an array of bools for the "selection" state and another state var for the view currently selected.