So I'm relatively new to SwiftUI, and I'm trying to add a UISearchBar into my navigation bar, but I'm having trouble getting it to show up. If I add it as a separate component, it appears but it's too far down the screen. Here's what I have so far:
UISearchBar Component:
struct SearchBar2: UIViewRepresentable {
@Binding var text: String
func makeUIView(context: Context) -> UISearchBar {
let searchbar = UISearchBar(frame: .zero)
return searchbar
}
func updateUIView(_ uiView: UISearchBar, context: Context) {
uiView.text = text
}
}
UIView:
var body: some View{
ZStack{
NavigationView{
VStack {
NavigationLink()
{
InitView()
}.buttonStyle(PlainButtonStyle())
ZStack {
HStack {
//some items here
}
}.toolbar{
SearchBar2(text: $text)
}
DayView()
}
}
}
}
And here's what this looks like on the simulator: Screenshot
CodePudding user response:
Ok so since you're a student I want to provide some critical feedback as well as give you the tools you need to succeed.
Critical Feedback
- First, always remember that when you start working professionally as a developer, you're not expected to know it all. In fact if you have an employer that does expect that, RUN. The point in this assignment is to get you to think critically about a solution and break it down into its smallest parts.
How I came to the solution
First I appraised the problem and considered a solution, the easiest solution in SwiftUI is NOT to use a UISearchBar, but I realize that your requirements call for it, which is very real to how the real world works. Often times, when working, you'll come across a challenge with a simple solution and you'll be asked to do it in a non-simple manner, but you have to do it that way.
Once I had the requirements I looked into the modifier
.toolBar(...)
. Turns out it requires aToolbarItem(..)
. I needed more information than that; however, and I found it by following the trail. I learned that theToolbarItem(...)
acceptssome View
. Which means we can slap whatever we want into there. You could place an image, a searchbar, a text string, or whatever. I also learned that you can change the arguments in it's constructor forplacement:
which has several options including.bottom
or.principal
which is useful if you're putting a toolbar at the top or the bottom. In the case of this solution I placed it using.principal
NavigationLink(destination: Text("Some Link")) { Text("Button Text") }.toolbar { ToolbarItem(placement: .principal) { SearchBar2(text: $sampleText) } }
Problems I Encountered
- The first problem I encountered, and you'll soon find out if you copy this code, is that the
SearchBar2(...)
is going to appear on the firstNavigationView
which, if that's where you want it, great, but I suspect you may want it on the "Presented View" which is an important skill to begin to understand.
Navigation Crash-Course
The reason the toolbar is appearing on the first page, and not the navigated to page, is because it's constructed on the first page. You may be asking yourself, "Well how do I get it to appear on the second page?"
To have it appear on the second page you simply move the
ToolbarItem(...)
to that second page, typically on the root view as a modifier. The reason for that is such that it can be constructed when that view itself is constructed.NavigationViews are like a stack of cards, literally, and when you go from one to the other the ones "Behind" are held onto, and the ones ahead are created as you begin to navigate. This, again, is super important because if you navigate back, then attempt to go forward again, you WILL lose data if the view is destroyed, or in
Swifty
terms,deInit(...)
. There are tons of options available to hold that data and restore it later, such as@AppStorage
orUserDefaults
.A helpful way to demonstrate this conundrum of view construction, deconstruction is to have a look at this block of code, before running it, ask yourself what will happen? Notice the
.toolBar(...)
is added to the rootText(...)
so will it appear on the first view, or the second?NavigationView { NavigationLink(destination: Text("Some Link")) { Text("Button Text") .toolbar { ToolbarItem(placement: .principal) { SearchBar2(text: $sampleText) } } } }
The answer is that it will appear on the first view, once again. You might be asking, "Why?" It's because the
Text(...)
is constructed on the first view along with all the modifiers associated. If however we add the toolbar as a modifier to the second view, the SearchBar2 will appear there.struct ContentView: View { @State var sampleText = "" var body: some View { NavigationView { NavigationLink(destination: SecondView()) { Text("To Second View") } } } } struct SecondView: View { var body: some View { Text("Button Text") .toolbar { ToolbarItem(placement: .principal) { SearchBar2(text: .constant("Test")) } } } }
I hope this adds some value to your learning, keep trucking on, and enjoy coding.
Hot Tip
OPT Click on a method/modifier or basically anything will give you a description of what it does. It's really useful for finding things out, such as what sort of view is being expected. There's even a handy link that says "Open Developer Documentation" which will provide even more data.