How do I prevent Text "bounce" in SwiftUI SegmentedControl - swiftui

I am having trouble with the text in SwiftUI SegmentedPicker bouncing when I tap on the various segments.
This is super basic so I am not sure what options there are for adjusting this:
struct ContentView : View {
#State private var selectorIndex = 0
#State private var numbers = ["One","Two","Three"]
var body: some View {
VStack {
Picker("Numbers", selection: $selectorIndex) {
ForEach(0 ..< numbers.count) { index in
Text(self.numbers[index]).tag(index)
}
}
.pickerStyle(SegmentedPickerStyle())
Text("Selected value is: \(numbers[selectorIndex])").padding()
}
}
}

try adding this to the Text, especially the alignment:
.frame(width: 222, height: 55, alignment: .leading)
Edit:
I'm using the following code to test the text bounce on real devices and various simulators:
struct ContentView: View {
#State private var selectorIndex = 0
#State private var numbers = ["One","Two","Three"]
var body: some View {
VStack {
Picker("Numbers", selection: $selectorIndex) {
ForEach(0 ..< numbers.count) { index in
Text(self.numbers[index]).tag(index)
}
}
.pickerStyle(SegmentedPickerStyle())
Text("Selected value is: \(numbers[selectorIndex])")
.frame(width: 222, height: 55, alignment: .leading)
}
}
}

Related

How to draw a rectangle to a specific area of the screen based on user search input?

I am trying to make a program where the user enters a certain set of characters into the search bar (for example "AP1") and the program draws a rectangle on top of an image I have.
I will have a bunch of if statements testing what the user entered and giving the coordinates for where the rectangle will be drawn. I am just having trouble with the "scopes" and the ZStack and VStack for the image overlay not wanting to cooperate with how I have the if statement(s) set up. Here is my entire program:
This is my third day doing any type of iOS development
import SwiftUI
struct ContentView: View {
private var listOfBins = binList
#State var searchText = ""
var body: some View {
// MAP
VStack {
Image("map")
.resizable()
.scaledToFit()
.position(x: 195, y: 175)
.overlay(ImageOverlay(), alignment: .bottomTrailing)
Spacer()
}
NavigationView {
List {
ForEach(bins, id: \.self) { bin in
HStack {
Text(bin.capitalized)
.textCase(.uppercase)
Spacer()
Image(systemName: "figure.walk")
.foregroundColor(Color.blue)
}
.padding()
}
}
.searchable(text: $searchText)
.navigationTitle("Bins")
if (searchText.elementsEqual("AP1")) {
drawBox(width: 50, height: 50, x: 50, y: 50)
}
}
}
func drawBox(width: Int, height: Int, x: Int, y: Int) -> Rectangle{
struct ImageOverlay: View{
var body: some View {
ZStack {
Rectangle()
.fill(.green)
.frame(width: 100, height: 100)
.position(x: 200, y: 300)
}
}
}
}
// DISPLAY LIST OF BINS AND SEARCH BAR
var bins: [String] {
let upBins = listOfBins.map {$0.uppercased()}
return searchText == "" ? upBins : upBins.filter{
$0.contains(searchText.uppercased())
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
}
Could not run your provided code, so I replicated a temp view.
Is this something you wanted? (code is below the image)
struct SSContentView: View {
#State var searchText = ""
var images = ["Swift", "Ww", "Luffy"]
var body: some View {
NavigationView {
List {
TextField("Search Here", text: $searchText)
ForEach(0...5, id: \.self) { _ in
ForEach(images, id: \.self) { image in
ZStack {
Image(image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 50)
.overlay {
if searchText == image {
OverlayImage
}
}
}
}
}
}
.navigationTitle("My Pictures")
}
}
var OverlayImage: some View {
ZStack {
Rectangle()
.fill(.clear)
.frame(width: 60, height: 60)
.border(.green)
}
}
}

Why the scrollview doesn't get updated with new data from array?

I'm trying to send and then display them in the scrollview realtime. But nothing shows up. How to solve it? So, basically when the user types the message into a textbox then it will be saved in array and then it will be populated to the crollView in realtime so the user can view all the messages.
Error: No errors, it just isn't visible.
import SwiftUI
struct SingleMessageBubbleModel: Identifiable {
let id = UUID()
var text: String
var received: Bool
var timeStamp: Date
}
var messagesDBArray : [SingleMessageBubbleModel] = []
struct ContentView: View {
#State private var showOnTheSpotMessaging: Bool = true
#State var textTyped: String
var body: some View {
if (showOnTheSpotMessaging) {
VStack {
HStack {
ScrollViewReader { proxy in
ScrollView {
LazyVStack {
ForEach(messagesDBArray, id: \.id) { message in
MessageBubble(message: message)
}
}
}
.padding(.top, 10)
.background(.gray)
.onChange(of: messagesDBArray.count) { id in
withAnimation {
proxy.scrollTo(id, anchor: .bottom)
}
}
}
.frame( height: 200, alignment: .bottomLeading)
}
HStack () {
TextEditor (text: $textTyped)
.frame(width: 200, height: 200, alignment: .leading)
Button ("Send", action: {
messagesDBArray.append(SingleMessageBubbleModel(text: textTyped, received: true, timeStamp: Date()))
})
}
}
}
}
}
struct MessageBubble: View {
var message: SingleMessageBubbleModel
#State private var showTime = false
var body: some View {
VStack(alignment: message.received ? .leading : .trailing) {
HStack {
Text(message.text)
.padding()
.background(message.received ? Color.gray : Color.blue)
.cornerRadius(30)
}
.frame(maxWidth: 300, alignment: message.received ? .leading : .trailing)
.onTapGesture {
withAnimation {
showTime.toggle()
}
}
if showTime {
Text("\(message.timeStamp.formatted(.dateTime.hour().minute()))")
.font(.caption2)
.foregroundColor(.gray)
.padding(message.received ? .leading : .trailing, 25)
}
}
.frame(maxWidth: .infinity, alignment: message.received ? .leading : .trailing)
.padding(message.received ? .leading : .trailing)
.padding(.horizontal, 4)
}
}
Basically, when the button is pressed, your property messagesDBArray is well and truly append with the new value.
However, and it's really important to understand this point in swiftUI, nothing triggers the refresh of the view.
I suggest you two solutions:
If you don't need messagesDBArray to be outside of ContentView:
You just have to add messagesDBArray as a state in ContentView like following
struct ContentView: View {
#State var messagesDBArray : [SingleMessageBubbleModel] = []
#State private var showOnTheSpotMessaging: Bool = true
#State var textTyped: String = ""
var body: some View {
if (showOnTheSpotMessaging) {
VStack {
HStack {
ScrollViewReader { proxy in
ScrollView {
LazyVStack {
ForEach(messagesDBArray, id: \.id) { message in
MessageBubble(message: message)
}
}
}
.padding(.top, 10)
.background(.gray)
.onChange(of: messagesDBArray.count) { id in
withAnimation {
proxy.scrollTo(id, anchor: .bottom)
}
}
}
.frame( height: 200, alignment: .bottomLeading)
}
HStack () {
TextEditor (text: $textTyped)
.frame(width: 200, height: 200, alignment: .leading)
Button ("Send", action: {
messagesDBArray.append(SingleMessageBubbleModel(text: textTyped, received: true, timeStamp: Date()))
})
}
}
}
}
}
If you need messagesDBArray to be outside of ContentView:
1- Create a class (ViewModel or Service or whatever you wan to call it) with messagesDBArray as a #Published property
final class ViewModel: ObservableObject {
#Published var messagesDBArray : [SingleMessageBubbleModel] = []
}
2- Observe this class in ContentView in order to append and receive the update
struct ContentView: View {
#ObservedObject private var viewModel = ViewModel()
#State private var showOnTheSpotMessaging: Bool = true
#State var textTyped: String = ""
var body: some View {
if (showOnTheSpotMessaging) {
VStack {
HStack {
ScrollViewReader { proxy in
ScrollView {
LazyVStack {
ForEach(viewModel.messagesDBArray, id: \.id) { message in
MessageBubble(message: message)
}
}
}
.padding(.top, 10)
.background(.gray)
.onChange(of: viewModel.messagesDBArray.count) { id in
withAnimation {
proxy.scrollTo(id, anchor: .bottom)
}
}
}
.frame( height: 200, alignment: .bottomLeading)
}
HStack () {
TextEditor (text: $textTyped)
.frame(width: 200, height: 200, alignment: .leading)
Button ("Send", action: {
viewModel.messagesDBArray.append(SingleMessageBubbleModel(text: textTyped, received: true, timeStamp: Date()))
})
}
}
}
}
}
I hope that this is clear to you and that it has been useful 😉

Keyboard not overlaying the view in SwiftUI

So i want my keyboard to overlay the view so that the view stays and not going upwards. i did several variations such as adding it in my loginstuff, or adding in it navigationView. it doesn't work at all
Here's my code
struct LoginView: View {
#StateObject var userData = UserData()
var body: some View {
NavigationView {
ZStack(alignment:.top) {
Color.pink.ignoresSafeArea(edges: .top)
VStack {
Image(systemName: "graduationcap.fill")
.resizable()
.scaledToFit()
.frame(width: /*#START_MENU_TOKEN#*/100/*#END_MENU_TOKEN#*/, height: /*#START_MENU_TOKEN#*/100/*#END_MENU_TOKEN#*/, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.foregroundColor(.white)
.padding(.top,30)
Text("Study +")
.font(.title)
.fontWeight(.medium)
.foregroundColor(.white)
Spacer()
//Mark : The login Thinggy
LoginStuffs()
}
}
.edgesIgnoringSafeArea(.bottom)
.navigationTitle("Login")
.navigationBarHidden(true)
}
}
}
Login Stuff
struct LoginStuffs: View {
#State var username:String = ""
#State var password:String = ""
#State var isShow:Bool = false
var body: some View {
Vstack{
Textfield()
Securefield()
Securefield()
}
.padding()
.frame(width:UIScreen.width,height:UIScreen.height/1.5)
.background(Color.white)
.cornerRadius(15, corners: [.topLeft, .topRight])
//.ignoresSafeArea(edges: /*#START_MENU_TOKEN#*/.bottom/*#END_MENU_TOKEN#*/)
}
}
Seems like there's a problem within my codes in which I did not know (probably due to not learn it properly). please do help, thank you for your attention
use on your NavigationView
.edgesIgnoringSafeArea(.bottom))

SwiftUI: How to center multi column picker on screen

I created a multi column picker with SwiftUI that I want to center on the screen.
However, whatever I try it remains left outlined as shows on the picture.
What I've tried:
Adding (alignment: .center) on the GeometryReader, HStack and VStack.
Trying to center the picker itself
putting the pickers in a container and center that
So the question is how do center the 3 columned picker on the screen.
Thanks for your support!
Paul
import SwiftUI
import Combine
struct ContentView: View {
#State var initial = "n"
#State var final = "iu"
#State var tone = 2
#State var pinyin = ""
var initials = ["b","c","ch","d","f","g","h","k","l","m","n","p","q","r","s","sh","t","w","x","z","zh"]
var finals = ["a","ai","an","ang","ao","e","ei","en","eng","er","i","ia","ian","iang","iao","ie","in","iong","iu","o","ong","u","ua","uan","uang","uai","ui","un","uo","ü","üan","üe","ün"]
var tones = [Int](1..<6)
var body: some View {
VStack{
Text("你")
.font(/*#START_MENU_TOKEN#*/.title/*#END_MENU_TOKEN#*/)
GeometryReader { geometry in
HStack{
Picker(selection: self.$initial, label: Text("")) {
ForEach(0 ..< self.initials.count) { index in
Text("\(self.initials[index])").tag(self.initials[index])
}
}
.onReceive(Just(initial), perform: { value in
updatePinyin()
})
.frame(width: geometry.size.width/6, height: 200).clipped()
Picker(selection: self.$final, label: Text("")) {
ForEach(0 ..< self.finals.count) { index in
Text("\(self.finals[index])").tag(self.finals[index])
}
}
.onReceive(Just(final), perform: { value in
updatePinyin()
})
.frame(width: geometry.size.width/6, height: 200).clipped()
Picker(selection: self.$tone, label: Text("")) {
ForEach(0 ..< self.tones.count) { index in
Text("\(self.tones[index])").tag(self.tones[index])
}
}
.onReceive(Just(tone), perform: { value in
updatePinyin()
})
.frame(width: geometry.size.width/6, height: 200).clipped()
}
}
Text(pinyin)
}
}
func updatePinyin() {
pinyin = initial + final + String(tone+1)
print(pinyin)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
}
}
}
Not really sure the final goal, but for provided code it can be done just by making Stack consume all space provided by GeometryReader, like
HStack{
// ... other code here
}
.frame(maxWidth: .infinity) // << this one !!
Tested with Xcode 12.4 / iOS 14.4
Just add spacer in both side of Picker(s), Like that,
HStack {
Spacer()
// Other Codes
Spacer()
}

How to display a Picker in SwiftUI completely in a ScrollView or VStack with other components?

As soon as I put a Picker in a ScrollView or VStack and there is some more code in it like a ForEach Loop, only the Picker-label is shown.
I wonder why, because outside of the Scrollview or alone it is displayed correctly.
Here is the code:
#State private var exercises = ["Unterarmstütz", "Dehnen", "Kopfstand", "Handstand"]
#State private var selectedExercise = "Plank"
#State private var selectedTimeIndex = 60
var body: some View {
VStack {
ScrollView {
Picker(selection: $selectedTimeIndex, label: Text("select Time")) {
Text("Option 1")
Text("Option 2")
Text("Option 3")
}
ForEach(exercises.identified(by: \.self)) { exercise in
Button(action: {
self.selectedExercise = String("exercise")
}) {
Text("exercise")
}
}
}
}
}
The Problem is ScrollView, If you place any content inside of ScrollView then you have to set frame of it.
Here you can do that,
#State private var exercises = ["Unterarmstütz", "Dehnen", "Kopfstand", "Handstand"]
#State private var selectedExercise = "Plank"
#State private var selectedTimeIndex = 60
var body: some View {
//use GeometryReader for height & weight//
GeometryReader { geometry in
ScrollView() {
VStack {
Picker(selection: self.$selectedTimeIndex, label: Text("select Time")) {
Text("Option 1")
Text("Option 2")
Text("Option 3")
}.frame(width: geometry.size.width, height: geometry.size.height, alignment: .center)
ForEach(self.exercises, id: \.self) { exercise in
Button(action: {
self.selectedExercise = String("exercise")
}) {
Text("exercise")
}
}
}
}
}
}
Note: I'm using Xcode 11 Beta 4