swiftUI: Updating button and toggle text when user tapping (button inside button) - swiftui

I have a good feature by translating text on fly by toggling text by tapping.
But a lot of server working is too expansive.
I would like to update it little, but not imagine, how can I do this by toggling text in view.
Logic now:
when text is loaded - It automaticaly sending to server request user's language phone & if it != language on view - it can be translated. This feature load a lot of traffic api.
Logic I whant:
Text simply loaded -> Button with text (Do you want to translate text?) & only after that - by tapping on it - it will send requests to server.
Have tried by .onTapGesture and Toggle widget, but cannot realize that. By this logic - same text on original language will duplicate below text. (But in this case request really sending only when user tap again).
But view not good...
I want to make this logic:
Original text with button below (Do you want to translate?)->Tap on it->appearing another button (Translate)->Tapping on it - sending request and toggeling text.
Here is code now:
UPD. Full View
struct stepDetailItemView: View {
var item: stepDetailItem
#ObservedObject var translate = GoogleTranslate()
var recipeName:String?
#State var image: Image? = nil
var body: some View {
VStack {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading) {
VStack {
if image != nil {
GeometryReader { reader in
image!
.resizable()
.aspectRatio(contentMode: .fill)
.offset(y: -reader.frame(in: .global).minY)
.frame(width: UIScreen.main.bounds.width, height: reader.frame(in: .global).minY + 350)
}.frame(height: 350, alignment: .center)
}
else {
DetailCardShimmer().padding(.top)
}
}
Group {
VStack(alignment: .leading) {
if recipeName != nil {
Text("\(recipeName!)").font(.subheadline)
.foregroundColor(Color("SubLabelTextColor"))
.padding(.top)
}
else {
Text("No recipe name").font(.headline).padding(.horizontal)
}
VStack(alignment: .leading) {
HStack {
Text(LocalizedStringKey("Step")).font(.headline)
.foregroundColor(Color("LabelTextColor"))
.bold()
Text("\(item.number+1)").font(.headline)
.foregroundColor(Color("LabelTextColor"))
.bold()
}
Text(LocalizedStringKey("Step description")).font(.headline)
.bold()
.foregroundColor(Color("LabelTextColor"))
}.padding(.top)
if (item.description != nil) {
if let text = translate.getText() {
Text(text.str).font(.subheadline)
.foregroundColor(Color("SubLabelTextColor"))
.padding(.bottom)
HStack {
Spacer()
if text.canBeTranslated {
Button(action: {
text.toggleTexts()
}) {
Text(text.isTranslated ? LocalizedStringKey("Original") : LocalizedStringKey("Translate"))
.font(.system(size: 14))
.foregroundColor(Color("SubLabelTextColor"))
.padding(.leading)
Image(text.isTranslated ? "translated_white" : "translate_white")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 50, height: 50)
.padding()
}.frame(height: 60)
.modifier(NEUROMPHIC_BUTTON_PROFILE_MODIFICATOR())
.padding(.top, 10)
}
}
} else {
Text(item.description!).font(.subheadline)
.foregroundColor(Color("SubLabelTextColor"))
.padding(.bottom).onAppear(perform: {
if let description = item.description {
translate.setText(str: description)
}
})
}
}
else {
StepDescriptionLoadingShimmer()
}
}.padding(.horizontal).padding(.bottom)
.background(Color("MainBackground"))
}
}
}
}.background(Color("MainBackground").edgesIgnoringSafeArea(.vertical))

Related

How do i implement .tag when jumping to a posiiton in a scrollview?

I have added the .id(1) to the positions in the scrollview and can get it to work as expected if i add a button inside the scrollview but i want to use a picker to jump to the .id and outside the scrollview.
Im new to this.
I have this code:
if i use this button it works as expected although its placed inside the scrollview...
Button("Jump to position") {
value.scrollTo(1)
}
This is my picker...
// Main Picker
Picker("MainTab", selection: $mainTab) {
Text("iP1").tag(1)
Text("iP2").tag(2)
Text("Logo").tag(3)
Text("Canvas").tag(4)
}
.frame(width: 400)
.pickerStyle(.segmented)
ScrollViewReader { value in
ScrollView (.vertical, showsIndicators: false) {
ZStack {
RoundedRectangle(cornerRadius: 20)
// .backgroundStyle(.ultraThinMaterial)
.foregroundColor(.black)
.opacity(0.2)
.frame(width: 350, height:185)
// .foregroundColor(.secondary)
.id(1)
There are 2 things you are mixing here:
The tag modifier is to differentiate elements among certain selectable views. i.e. Picker TabView
You can't access the proxy reader from outside unless you make it available. In other words the tag in the Picker and the ScrollViewReader does not have a direct relationship, you have to create that yourself:
import SwiftUI
struct ScrollTest: View {
#State private var mainTab = 1
#State private var scrollReader: ScrollViewProxy?
var body: some View {
// Main Picker
Picker("MainTab", selection: $mainTab) {
Text("iP1").tag(1)
Text("iP2").tag(2)
Text("Logo").tag(3)
Text("Canvas").tag(4)
}
.frame(width: 400)
.pickerStyle(.segmented)
.onChange(of: mainTab) { mainTab in
withAnimation(.linear) {
scrollReader?.scrollTo(mainTab, anchor: .top)
}
}
ScrollViewReader { value in
ScrollView (.vertical, showsIndicators: false) {
ForEach(1...4, id: \.self) { index in
ZStack {
RoundedRectangle(cornerRadius: 20)
.foregroundColor(.black)
.opacity(0.2)
.frame(width: 350, height: 500)
Text("index: \(index)")
}
.id(index)
}
}
.onAppear {
scrollReader = value
}
}
}
}

Set View Picker Header Color?

Is it possible to add background color to the top of a picker view? I'm specifically interested in the area from the top of the view down to just below the navigation bar. I have made all my app tab and navigation views with the same header appearance but haven't had any luck with the pickers.
I would like to set the picker backgrounds to look like this:
Here is a picker view that I would like to add background color.
Here is what happens when adding call to DrawNavBox() just after the Picker (See code below). I need help pushing the green block to the top of the view.
The code below is what generates the Home Currency green header above
var body: some View {
GeometryReader { g in
VStack {
DrawNavBox(g: g) // draw green header
Text("Your home currency is:")
.padding(.top, g.size.height * 0.10)
Text("\(base.baseCur.baseCurrency) \(base.baseCur.baseCountry)")
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
.padding(15)
.background(Color("Dark Green"))
.cornerRadius(20)
.padding(.trailing, g.size.width * 0.19)
.padding(.leading, g.size.width * 0.22)
Form {
Section(header: Text("Select Home Currency")) {
Picker("Home Currency", selection: $gotBase) {
DrawNavBox(g: g)
ForEach(0 ..< currCountry.count, id: \.self) { item in
VStack(alignment: .leading) {
Text(currCountry[item])
.font(.subheadline)
.padding(.leading, 25)
HStack {
Text(currSymbol[item])
Text(currName[item])
}
.font(.caption)
.padding(.leading, 25)
}
}
}
}
}
}
.navigationBarTitle(Text("Home Currency"), displayMode: .inline)
.navigationBarItems(trailing:
Button(action: {
// search for new base currency
self.changeBaseCurrency()
}) {
Text ("Save").bold()
}
)}
}
}
This is the code I have been using to set the navigation bar area green
struct DrawNavBox: View {
var g: GeometryProxy
var body: some View {
ZStack (alignment: .top) {
Color(.systemGreen)
.frame(height: (g.safeAreaInsets.top), alignment: .top)
.ignoresSafeArea()
}
}
}

LazyVGrid miss handles the tap

I am using the LazyVGrid in my app and a faced such problem
There are two columns in the grid and that is handling the tap in the wrong place
On the screenshot, red arrows - places of the click
I used to think that would open the MacBookAir's card, but that opens the iPadAir's one
struct GalleryView: View {
var ns: Namespace.ID
#EnvironmentObject private var model: Model
let columns = [
GridItem(.adaptive(minimum: 400))
]
var body: some View {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(Card.list) { card in
if card != model.selectedCard {
GalleryCardView(card: card, ns: ns)
.onTapGesture {
withAnimation(.openCard) {
model.selectedCard = card
model.isDetailShown = true
}
}
} else {
Color(.clear)
.frame(width: 400, height: 400)
}
}
}
}
}
Here is the GalleryCardView:
struct GalleryCardView: View {
let card: Card
var ns: Namespace.ID
var body: some View {
ZStack {
Image(card.image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 400, height: 400)
.clipped()
.matchedGeometryEffect(id: "\(card.id)-image", in: ns)
.overlay(alignment: .bottom) {
Text(card.title)
.font(.largeTitle)
.fontWeight(.bold)
.frame(maxWidth: .infinity)
.frame(height: 50)
.padding()
.background {
Rectangle()
.fill(.ultraThinMaterial)
}
.matchedGeometryEffect(id: "\(card.id)-title", in: ns)
}
}
.clipShape(RoundedRectangle(cornerRadius: 30))
.matchedGeometryEffect(id: "\(card.id)-card", in: ns)
}
}
UPD:
I had some work around and find out that the problem is with the GalleryCardView - even though the image is clipped, it took extra space outside the frame. If I change the aspect ratio to .fit, everything works fine

How to know which button is pressed in a list of views - SwiftUI

I have a ResidentsView() which contains a list of ResidentCell(), and every cell has its own button. This button adds a stripe to one's name. Currently, it only increases the top resident because I've given that as the default resident for testing. However, I want the stripes of the person to increase when the button beneath their name is pressed. How do I access which button is pressed out of the four cells so I know who's stripes I have to increase?
struct ResidentCell: View {
#State var showStripeView: Bool = false
var resident: Resident
var body: some View {
VStack {
HStack{
VStack(alignment: .leading) {
Text(resident.name)
.font(.title)
Text(String(resident.year))
.font(.caption)
}.padding(.leading)
Spacer()
Text("Streepjes: \(numberOfStripes(stripes: resident.stripes))")
.font(.subheadline)
.fontWeight(.bold)
.padding(.trailing)
.foregroundColor(resident.stripes > 4 ? .red : .black)
}
Divider()
.frame(width: 200)
Button(action: {
self.showStripeView.toggle()
}) {
Text("Add stripe")
.padding()
.border(Color.green, width: 5)
.foregroundColor(.black)
.cornerRadius(5)
}
.padding(.top, 5)
.buttonStyle(BorderlessButtonStyle())
.sheet(isPresented: $showStripeView) {
AddStripeView(currentUser: Resident.testResidents[0])
}
}
}
func numberOfStripes(stripes: Int) -> String {
let count = stripes
var stripeString = ""
for _ in (1...count) {
stripeString.append("I")
}
return stripeString
}
}

How to select checkbox button and NavigationLink to another view independently in SwiftUI List row

I hope to make a list just like to-do list, the cell has a checkbox and Text. And I want when list cell selected, the view could be translate to detailView. Meanwhile I also could select the checkbox, but don't trigger the view translate to the detailView.
I removed the List{}, the code run ok, but the List property and method can't use any more. So I plan to code a customer list view. But I don't think it will be the good method. So if I still can't find a good way to make it, I may implement in this way at last.
NavigationView {
VStack {
List {
ForEach(todayDietModel.todayDietItems) { item in
NavigationLink(destination: DietItemDetailView()) {
TodayDietRow(todayDietItem: item)
.animation(.spring())
}
}
}
}
.frame(width: 352, height: 400)
.background(Color.white)
.cornerRadius(16)
}
struct TodayDietRow: View {
#State var isFinished: Bool = false
var body: some View {
HStack(spacing: 20.0) {
Button(action: {self.isFinished.toggle()}) {
if self.isFinished {
Image("icon_checked_s001")
.resizable()
.renderingMode(.original)
.aspectRatio(contentMode: .fit)
.frame(width: 24, height: 24)
} else {
Image("icon_unchecked_s001")
.resizable()
.frame(width: 24, height: 24)
}
}
.padding(.leading, 10)
Text("DietName")
.font(.system(size:13))
.fontWeight(.medium)
.foregroundColor(Color.black)
}
.frame(width: 327, height: 48)
}
}
This works quite well with a gesture on the checkbox image. Two Buttons didn't work since every tap went to both buttons.
struct TodayTodoView2: View {
#ObservedObject var todayDietModel = TodayDietModel()
func image(for state: Bool) -> Image {
return state ? Image(systemName: "checkmark.circle") : Image(systemName: "circle")
}
var body: some View {
NavigationView {
VStack {
List {
ForEach($todayDietModel.todayDietItems) { (item: Binding<TodayDietItem>) in
HStack{
self.image(for: item.value.isFinished).onTapGesture {
item.value.isFinished.toggle()
}
NavigationLink(destination: DietItemDetailView2(item: item)) {
Text("DietNamee \(item.value.dietName)")
.font(.system(size:13))
.fontWeight(.medium)
.foregroundColor(Color.black)
}
}
}
}
.background(Color.white)
}
}
}
}