XCode SwiftUI - Why is my keypad toolbar doing this? - swiftui

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.

Related

Trouble With Constraints for a Navigation Link

Here is my code:
import SwiftUI
struct ContentView: View {
var link: some View {
NavigationLink(destination: OtherView()) {
Text("NLTitle")
}
.foregroundColor(.blue)
}
var body: some View {
NavigationView {
List {
ZStack {
HStack {
Text("1")
Spacer()
}.padding([.leading, .trailing], 20)
HStack {
Spacer()
Text("2")
.multilineTextAlignment(.center)
.frame(
alignment: .center
)
Spacer()
}
.padding([.leading, .trailing], 20)
HStack {
Spacer()
link
}.padding([.leading, .trailing], 20)
}
}
}
}
}
I have a NavigationLink (named 'link') in a list cell. I would like for the Text within 'link' to be to the rightmost side of the view. To try to accomplish this, I inserted 'link' in an HStack and put a Spacer() before it to try and push it to the rightmost part of the view. When I run the app though, the Text ends up in between Text("1") and Text("2") and I can't figure out why. I want Text("1") to be in the leftmost part of the view, Text("2") to be in the center of the view, and 'link' to be in the rightmost part of the view. I have provided visuals (the colors aren't important, I just wanted to make the different Texts clear):
Desired layout:
What I get instead:
I found that if I take everything out of the List view I get my desired layout. Also, if I keep everything in the List view and replace the NavigationLink with a Button I get my desired layout. The goal is to get the desired layout without having to change either of these aspects.
For the sake of clarity, I didn't include the code for OtherView() as I don't think it's necessary for this question.
The "quick" fix is to add fixedSize() to the NavigationLink
var link: some View {
NavigationLink(destination: Text("OtherView()")) {
Text("NLTitle")
}
.foregroundColor(.blue)
.fixedSize()
}
That will allow the link to shrink.

Why tap Gesture of Button get triggered even out side of it's frame in SwiftUI?

I was testing accuracy of recognizing tap Gesture of a simple Button in SwiftUI, which I noticed it get activated even outside of its frame! I wanted be sure that I am not messing with label or other thing of Button, there for I build 2 different Buttons to find any improvement, but both of them get triggered in outside of given frame. I made a gif for showing issue.
gif:
code:
struct ContentView: View {
var body: some View {
Spacer()
Button("tap me!") {
print("you just tapped Button1!")
}.font(Font.largeTitle.bold()).background(Color.red)
Spacer()
Button(action: {
print("you just tapped Button2!")
}, label: {
Text("tap me!").font(Font.largeTitle.bold()).background(Color.red)
})
Spacer()
}
}
update:
some one said:
Do you see circle on click? - It is a size of active tap spot (kind of finger), and as you see, from your own demo, it overlaps red square - so gesture detected. That's it
I made another gif to show it is completely wrong to thinking like that, in this gif, even with overlaps tap get not registered!
I don't have an answer why this happens other than maybe there is some built-in functionality that increases the tappable area where possible for a better user experience? Anyway, if you put another tappable button behind it or next to it, you'll notice that this no longer happens and the tappable area is exactly where you'd expect it. Therefore, I wouldn't worry about it. But if you need to clip the tap area to the exact frame, you could add a clear background to the button and add a tap event that doesn't doesn't do anything, but takes priority on that location.
var body: some View {
VStack {
// Button behind button will take priority tap.
ZStack {
Button(action: {
print("tap 2")
}, label: {
Text("tap me!")
.padding()
.font(Font.largeTitle.bold())
.background(Color.green)
})
Button(action: {
print("tap 1")
}, label: {
Text("tap me!")
.font(Font.largeTitle.bold())
.background(Color.red)
})
}
// Clear background with "fake" tap event
Button(action: {
print("tap 3")
}, label: {
Text("tap me!")
.font(Font.largeTitle.bold())
.background(Color.red)
})
.padding()
.background(Color.black.opacity(0.001).onTapGesture {
//print("tap 4")
})
}
}
I think it's a built-in functionality to expand the tappable area:
Color.red
.frame(width: 30.0, height: 30.0)
.gesture(DragGesture(minimumDistance: 0).onEnded({ (value) in
// Tap with Apple Pencil: location is exactly the frame
// Tap with finger or an capacitance pen: the hit location is inaccurate
print(value.startLocation)
}))
Solution (Bug: If the view is inside a ScrollView, this will block scroll gesture):
let size = CGSize(width: 200.0, height: 200.0)
Color.red
.frame(width: size.width, height: size.height)
.gesture(DragGesture(minimumDistance: 0).onEnded({ (value) in
print(value.startLocation)
if value.startLocation.x >= 0 && value.startLocation.y >= 0 && value.startLocation.x <= size.width && value.startLocation.y <= size.height {
print("inside frame")
// action ...
} else {
print("outside frame")
}
}))

SwiftUI insertion transion is not working when view added

I'm trying SwiftUI tutorial in apple developer page. now I'm following transition tutorial but my transition is not working when the view added.
here my code.
VStack(alignment: .leading) {
HStack() {
// title
Text(titleText)
.font(.headline)
.padding()
Spacer()
// button
Button(action: {
withAnimation {
self.showDetail.toggle()
}
}) {
Image(systemName: "chevron.right.circle")
.imageScale(.large)
.rotationEffect(.degrees(showDetail ? 90 : 0))
.padding()
}
}
// detail
if showDetail {
Text(contentText)
.transition(.slide)
.padding()
}
}
I think the Text that has contentText, should appear with slide transition but when I press the Button, it just pop up. but when I press button again it disappear with transition. so removal transition is work.
How can I fix this?
After some test I found some solution.
solution 1:
It is just bug of XCode canvas.
It works in the simulator or on a real machine.
solution 2. (if you really want to do in canvas)
Button (action : {
withAnimation(.easeInOut) { // add animation manually
self.showDetail.toggle()
}
}) {
// label Image no change
}
if showDetail {
Text(contentText)
.transition(.slide)
.animation(.easeInOut) // match with withAnimation
}
Although I haven't done the tutorial you are talking about but I think it works pretty fine using the .move transition instead of the .slide transition.
Here's my code for the detail part (the rest is unchanged). I hope my answer can help you
if showDetail {
Text(contentText)
.transition(.move(edge: .leading))
.padding()
}

Dismiss button (X) on an image - top right alignment HOW?

What is an effective & effecient way to get the Dismiss button (X) into the top right corner?
I'm struggling with container alignment... can't say I GROK it.
Needless to say ... this ain't it!
var body: some View {
ZStack {
Image("Biz-card_2020")
.resizable()
.edgesIgnoringSafeArea(.all)
HStack(alignment: .top) {
VStack() {
Spacer(minLength: 5) // vertical space
HStack() {
Spacer()
// close Welcome page (X) button
Button(action: {
//print(" - Button to dismiss page \(self.isPresented)")
self.isPresented = false // dismiss the Welcome view
//print(" - after action Button to dismiss Welcome page \(self.isPresented)")
}, label: {
Image(systemName: "xmark.circle" )
.scaledFont(name: "Georgia", size: Const.titleText)
.minimumScaleFactor(0.3)
.accentColor(.white)
.padding(10)
})
}
Spacer()
}
}
}
}
You need to remove Spacer(minLength: 5) and replace it with padding for HStack.
Spacer(minLength: 5) doesn't mean its length will be exactly 5 (only that the minimum length will be 5).
You may also want to extract close button to another function for clarity.
Try the following:
struct ContentView: View {
...
var body: some View {
ZStack {
Image("Biz-card_2020")
.resizable()
.edgesIgnoringSafeArea(.all)
closeButton
}
}
var closeButton: some View {
VStack {
HStack {
Spacer()
Button(action: {
...
}) {
Image(systemName: "xmark.circle")
.padding(10)
}
}
.padding(.top, 5)
Spacer()
}
}
}
ZStack can be configured using .topTrailing etc. You should use those alignments to configure your view. Using something like Spacer() is going to cause the rest of your views to get pushed down.
ZStack(alignment: .topTrailing) {
// your code here
}
If you need to move your code a bit more, use either padding, or offset modifiers.

SwiftUI: Text Field getting cut off

The textField on my SwiftUI app is getting cut off. But it doesn't happen every time. It seems to happen at random.
Here is the code I'm using:
var body: some View {
VStack {
Spacer()
// Target row
HStack {
Text("Put the bullseye as close as you can to:")
Text("\(target)")
}
Spacer()
// Slider row
HStack {
Text("1")
Slider(value: $sliderValue, in: 1...100) {_ in
print(self.sliderValue)
}
Text("100")
}
Spacer()
// Hit me button row
Button(action: {
print("Button pressed")
self.alertIsVisible = true
}) {
Text(/*#START_MENU_TOKEN#*/"Hit Me!"/*#END_MENU_TOKEN#*/)
}
.alert(isPresented: $alertIsVisible) { () -> Alert in
let roundedValue = Int(sliderValue.rounded())
let score = pointsForCurrentRound()
return Alert(title: Text("Hello there!"), message: Text("The slider's value is \(roundedValue)!\n" +
"You scored \(score) points this round"
), dismissButton: .default(Text("Awesome")))
}
Spacer()
// Score and start over button row
HStack {
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Start Over")
}
Spacer()
Text("Score:")
Text("999999")
Spacer()
Text("Round:")
Text("999")
Spacer()
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Info")
}
}
.padding(.bottom, 20)
}
}
I've tried adding padding trailing the text field and before the target. I've tried adding padding to the leading edge of the target. I've tried giving using the frame method on the text field to add a min length. None of these work. Any ideas?
Thanks
You may add fixedSize() to lock the labels.
HStack {
Text("Put the bullseye as close as you can to:").fixedSize()
Text("\(target)").fixedSize()
}
I just came across this exact situation! After a few moments of searching, trial, and errors, I finally figured it out. The text view is trying to resize and one of the parent views have animations enabled. If anyone having this same issue adds .animation(nil) to the Text, this will likely solve the issue.
VStack {
Text("\(Int(self.viewModel.ProgressPercentage * 100.0))%")
.font(.largeTitle)
.animation(nil)
}
Good luck!