Text view in SwiftUI doesn't show only with bigger font - ios

In DoctorHomePage I have a grouped list and above the list I want to add a text view, but the text view doesn't show only if I change the font to a bigger one, but it is too big and I want it smaller. Here is my code:
import SwiftUI
import Combine
struct DoctorHomePage: View {
#Binding var shouldPopToRootView : Bool
#State private var curent: Int? = nil
#State private var isActive: Bool = false
#State private var id = 0
let defaults = UserDefaults.standard
let networkRequest = Network()
#State var cancelable: AnyCancellable? = nil
#State var localPatients : [Patients] = []
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: ContentView(), tag: 1, selection: $curent) {
EmptyView()
}
Text("Welcome, doctor!") // this is the text that I want to add
.font(.system(size: 30)).fontWeight(.ultraLight)
.padding(.top, 50)
// PATIENT LIST
List(localPatients) { patient in
VStack(alignment: .leading) {
Text(patient.name)
}
}.listStyle(GroupedListStyle())
.onAppear(perform: {
self.loadPatients()
connCode = self.defaults.integer(forKey: "doctorID")
self.id = connCode
})
}.edgesIgnoringSafeArea([.top, .bottom])
}.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
}
}
Here are some screen shots to help you understand the problem:
The first image is with no text view.
The second image is with the font size of 60.
The third image is with the font size of 30.

Seems like some strange / buggy behavior.
Setting the zIndex of you welcome text will fix your problem.
Text("Welcome, doctor!").zIndex(1)

Related

SwiftUI #State variable does not change view

Using HalfASheet (https://github.com/franklynw/HalfASheet).
I have a View called ProjectsView, and in the ZStack in ProjectsView I have ProjectSorting and SortingView(both injected with the EnvironmentObject). I want the Text(🟩) in ProjectSorting to be changed, and the HStack(🟦) in SortingView to have a checkmark, both depending on the value of the sorting variable in SortingValues. Users can change the value of the sorting by pressing the Button in SortingView.
For whatever reason, the Text(🟩) in ProjectSorting does not change at all. And the HStack(🟦) in SortingView only gets the checkmark when its ancestor stack has another Text(🟨) which includes the #State variable from the environment, which I find very weird.
What should I change? Is there any way I can make this work using #EnvironmentObject? I'm a newbie and couldn't really understand other wrappers so I'd like to make this work within #State, #Binding, #EnvirionmentObject.
Thanks in advance.
SortingValues.swift
import Combine
class SortingValues: ObservableObject {
#Published var sorting = "Top Rated"
}
ProjectsView.swift
struct ProjectsView: View {
#Binding var isPresented: Bool
#State var showSortingSheet = false
var body: some View {
ZStack {
NavigationView {
VStack(spacing: 0) {
ProjectsTopView(isPresented: $isPresented)
ProjectSorting(showSortingSheet: $showSortingSheet)
.environmentObject(SortingValues())
ProjectList()
}
.navigationBarHidden(true)
}
SortingView(showSortingSheet: $showSortingSheet)
.environmentObject(SortingValues())
}
}
}
ProjectSorting.swift
import SwiftUI
struct ProjectSorting: View {
#EnvironmentObject var sortingValues: SortingValues
#Binding var showSortingSheet: Bool
#State var sortingValue = ""
var body: some View {
VStack {
HStack {
Text("Projects")
Spacer()
Button {
showSortingSheet.toggle()
} label: {
HStack(spacing: 3) {
Image("sortingArrows")
Text(sortingValue) // < 🟩 this is the Text I want to be changed
}
}
}
// Another HStack goes here
}
.onReceive(sortingValues.$sorting) { sorting in
print("This is ProjectSorting. sorting:", sorting) // < this does not print when I close the half sheet
sortingValue = sorting
}
}
}
SortingView.swift
import SwiftUI
import HalfASheet
struct SortingView: View {
#EnvironmentObject var sortingValues: SortingValues
#Binding var showSortingSheet: Bool
#State var sortingValue = ""
var body: some View {
VStack {
HalfASheet(isPresented: $showSortingSheet) {
let sorting = ["Most Recent", "Most Reviewed", "Top Rated", "Lowest Price", "Highest Price"]
VStack(alignment: .leading) {
ForEach(sorting, id: \.self) { sorting in
VStack(alignment: .leading, spacing: 14) {
Button (action: {
sortingValues.sorting = sorting
}, label: {
HStack { // 🟦
Text(sorting)
Spacer()
if sorting == sortingValue { // < this is where I add the checkmark
Image(systemName: "checkmark")
}
}
.foregroundColor(.primary)
})
if sorting != "Highest Price" {
Divider()
}
}
}
}
}
.height(.fixed(325))
// Text("Inside VStack, outside HalfASheet") // adding this Text DOES NOT make the HStack have a checkmark
Text("Inside VStack, outside HalfASheet: \(sortingValue)") // 🟨 adding this Text DOES make the HStack have a checkmark
}
.onReceive(sortingValues.$sorting) { sorting in
// the two printing lines below print correctly every time I tap the Button
print("This is SortingView. sorting:", sorting)
print("sortingValues.sorting: \(sortingValues.sorting)")
sortingValue = sorting
}
}
}
Your SortingView and ProjectSorting both access an environment object of type SortingValues, but you're passing new, separate instances to each. So the change you make in one place isn't being reflected in the other, because each view is communicating with one of two completely different objects of the same type.
If you want them to interact with the same object instance, you need to declare it at a point that's above both in the object hierarchy and make sure that that single instance is passed into both. For example:
struct ProjectsView: View {
#Binding var isPresented: Bool
#State var showSortingSheet = false
#StateObject var sortingValues = SortingValues()
var body: some View {
ZStack {
NavigationView {
VStack(spacing: 0) {
ProjectsTopView(isPresented: $isPresented)
ProjectSorting(showSortingSheet: $showSortingSheet)
.environmentObject(sortingValue)
ProjectList()
}
.navigationBarHidden(true)
}
SortingView(showSortingSheet: $showSortingSheet)
.environmentObject(sortingValues)
}
}
}
But you can go one step further. Because environment objects and values propagate down the view hierarchy automatically, you can replace two separate .environmentObject calls with one:
struct ProjectsView: View {
#Binding var isPresented: Bool
#State var showSortingSheet = false
#StateObject var sortingValues = SortingValues()
var body: some View {
ZStack {
NavigationView {
VStack(spacing: 0) {
ProjectsTopView(isPresented: $isPresented)
ProjectSorting(showSortingSheet: $showSortingSheet)
ProjectList()
}
.navigationBarHidden(true)
}
SortingView(showSortingSheet: $showSortingSheet)
}
.environmentObject(sortingValues)
}
}
There are probably better ways of dealing with reacting to changes in your observed model rather than duplicating variable values in a local state variable -- but ensuring that all your views are using the same shared environment object should get you on your way.

#State var not updated as expected in LazyVGrid

I'm having trouble understanding why my variable "selectedItem" is being updated in one part of this code, and not the other. My goal is to make it so when you tap on the image in the grid, it passes the selected image name to an ImageDetailView (ideally I'd like it to be a Navigationlink, but a sheet is easier for me to test.. one step at a time).
Where I have print(selectedItem) it prints the name of the LazyVGrid's tapped Image in the console as expected. Awesome.
But then the sheet that opens is blank because it's looking for "test" still... the console shows a message saying "No image named 'test' found in asset catalog..."
Why is the sheet still using the initialized value of "test?" and not the updated value?
struct ImagesView: View {
#State var gridLayout: [GridItem] = [ GridItem() ]
var title: String
var imageSet = [Photo]()
#State private var selectedItem = "test"
var body: some View {
ZStack {
Color.black.edgesIgnoringSafeArea(.all)
GeometryReader { reader in
ScrollView {
LazyVGrid(columns: gridLayout, alignment: .center, spacing: 10) {
ForEach(imageSet.indices) { index in
Image(imageSet[index].name)
.resizable()
.onTapGesture {
showImageDetailView = true
selectedItem = imageSet[index].name
print(selectedItem)
}
)}
.padding(.horizontal, 10)
.padding(.bottom, 25)
}
.sheet(isPresented: $showImageDetailView, content: {
ImageDetailView(selectedItem: selectedItem)
})
Here's the ImageDetailView
struct ImageDetailView: View {
#State var selectedItem: String
var body: some View {
ZStack {
Color.black.edgesIgnoringSafeArea(.all)
Image(selectedItem)
.resizable()
.aspectRatio(contentMode: .fit)
.cornerRadius(10)
}
}
}
Sheet is picky about when it loads its content with isPresented.
A more reliable solution is to use sheet(item: ), which will work with your situation with just a small modification to selectedItem -- it'll have to conform to Identifiable. So, you can wrap it like this:
struct ImageSelection : Identifiable {
var name : String
var id: String {
return name
}
}
Then, selectedItem will become an optional, because it will determine whether the sheet is open. Here's a minimal example showing the optional and how you use it with sheet(item:):
struct ContentView : View {
#State var selectedItem : ImageSelection?
var body: some View {
Text("Test")
.sheet(item: $selectedItem) { item in
Text(item.name)
}
}
}
(Note that item is passed into the sheet's closure -- that's how you make sure that the correct data is used)
Update
Based on your comments:
selectedItem = ImageSelection(name: imageSet[index].name)
print(selectedItem?.name)

Content inside Picker overlaps when the font size increases in SwiftUI

struct ContentView: View {
#State private var selectedNumber = 0
// var numbersArray - This will be the array
var body: some View {
VStack {
Picker("Number Picker", selection: $selectedNumber) {
ForEach(0..<Int(numbersArray.count)) {
Text("\($0 + 1)").font(.system(size: 60))
}
}
}
}
}
I am creating a Picker for selecting numbers in Watchkit. When I try to increase the font size the numbers are overlapping. How to make the Picker content resize automatically so that the contents does not overlap.
You could add something like Spacer():
struct TestSwiftUIView: View {
#State private var selectedNumber = 0
var numbersArray = [1,2,3,4,5,6]
var body: some View {
VStack {
Picker("Number Picker", selection: $selectedNumber) {
ForEach(0..<Int(numbersArray.count)) {
Spacer()
Text("\($0 + 1)").font(.system(size: 60))
}
}
}
}
}
It's just my first Idea, probably not the best solution.

#Binding properties are not refresh view of Child View in SwiftUI

I'm trying to reusable View and I added it on ContentView
This is my Child View
struct VStackView: View {
#Binding var spacing: Double
#Binding var alignmentIndex: Int
#Binding var elementsCount: Int
private let alignments: [HorizontalAlignment] = [.leading, .center, .trailing]
var body: some View {
VStack(alignment: self.alignments[alignmentIndex], spacing: CGFloat(spacing)) {
ForEach(0..<elementsCount) {
Text("\($0)th View")
}
}
}
}
and This is SuperView
Superview has Controls like Stepper, Slider, Picker that adjust values of VStack (alignment, spacing etc)
and I want to show the result depending on that values. but Child View is not changed
struct LayoutView: View {
private let layout: StackLayout
#State private var spacing = 0.0
#State private var alignmentIndex = 0
#State private var alignment: HorizontalAlignment = .leading
#State private var elementsCount: Int = 0
private let alignmentsString = [".leading", ".center", ".trailing"]
private let minValue = 0.0
private let maxValue = 100.0
init(_ layout: StackLayout) {
self.layout = layout
}
var body: some View {
NavigationView {
Form {
Section(header: Text("Controls")) {
VStack(alignment: .leading) {
Text("Spacing: \(Int(spacing))").font(.caption)
HStack {
Text("\(Int(minValue))")
Slider(value: $spacing, in: minValue...maxValue, step: 1)
Text("\(Int(maxValue))")
}
Divider()
Picker("alignment", selection: $alignmentIndex) {
ForEach(0..<self.alignmentsString.count) {
Text("\(self.alignmentsString[$0])")
}
}.pickerStyle(SegmentedPickerStyle())
Divider()
Stepper(value: $elementsCount, in: 0...10) {
Text("Element Count: \(elementsCount)")
}
}
}
VStackView(spacing: $spacing, alignmentIndex: $alignmentIndex, elementsCount: $elementsCount)
}
.navigationBarTitle(Text(layout.rawValue), displayMode: .inline)
}
}
}
I also search google and they recommend #EnviornmentObject. if that is correct, when to use #Binding property wrapper.
Isn't it two way binding properties?
Simply speaking you can use #Binding, when you want to share data in two places.
#Observable or #environmetobject is to be used, when you want to share your data in multiple views.
Your ForEach Loop in the VStackView generates a problem, because Swiftui does not know how it can identify each of your items uniquely so it does not know how to update them, when values change.
Append your code like this:
ForEach(0..<elementsCount, id: \.self) {
Text("\($0)th View")
}

SwiftUI 2 questions but very simple, about searchBar and the backgroundImage for List

SwiftUI very simple 2 questions
1, how to make the searchBar stick to top so that when use scroll down they can still see they searchBar
2, I'm using a List and work with a TabView and I want the List or the VStack maybe(or something that is holding the List) to have a background Image(a png already in my Assets.xcassets). I tried ZStack or .background(Image("...")) but none of them worked.
Here is the image, I want the background to be as the teal color png image.
Here is the code of one of the view for TabView:
struct Discover: View {
#State private var keywords: String = ""
var names = ["Yu Song", "ZhangYuan", "Kotoyama"]
var body: some View {
ZStack {
List {
HStack {
SearchBar(text: $keywords)
Image(systemName: "person")
Image(systemName: "bell")
}
ForEach(self.names.filter {
self.keywords.isEmpty ? true : $0.localizedCaseInsensitiveContains(self.keywords)
}, id: \.self) { name in
Text(name)
}
}.background(Image("bg_global"))
}.edgesIgnoringSafeArea(.all)
}
}
here is the preview for your convinence:
Notice that I already added .background(Image("bg_global")) in List and VSrack but the image never showed.
Thanks a lot.
To make background visible it's needed to make List and its content transparent. It can be done for example in init,
init() {
UITableView.appearance().backgroundColor = .clear
UITableViewCell.appearance().backgroundColor = .clear
}
Use a VStack instead of a ZStack to make the SearchBar stick to the top. And make sure the SearchBar is not part of the List View.
struct ContentView: View {
init() {
UITableView.appearance().backgroundColor = .clear
UITableViewCell.appearance().backgroundColor = .clear
}
#State private var keywords: String = ""
var names = ["Yu Song", "ZhangYuan", "Kotoyama"]
var body: some View {
VStack{
// Your Search Bar View
HStack{
Spacer()
Text("Search")
Spacer()
}.padding().background(Color.red)
List {
ForEach(self.names.filter {
self.keywords.isEmpty ? true : $0.localizedCaseInsensitiveContains(self.keywords)
}, id: \.self) { name in
Text(name)
}
}
}.offset(x: 0, y: 45).background(Color.yellow).edgesIgnoringSafeArea(.all)
}
}
Since you also want to show the background in the safe areas you need to push down the view with offset.

Resources