I have a view model for country codes, names, flags...
How can I search through that list once I fetch it?
My search code is like this:
ForEach(countryVM.diallingCode.filter{$0.name.lowercased()
.contains(self.textlowercased())}, id:\.self) { option in
VStack{
Divider()
.frame(maxWidth:.infinity)
HStack{
Image(option.flag)
.resizable()
.frame(width:30, height:30)
Text(option.name)
.font(.custom("Ubuntu-Light", size:12))
Spacer()
}.frame(maxWidth:.infinity)
}
}
There is array in diallingCode, it shows me the countries, but I can't search through them.
The same code worked when I used static values.
To make it easier on yourself, move
countryVM.diallingCode.filter{$0.name.lowercased().contains(self.textlowercased())}`
to a method on your view model, e.g.
func filteredDialingCodes(for text: String) {
let codes = diallingCode.filter{$0.name.lowercased().contains(text.lowercased())}
print(codes) // see what's happening
return codes
}
Then call like:
ForEach(filteredDialingCodes(for: text), id:\.self) { option in
Not only is it clearer what you're trying to do, it also will allow you to see what values are returned when filtering.
I have a set of seven pages. I have made my own scrollable view.
I have a working solution where I have the current page and attach gestures to it like this...
switch page {
case .EyeTest:
EyeTestView(sd: $sd)
.gesture(DragGesture(minimumDistance: swipeMin)
.onEnded { value in
if value.translation.width < -swipeMin { page = Page.Tone }
} )
...`
This worked. It was rather clunky, but I could animate the drags better if I worked at it. However, the tab view worked beautifully with TabView for up to five entries. So, I thought I might be able to span the solution using two tab views. Here is what I did, concluding the custom scrollbar at the top...
VStack() {
ScrollViewReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
pageButton(Page.EyeTest, "eyeglasses", "EyeTest", proxy)
pageButton(Page.Tone, "pause.rectangle", "Tone", proxy)
pageButton(Page.Chart, "square.grid.4x3.fill", "Chart", proxy)
pageButton(Page.ByEye, "eye", "ByEye", proxy)
pageButton(Page.List, "list.bullet", "List", proxy)
pageButton(Page.Camera, "camera", "Camera", proxy)
pageButton(Page.Settings, "gear", "Settings", proxy)
}
}
.onAppear { proxy.scrollTo(Page.ByEye, anchor: .center) }
.onChange(of: page) { page in
withAnimation {
proxy.scrollTo(page, anchor: .center)
}
}
}
if (page == Page.EyeTest || page == Page.Tone || page == Page.Chart) {
TabView(selection:$page) {
EyeTestView(sd: $sd).tag(Page.EyeTest)
ToneView(sd: $sd).tag(Page.Tone)
ChartView(sd: $sd).tag(Page.Chart)
ByEyeView(sd: $sd).tag(Page.ByEye)
ListView(sd: $sd).tag(Page.List)
}.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
} else {
TabView(selection:$page) {
ChartView(sd: $sd).tag(Page.Chart)
ByEyeView(sd: $sd).tag(Page.ByEye)
ListView(sd: $sd).tag(Page.List)
CameraView(sd: $sd).tag(Page.Camera)
SettingsView(sd: $sd).tag(Page.Settings)
}.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
}
}`
However, this does not scroll past the 5th entry.
I would be interested to know why this doesn't work, but I would be happy to replace it with something equivalent that does work. I would also like to know how people handle things like books, which have lost more pages. I feel I ought to make two synchronised scrollable lists.
Yes, maybe it is bad API to have a flat menu with more than five entries. However, some of the ones at the end of the list are rarely used, but need to be discoverable.
I am using Xcode 14.0.1 with iOS 16.0.
Here is a solution that seems to work for me. It is quite close to my original bodge: we have two TabViews, and swap between them depending on the current page so we can always have the neighbours on either side.
I had written my own scrollable index to replace the TabView indexes, but you might make do with the original fittings. I have only been doing Swift for a month, so this may not be the best code, so please post your improvements.
`
enum Page {
case EyeTest
case Tone
case Chart
case ByEye
case List
case Camera
case Settings
}
#AppStorage("menu") private var menu = "Measure"
#State private var page = Page.ByEye
func setMenu(_ page: Page) {
menu = (page == Page.EyeTest || page == Page.Tone || page == Page.Chart ) ? "Test" : "Measure"
}
func pageButton(_ select: Page, _ icon: String, _ title: String, _ proxy: ScrollViewProxy) -> some View {
return Button {
page = select
setMenu(page)
withAnimation {
proxy.scrollTo(select, anchor: .center)
}
} label: {
VStack {
Image(systemName: icon)
Text(title)
}
.frame(width: tabW)
}
.foregroundColor( page == select ? Color.white : Color.gray )
.id(select)
}
var body: some View {
VStack() {
ScrollViewReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack() {
pageButton(Page.EyeTest, "eyeglasses", "EyeTest", proxy)
pageButton(Page.Tone, "pause.rectangle", "Tone", proxy)
pageButton(Page.Chart, "square.grid.4x3.fill", "Chart", proxy)
pageButton(Page.ByEye, "eye", "ByEye", proxy)
pageButton(Page.List, "list.bullet", "List", proxy)
pageButton(Page.Camera, "camera", "Camera", proxy)
pageButton(Page.Settings, "gear", "Settings", proxy)
}
}
.onAppear { proxy.scrollTo(page, anchor: .center) }
.onChange(of: page) { page in
withAnimation {
proxy.scrollTo(page, anchor: .center)
}
}
}
if (menu == "Test") {
TabView(selection:$page) {
EyeTestView(sd: $sd).tag(Page.EyeTest)
ToneView(sd: $sd).tag(Page.Tone)
ChartView(sd: $sd).tag(Page.Chart)
ByEyeView(sd: $sd).tag(Page.ByEye)
SettingsView(sd: $sd).tag(Page.Settings)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
.onChange(of: page) { page in
setMenu(page)
}
} else {
TabView(selection:$page) {
ChartView(sd: $sd).tag(Page.Chart)
ByEyeView(sd: $sd).tag(Page.ByEye)
ListView(sd: $sd).tag(Page.List)
CameraView(sd: $sd).tag(Page.Camera)
SettingsView(sd: $sd).tag(Page.Settings)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
.onChange(of: page) { page in
setMenu(page)
}
}
`
I have an app full of stories, and I want to see which stories users are reading.
I already installed Firebase Analytics, but all I'm seeing is this kind of content:
I want to be able to see the specific title of the stories users are reading.
This is the ForEach loop I'm using:
ForEach(modelData.stories.shuffled()) { item in
if item.paid == false {
NavigationLink {
StoryDetails(story: item)
} label: {
GeneralCard(isLocked: false, story: item)
}
}
}
And on StoryDetails I have this code, I would like to see the title of the story inside Firebase Analytics.
VStack (spacing: 0) {
Text(story.title)
.font(AppFont.detailTitle())
.bold()
.multilineTextAlignment(.center)
.foregroundColor(.mainColor)
Text(story.category.rawValue)
.font(AppFont.detailDescription())
.multilineTextAlignment(.center)
.padding()
.foregroundColor(.mainColor)
VStack(alignment: .leading, spacing: 20) {
ForEach(story.text, id: \.self) { item in
VStack {
Text(item)
.font(AppFont.detailText())
}
}
}
//.padding(.horizontal, 10)
}
Is this something that can be achieved?
Thanks in advance
You could set this up by manually logging your screenviews instead. For SwiftUI, it is something like this:
struct ContentView: View {
var body: some View {
Text("Hello, world!")
// Logging screen name with class and a custom parameter.
.analyticsScreen(name: "main_content",
class: "ContentView",
extraParameters: ["my_custom_param": 5])
// OR Logging screen name only, class and extra parameters are optional.
.analyticsScreen(name: "main_content")
}
}
I got this text:
Text("Indem du fortfährst, stimmst du unseren ") +
Text("Nutzungsbedingungen")
.underline()
.foregroundColor(Color("ClickableLink")) +
Text(" und unserer ") +
Text("Datenschutzerklärung")
.underline()
.foregroundColor(Color("ClickableLink")) +
Text(" zu")
I'd like to open a new view using NavigationLink after taping on Nutzungsbedingungen or Datenschutzerklärung, both need to open different views.
I've seen those answers:
SwiftUI tappable subtext
but those are either not what I need or trying them gives me errors and I don't know how to modify them since I'm absolutely new to swift/swiftui
Here are two possible approaches, one using the new NavigationStack with NavigationDestination and the other using popOver.
Both have the downside that you can't concatenate Text views within them. But you can still work with HStack/VStack to define the layout.
enum Legals: Identifiable {
case nutzungsbedingungen
case datenschutzerklärung
var id: Legals { self }
}
struct ContentView: View {
#State private var selected: Legals?
var body: some View {
NavigationStack {
// NavigationLink version
VStack(alignment: .leading) {
Text("Indem du fortfährst, stimmst du unseren")
NavigationLink("Nutzungsbedingungen", value: Legals.nutzungsbedingungen)
Text("und unserer")
NavigationLink("Datenschutzerklärung", value: Legals.datenschutzerklärung)
Text("zu")
}
.navigationDestination(for: Legals.self) { selection in
switch selection {
case .datenschutzerklärung:
Text("Datenschutzerklärung")
case .nutzungsbedingungen:
Text("Nutzungsbedingungen")
}
}
Divider()
// popOver Version
VStack(alignment: .leading) {
Text("Indem du fortfährst, stimmst du unseren ")
Text("Nutzungsbedingungen")
.underline()
.onTapGesture {
selected = .nutzungsbedingungen
}
Text("und unserer ")
Text("Datenschutzerklärung")
.underline()
.onTapGesture {
selected = .nutzungsbedingungen
}
Text("zu")
}
.popover(item: $selected) { selected in
switch selected {
case .datenschutzerklärung:
Text("Datenschutzerklärung")
case .nutzungsbedingungen:
Text("Nutzungsbedingungen")
}
}
}
}
}
I have a environment object that is an array, and a panel in my settings page where the user can add/remove values to the array...here is the list of items in the array displayed visually for the user:
ScrollView {
VStack {
ForEach(self.env.numbers.indices, id: \.self) { number in
ZStack {
HStack {
Text("#\(number + 1): ")
.font(Font.custom(K.font.montserratMedium, size: 17.0))
.foregroundColor(Color(K.SettingsTextColor))
// Spacer()
NumberTextField(self.env.numbers[number], text: self.$env.numbers[number])
}
}
}
}
}
and at the bottom of my view is a toggle to change the number of items in the array:
Stepper(onIncrement: {
self.env.numbers.append("12345")
print("default number should be added")
print(self.env.numbers.indices)
print(self.env.numbers)
print("count = \(self.env.numbers.count)")
},
onDecrement: {
if self.env.numbers.count != 0 {
print("number should be removed")
self.env.numbers.removeLast(1)
// also tried self.env.numbers = self.env.numbers.droplast() but this didnt work
print(self.env.numbers.indices)
print(self.env.numbers)
print("count = \(self.env.numbers.count)")
}
the NumberTextField view is a UIKit UITextField wrapper, and seems to work fine. Everything about this works fine until I try to decrease the stepper, and I get the Index out of range error (which surprisingly shows the error happening in the app delegate)...any ideas how to fix this?