I am fairly new to Swift, so please bear with me.
screenshot when nothing's tapped
screenshot when one button is tapped
I am trying to turn the other buttons, which are not tapped, inactive and change the opacity to 0.5.
So far I have tried using the $bindings but that did not work the way I wanted it to.
Here is the Code for my SelectionButtons:
struct SelectionButton: View {
var buttonText = "Selection Button"
var buttonColor = Color.white
var buttonWidth: CGFloat = 150
var active = false
var body: some View {
ZStack {
if active {
RoundedRectangle(cornerRadius: 45)
.frame(width: buttonWidth, height: 50)
.foregroundColor(Color("neonGreen"))
.shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.25)), radius:9, x:0, y:0)
Text(buttonText)
.font(.system(size: 18))
.foregroundColor(.black)
.bold()
}else{
RoundedRectangle(cornerRadius: 45)
.frame(width: buttonWidth, height: 50)
.foregroundColor(buttonColor)
.shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.25)), radius:9, x:0, y:0)
Text(buttonText)
.font(.system(size: 18))
.foregroundColor(.black)
}
}
}
And this is how I use them:
#State var gelbesBlatt = false
var body: some View {
NavigationView {
Button(action: {
self.gelbesBlatt.toggle()
}, label: {SelectionButton(buttonText: "gelb", buttonColor: .white, buttonWidth: 150, active: gelbesBlatt)
})
}
}
Thank you!
If you have an idea how I could add combinations, so that specific buttons which belong together (like "Ganzes Blatt > gelb" and "Ränder > braun") can be selected together, that would be awesome!
Check this:
#State var gelbesBlatt = false
#State var btnBraun = false
var body: some View {
NavigationView {
HStack {
Button(action: {
self.toggleButton(button: 0)
}, label: {SelectionButton(buttonText: "gelb", buttonColor: .white, buttonWidth: 150, active: gelbesBlatt)
})
Button(action: {
self.toggleButton(button: 1)
}, label: {SelectionButton(buttonText: "braun", buttonColor: .white, buttonWidth: 150, active: btnBraun)
})
}
}
}
func toggleButton(button: Int) {
if(button == 0) {
self.gelbesBlatt = true
self.btnBraun = false
} else {
self.gelbesBlatt = false
self.btnBraun = true
}
self.gelbesBlatt.toggle()
self.btnBraun.toggle()
}
Related
I'm currently running into a problem with a sheet not closing when the close button is pushed or the save button has completed transferring the data from the form to the list in homeview. Below is the code for a custom tab bar that when "New Survey" is pushed it will open the formView. The formView has two buttons closed and save. I would like it that when either one of these buttons are selected that it closes the formView.
struct FormView: View {
#Binding var surveys: [Survey]
#State var customerName = ""
#State var city = ""
#State var state = ""
#State var lineName = ""
#State var date = Date()
#Binding var isPresented: Bool
#State var showingFormView = false
var body: some View {
VStack{
ZStack {
NavigationView {
VStack {
ZStack {
Form {
Section(header: Text("Survey Info").font(.custom("Roboto-Light", size: 24)).foregroundColor(Color(red: 0.00, green: 0.188, blue: 0.42)).offset(x: -20)) { TextField("Customer Name", text: $customerName)
.padding(.vertical, 10.0)
.font(.custom("Roboto-Light", size: 18))
TextField("Customer City", text: $city)
.padding(.vertical, 10.0)
.font(.custom("Roboto-Light", size: 18))
TextField("State", text: $state)
.padding(.vertical, 10.0)
.font(.custom("Roboto-Light", size: 18))
TextField("Line Name", text: $lineName)
.padding(.vertical, 10.0)
.font(.custom("Roboto-Light", size: 18))
DatePicker("Date", selection: $date, displayedComponents: .date)
.padding(.vertical, 10.0)
.font(.custom("Roboto-Light", size: 18))
}
}
VStack (alignment: .center){
Spacer()
HStack {
**Button(action: {
self.showingFormView = false
self.isPresented = false
}, label: {
Text("Close")
.font(.custom("Roboto-Light", size: 23))
.foregroundColor(Color.white)
.frame(width: 150.0, height: 50.0, alignment: .center)
.background(Color(hue: 0.001, saturation: 0.768, brightness: 0.975))
.cornerRadius(3)
})
Button(action: {
// Save survey data to UserDefaults or any other means
let survey = Survey(customerName: customerName, city: city, state: state, lineName: lineName, date: date)
self.surveys.append(survey)
// Clear text fields
self.customerName = ""
self.city = ""
self.state = ""
self.lineName = ""
self.date = Date()
// Close FormView
self.showingFormView = false
self.isPresented = false
}, label: {
Text("Save")
.font(.custom("Roboto-Light", size: 23))
.foregroundColor(Color.white)
.frame(width: 150.0, height: 50.0, alignment: .center)
.background(Color(red: 0.451, green: 0.729, blue: 0.251))
.cornerRadius(3)
})**
}
}
}
}
.navigationBarHidden(true)
}
}
}
}
}
Custom Tab Bar
struct CustomTabBar: View {
#Binding var surveys: [Survey]
#Binding var isPresented : Bool
#State var showingFormView = false
var body: some View {
NavigationView{
VStack {
Spacer()
HStack (alignment: .bottom) {
Button {
} label: {
GeometryReader { geo in
VStack (alignment: .center, spacing: 4) {
Image(systemName: "magnifyingglass")
.resizable()
.scaledToFit()
.frame(width: 32, height: 32)
Text("Search")
.font(.custom("Roboto-Light", size: 14))
}
.frame(width: geo.size.width,height: geo.size.height)
}
}
.tint(Color(red: 0.0, green: 0.188, blue: 0.42))
**Button(action: {
showingFormView = true
}) {
GeometryReader { geo in
VStack(alignment: .center, spacing: 4) {
Image("PSIcon")
.resizable()
.scaledToFit()
.frame(width: 38, height: 38)
Text("New Survey")
.font(.custom("Roboto-Light", size: 14))
.tint(Color(red: 0.0, green: 0.188, blue: 0.42))
}
.frame(width: geo.size.width, height: geo.size.height)
}
}
.sheet(isPresented: $showingFormView) {
FormView(surveys: $surveys, isPresented: $isPresented) }
//code allows you to call up a specific view so long as a var is indicated
**
Button {
} label: {
GeometryReader { geo in
VStack (alignment: .center, spacing: 4) {
Image(systemName: "arrow.up.arrow.down")
.resizable()
.scaledToFit()
.frame(width: 35, height: 35)
Text("Sort")
.font(.custom("Roboto-Light", size: 14))
}
.frame(width: geo.size.width,height: geo.size.height)
}
}
.tint(Color(red: 0.0, green: 0.188, blue: 0.42))
}
.frame(height: 50.0)
}
}
}
}
The save button saves and creates a new item in the dynamic list but it does not close the formView. The only way for me to close the view is by swiping down on the screen.
I've been trying to find a way to change the color of every flashcard in my app. I am struggling with understanding how to use #Binding, which I believe is the best solution. I ended up creating static variables in my Card struct, which allows me to change the card colors via onTapGesture actions. It seems very slow and I know there has to be a better way to accomplish this. Below is my code. The action for changing the color is inside the Circle() in ContentView. Thank you in advance. Also could someone please tell me how to resize screenshots to post in here? The solutions online make my screenshots super blurry so I hate uploading them
CardView
import SwiftUI
struct CardView: View {
let card: Card
var removal: (() -> Void)? = nil
#State private var isShowingAnswer = false
#State private var changeColors = false
#State private var offset = CGSize.zero
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(LinearGradient(colors: [Card.gradCs[Card.selec][0].opacity(1 - Double(abs(offset.width / 50))), Card.gradCs[Card.selec][1].opacity(1 - Double(abs(offset.width / 50)))], startPoint: .topLeading, endPoint: .bottomTrailing))
.background(RoundedRectangle(cornerRadius: 25, style: .continuous).fill(offset.width > 0 ? .green : .red))
.shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.25)), radius:6, x:1, y:8)
VStack(spacing: 20){
Text(card.prompt)
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(.white.opacity(0.8))
if isShowingAnswer {
Text(card.answer)
.font(.title)
.fontWeight(.semibold)
.foregroundColor(Color(#colorLiteral(red: 0.8947916626930237, green: 0.8666666746139526, blue: 1, alpha: 1)))
}
}
.padding()
.multilineTextAlignment(.center)
}
.frame(width: 450, height: 250)
.rotationEffect(.degrees(Double(offset.width / 5)))
.offset(x: offset.width * 5, y: 0)
.opacity(2 - Double(abs(offset.width / 50)))
.gesture(
DragGesture()
.onChanged { gesture in
offset = gesture.translation
}
.onEnded { _ in
if abs(offset.width) > 100 {
removal?()
} else {
offset = .zero
}
}
)
.onTapGesture {
isShowingAnswer.toggle()
}
}
}
struct CardView_Previews: PreviewProvider {
static var previews: some View {
CardView(card: Card.example)
.previewInterfaceOrientation(.portraitUpsideDown)
}
}
ContentView
import SwiftUI
extension View {
func stacked(at position: Int, in total: Int) -> some View {
let offset = Double(total - position)
return self.offset(x: 0, y: offset * 10)
}
}
struct ContentView: View {
#Environment(\.accessibilityDifferentiateWithoutColor) var differentiateWithoutColor
#State private var cards = Array(repeating: Card.example, count: 10)
#State private var timeRemaining = 100
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
#Environment(\.scenePhase) var scenePhase
#State private var isActive = true
var body: some View {
ZStack {
VStack {
HStack {
Text("\(timeRemaining / 60):\(timeRemaining % 60, specifier: "%02d")")
.font(.title)
.fontWeight(.semibold)
.foregroundColor(.white)
.padding(.horizontal, 20)
.padding(.vertical, 5)
.background(.black.opacity(0.75))
.clipShape(Capsule())
Circle()
.fill(LinearGradient(colors: Card.gradCs[Card.selec], startPoint: .topLeading, endPoint: .bottomTrailing))
.onTapGesture {
withAnimation {
if Card.selec == 0 {
Card.selec = 1
} else {
Card.selec = 0
}
}
}
.frame(width: 40, height: 40)
}
ZStack {
ForEach(0 ..< cards.count, id: \.self) { index in
CardView(card: cards[index]) {
withAnimation {
removeCard(at: index)
print(self.offset())
}
}
.stacked(at: index, in: cards.count)
}
}
.allowsHitTesting(timeRemaining > 0)
if cards.isEmpty {
Button("Start Again", action: resetCards)
.padding()
.background(.white)
.foregroundColor(.black)
.clipShape(Capsule())
}
}
if differentiateWithoutColor {
VStack {
Spacer()
HStack {
Image(systemName: "xmark.circle")
.padding()
.background(.black.opacity(0.7))
.clipShape(Circle())
Spacer()
Image(systemName: "checkmark.circle")
.padding()
.background(.black.opacity(0.7))
.clipShape(Circle())
}
.foregroundColor(.white)
.font(.largeTitle)
.padding()
}
}
}
.onReceive(timer) { time in
guard isActive else { return }
// Use if let when the non-nil case is valid. Use guard when the nil case represents some sort of error.
// use guard when there should only be one result.. use if when you need ELSE to do something
if timeRemaining > 0 {
timeRemaining -= 1
}
}
.onChange(of: scenePhase) { newPhase in
if newPhase == .active {
if cards.isEmpty == false {
isActive = true
}
} else {
isActive = false
}
}
}
func removeCard(at index: Int) {
cards.remove(at: index)
if cards.isEmpty {
isActive = false
}
}
func resetCards() {
cards = Array(repeating: Card.example, count: 10)
timeRemaining = 100
isActive = true
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Card
import Foundation
import SwiftUI
struct Card {
let prompt: String
let answer: String
static var example = Card(prompt: "What is the name of Chicago's NFL Team?", answer: "Da Bears")
static var gradCs = [[Color(#colorLiteral(red: 0.6039215922355652, green: 0.49803921580314636, blue: 1, alpha: 1)), Color(#colorLiteral(red: 1, green: 0.49803924560546875, blue: 1, alpha: 1))], [Color(#colorLiteral(red: 0.6039215922355652, green: 1, blue: 1, alpha: 1)), Color(#colorLiteral(red: 0.6039215922355652, green: 0.49803921580314636, blue: 1, alpha: 1))]]
static var selec = 0
}
binding is a possible way to change colors, but I prefer the use of ObservableObject
to manage the set of Cards and their color selection.
The code in my answer shows how to use a ObservableObject CardManager to change all cards color
resulting from your selection in Circle.
// manages the cards and all cards color selection
class CardManager: ObservableObject {
let gradCs = [[Color(#colorLiteral(red: 0.6039215922355652, green: 0.49803921580314636, blue: 1, alpha: 1)), Color(#colorLiteral(red: 1, green: 0.49803924560546875, blue: 1, alpha: 1))], [Color(#colorLiteral(red: 0.6039215922355652, green: 1, blue: 1, alpha: 1)), Color(#colorLiteral(red: 0.6039215922355652, green: 0.49803921580314636, blue: 1, alpha: 1))]]
#Published var cards = [Card]()
#Published var selec = 0
func getColorSet() -> [Color] {
return gradCs[selec]
}
}
struct Card {
let prompt: String
let answer: String
static var example = Card(prompt: "What is the name of Chicago's NFL Team?", answer: "Da Bears")
}
extension View {
func stacked(at position: Int, in total: Int) -> some View {
let offset = Double(total - position)
return self.offset(x: 0, y: offset * 10)
}
}
struct ContentView: View {
#Environment(\.accessibilityDifferentiateWithoutColor) var differentiateWithoutColor
#StateObject var manager = CardManager() // <--- here
#State private var timeRemaining = 100
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
#Environment(\.scenePhase) var scenePhase
#State private var isActive = true
var body: some View {
ZStack {
VStack {
HStack {
Text("\(timeRemaining / 60):\(timeRemaining % 60, specifier: "%02d")")
.font(.title)
.fontWeight(.semibold)
.foregroundColor(.white)
.padding(.horizontal, 20)
.padding(.vertical, 5)
.background(.black.opacity(0.75))
.clipShape(Capsule())
Circle()
.fill(LinearGradient(colors: manager.getColorSet(), startPoint: .topLeading, endPoint: .bottomTrailing))
.onTapGesture {
withAnimation {
if manager.selec == 0 {
manager.selec = 1
} else {
manager.selec = 0
}
}
}
.frame(width: 40, height: 40)
}
ZStack {
ForEach(0 ..< manager.cards.count, id: \.self) { index in
CardView(card: manager.cards[index]) {
withAnimation {
removeCard(at: index)
print(self.offset())
}
}
.stacked(at: index, in: manager.cards.count)
}
}
.allowsHitTesting(timeRemaining > 0)
if manager.cards.isEmpty {
Button("Start Again", action: resetCards)
.padding()
.background(.white)
.foregroundColor(.black)
.clipShape(Capsule())
}
}
if differentiateWithoutColor {
VStack {
Spacer()
HStack {
Image(systemName: "xmark.circle")
.padding()
.background(.black.opacity(0.7))
.clipShape(Circle())
Spacer()
Image(systemName: "checkmark.circle")
.padding()
.background(.black.opacity(0.7))
.clipShape(Circle())
}
.foregroundColor(.white)
.font(.largeTitle)
.padding()
}
}
}
.onReceive(timer) { time in
guard isActive else { return }
// Use if let when the non-nil case is valid. Use guard when the nil case represents some sort of error.
// use guard when there should only be one result.. use if when you need ELSE to do something
if timeRemaining > 0 {
timeRemaining -= 1
}
}
.onChange(of: scenePhase) { newPhase in
if newPhase == .active {
if manager.cards.isEmpty == false {
isActive = true
}
} else {
isActive = false
}
}
.onAppear {
manager.cards = Array(repeating: Card.example, count: 10) // <--- here
}
.environmentObject(manager) // <--- here
}
func removeCard(at index: Int) {
manager.cards.remove(at: index)
if manager.cards.isEmpty {
isActive = false
}
}
func resetCards() {
manager.cards = Array(repeating: Card.example, count: 10)
timeRemaining = 100
isActive = true
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct CardView: View {
#EnvironmentObject var manager: CardManager // <--- here
let card: Card
var removal: (() -> Void)? = nil
#State private var isShowingAnswer = false
#State private var changeColors = false
#State private var offset = CGSize.zero
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(LinearGradient(colors: [manager.getColorSet()[0].opacity(1 - Double(abs(offset.width / 50))), manager.getColorSet()[1].opacity(1 - Double(abs(offset.width / 50)))], startPoint: .topLeading, endPoint: .bottomTrailing))
.background(RoundedRectangle(cornerRadius: 25, style: .continuous).fill(offset.width > 0 ? .green : .red))
.background(RoundedRectangle(cornerRadius: 25, style: .continuous).fill(offset.width > 0 ? .green : .red))
.shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.25)), radius:6, x:1, y:8)
VStack(spacing: 20){
Text(card.prompt)
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(.white.opacity(0.8))
if isShowingAnswer {
Text(card.answer)
.font(.title)
.fontWeight(.semibold)
.foregroundColor(Color(#colorLiteral(red: 0.8947916626930237, green: 0.8666666746139526, blue: 1, alpha: 1)))
}
}
.padding()
.multilineTextAlignment(.center)
}
.frame(width: 450, height: 250)
.rotationEffect(.degrees(Double(offset.width / 5)))
.offset(x: offset.width * 5, y: 0)
.opacity(2 - Double(abs(offset.width / 50)))
.gesture(
DragGesture()
.onChanged { gesture in
offset = gesture.translation
}
.onEnded { _ in
if abs(offset.width) > 100 {
removal?()
} else {
offset = .zero
}
}
)
.onTapGesture {
isShowingAnswer.toggle()
}
}
}
I created an ActionSheet, but was unable to print the value I selected on the TextField.
I do not know how to do it. I will be glad if you can help.
struct illedetayyip: View {
#State private var iliskiDurumlari: [String] = ["Evli", "Bekar", "Ayrı", "anan"]
#State private var iliskiArray = NSMutableArray()
#State private var iliskiVisible = false
#State private var iliski = ""
func iliskiFunc() {
for i in 0 ..< self.iliskiDurumlari.count {
let button: ActionSheet.Button = .default(Text(self.iliskiDurumlari[i]), action: {
print(self.iliskiDurumlari[i])
})
self.iliskiArray[i] = button
}
}
init() {
iliskiFunc()
}
var body: some View {
VStack {
TextField("İlişki Durumu Seçiniz..", text: $iliski)
.frame(width: 300, height: 50, alignment: .center)
.padding(5)
.font(Font.system(size: 15, weight: .medium, design: .serif))
.overlay(RoundedRectangle(cornerRadius: 30).stroke(Color(red: 45 / 255,
green: 0 / 255, blue: 112 / 255), lineWidth: 1))
.actionSheet(isPresented: $iliskiVisible) {
ActionSheet(title: Text("Bir İlişki Durumu Seçiniz"), message:
Text("Aşagıda"), buttons: self.iliskiArray as! [ActionSheet.Button])
}
.onTapGesture {
self.iliskiVisible.toggle()
}
}
}
}
You don't need to create a NSMutableArray and then cast it back to [ActionSheet.Button].
Try this instead:
struct illedetayyip: View {
#State private var iliskiDurumlari: [String] = ["Evli", "Bekar", "Ayrı", "anan"]
#State private var iliskiVisible = false
#State private var iliski = ""
var body: some View {
VStack {
TextField("İlişki Durumu Seçiniz..", text: $iliski)
.frame(width: 300, height: 50, alignment: .center)
.padding(5)
.font(Font.system(size: 15, weight: .medium, design: .serif))
.overlay(
RoundedRectangle(cornerRadius: 30)
.stroke(Color(red: 45 / 255, green: 0 / 255, blue: 112 / 255), lineWidth: 1)
)
.actionSheet(isPresented: $iliskiVisible, content: actionSheet)
.onTapGesture {
self.iliskiVisible.toggle()
}
}
}
func actionSheet() -> ActionSheet {
ActionSheet(
title: Text("Bir İlişki Durumu Seçiniz"),
message: Text("Aşagıda"),
buttons: iliskiDurumlari.map { value in
ActionSheet.Button.default(Text(value), action: {
self.iliski = value
})
}
)
}
}
How can I cancel the text field editing. I want no other editing on the TextField when the selection is made. I tried it myself, but I couldn't.
struct profile: View {
#State private var iliskiDurumlari: [String] = ["Evli", "Bekar", "Ayrı", "anan"]
#State private var iliskiVisible = false
#State private var iliski = ""
var body: some View {
VStack {
TextField("İlişki Durumu Seçiniz..", text: $iliski)
.frame(width: 300, height: 50, alignment: .center)
.padding(5)
.font(Font.system(size: 15, weight: .medium, design: .serif))
.overlay(
RoundedRectangle(cornerRadius: 30)
.stroke(Color(red: 45 / 255, green: 0 / 255, blue: 112 / 255), lineWidth: 1)
)
.actionSheet(isPresented: $iliskiVisible, content: actionSheet)
.onTapGesture {
self.iliskiVisible.toggle()
}
}
}
func actionSheet() -> ActionSheet {
ActionSheet(
title: Text("Bir İlişki Durumu Seçiniz"),
message: Text("Aşagıda"),
buttons: iliskiDurumlari.map { value in
ActionSheet.Button.default(Text(value), action: {
self.iliski = value
})
}
)
}
}
If you don't need user input (show keyboard) for this particular field, you can use Text instead:
var body: some View {
VStack {
Text(iliski) // <- change here
.frame(width: 300, height: 50, alignment: .center)
.padding(5)
.font(Font.system(size: 15, weight: .medium, design: .serif))
.overlay(
RoundedRectangle(cornerRadius: 30)
.stroke(Color(red: 45 / 255, green: 0 / 255, blue: 112 / 255), lineWidth: 1)
)
.actionSheet(isPresented: $iliskiVisible, content: actionSheet)
.onTapGesture {
self.iliskiVisible.toggle()
}
}
}
and instead of placeholder add initial value:
#State private var iliski = "İlişki Durumu Seçiniz.."
If you want to dismiss the keyboard and cancel editing of the textfield when the user taps on the Action Sheet then in SwiftUI you need this code:
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) reference here: https://www.hackingwithswift.com/quick-start/swiftui/how-to-dismiss-the-keyboard-for-a-textfield
So your code must be like that:
func actionSheet() -> ActionSheet {
ActionSheet(
title: Text("Bir İlişki Durumu Seçiniz"),
message: Text("Aşagıda"),
buttons: iliskiDurumlari.map { value in
ActionSheet.Button.default(Text(value), action: {
self.iliski = value
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
})
}
)
}
}
Add your text field inside the button and disable the text field interaction.
var body: some View {
VStack {
Button(action: {
self.iliskiVisible.toggle()
}, label: {
TextField("İlişki Durumu Seçiniz..", text: $iliski)
.frame(width: 300, height: 50, alignment: .center)
.padding(5)
.font(Font.system(size: 15, weight: .medium, design: .serif))
.overlay(
RoundedRectangle(cornerRadius: 30)
.stroke(Color(red: 45 / 255, green: 0 / 255, blue: 112 / 255), lineWidth: 1)
)
.allowsHitTesting(false)//<- Disable interection
})
.actionSheet(isPresented: $iliskiVisible, content: actionSheet)
}
}
This is a follow-up question to the following topic.
How can I fix the "Hello World" card inside the row to the top of its cell? It shouldn't move when the second card is coming out by tapping.
Current State:
But inside the row cell it should be alignment to it's top like sketched in this image:
struct ContentView: View {
var body: some View {
VStack {
Text("Hello!")
List {
Detail(isExpanded: false)
Detail(isExpanded: false)
Detail(isExpanded: false)
}
}
}
}
struct Detail: View {
#State var isExpanded :Bool = false
var body: some View {
VStack {
ZStack (alignment: .bottom){
ZStack (alignment: .center) {
RoundedRectangle(cornerRadius: 8)
.fill(Color(red: 0.0, green: 1.0, blue: 1.0, opacity: 0.5)).frame(height: 115)
Text("Helloo!")
}.zIndex(3).frame(height: 115).contentShape(Rectangle()).onTapGesture {
withAnimation (.linear(duration: 0.1)){
self.isExpanded.toggle()
}
}
Button(action: {
}) {
ZStack {
RoundedRectangle(cornerRadius: 50)
.fill(Color(red: 1.0, green: 0.0, blue: 1.0, opacity: 0.5)).frame(height: 70)
.cornerRadius(8)
.shadow(radius: 3)
Text("Test")
}
}.padding([.leading, .trailing], 12)
//.padding(.top, 6)
.frame(height: 70)
.buttonStyle(BorderlessButtonStyle())
.offset(y: self.isExpanded ? 80 : 0)
.disabled(!self.isExpanded)
}
if(self.isExpanded) {
Spacer()
}
}.modifier(AnimatingCellHeight(height: self.isExpanded ? 210 : 115)).background(Color(red: 1.0, green: 0.5, blue: 1, opacity: 0.5))
}
}
struct AnimatingCellHeight: AnimatableModifier {
var height: CGFloat = 0
var animatableData: CGFloat {
get { height }
set { height = newValue }
}
func body(content: Content) -> some View {
content.frame(height: height)
}
}
EDIT
The end result should look like this. The inverted isExpanded Bool is my fault...should be the other way around.
EDIT 2 Code above is updated to the newest try. which you will see in the gif below.
Almost got there....The blue card which I am pressing to expand is moving a little bit up and down (you can clearly see when increasing the click frequency) in the following gif. Otherwise the behaviour is perfect, just the card should do this little wiggle up and down...
do you mean this way? (added VStack + Spacer)
struct Detail: View {
#State var isExpanded :Bool = false
var body: some View {
VStack {
ZStack {
ZStack {
RoundedRectangle(cornerRadius: 8)
.fill(Color(red: 0.0, green: 1.0, blue: 1.0, opacity: 1.0)).frame(height: 115)
Text("Hello!")
}.zIndex(3).frame(height: 115).contentShape(Rectangle()).onTapGesture {
withAnimation {
self.isExpanded.toggle()
}
}
Button(action: {
}) {
ZStack {
RoundedRectangle(cornerRadius: 50)
.fill(Color(red: 1.0, green: 0.0, blue: 1.0, opacity: 1.0)).frame(height: 70)
.cornerRadius(8)
.shadow(radius: 3)
Text("Test")
}
}.padding([.leading, .trailing], 12)
.padding(.top, 6)
.frame(height: 70)
.buttonStyle(BorderlessButtonStyle())
.offset(y: self.isExpanded ? 0 : 40)
.disabled(!self.isExpanded)
}
Spacer()
}.modifier(AnimatingCellHeight(height: self.isExpanded ? 120 : 200))
}
}