I have Row View:
struct TestRow: View {
var rowText: String
var body: some View {
HStack {
Text(rowText)
Spacer()
Image(systemName: "heart.circle.fill")
}
.listRowBackground(Color.red)
}
.listRowbackground work correctly, but if I add contextMenu, background changing to default (white) and don't changing from contextMenu :
struct TestRow: View {
#State var rowColor: Color = Color.mint
var rowText: String
var body: some View {
HStack {
Text(rowText)
Spacer()
Image(systemName: "heart.circle.fill")
}
// .background(rowColor) -> this code working with contextMenu
.listRowBackground(rowColor)
.contextMenu {
VStack {
Button {
//action code
rowColor = Color.red
} label: {
MenuButton(colorOrb: "red_circle", buttonText: "High")
}
Button {
//action code
rowColor = .yellow
} label: {
MenuButton(colorOrb: "yellow_circle", buttonText: "Medium")
}
Button {
//action code
rowColor = .green
} label: {
MenuButton(colorOrb: "green_circle", buttonText: "Normal")
}
}
}
}
}
If I use .background(rowColor) instead .listRowBackground(rowColor) code work correctly, but I need colorise all row, not HStack only.
struct Item: Identifiable {
var id: Int
var color: Color
}
struct ContentView2: View {
#State var items: [Item] = [Item(id: 1, color: .red),
Item(id: 2, color: .yellow),
Item(id: 3, color: .blue)]
var body: some View {
VStack {
List {
ForEach($items) { $item in
TestRow(rowColor: $item.color, rowText: "\(item.id)")
.listRowBackground(item.color)
}
}
}
}
}
struct ContentView2_Previews: PreviewProvider {
static var previews: some View {
ContentView2()
}
}
struct TestRow: View {
#Binding var rowColor: Color
let rowText: String
var body: some View {
HStack {
Text(rowText)
Spacer()
Image(systemName: "heart.circle.fill")
}
// .background(rowColor) -> this code working with contextMenu
.contextMenu {
Button {
//action code
rowColor = Color.red
} label: {
Text("red")
}
Button {
//action code
rowColor = .yellow
} label: {
Text("yellow")
}
Button {
//action code
rowColor = .green
} label: {
Text("green")
}
}
}
}
Related
I'm trying to build a simple animated overlay. Ideally, the dark background fades in (which it's doing now) and the white card slides up from the bottom edge (using .transition(.move(edge: .bottom).
Here's my ContentView.swift file:
struct Overlays: View {
#State var showOverlay = false
var body: some View {
NavigationView {
Button {
withAnimation(.spring()) {
showOverlay.toggle()
}
} label: {
Text("Open overlay")
}
.navigationTitle("Overlay demo")
}
.overlay {
if showOverlay {
CustomOverlay(
overlayPresented: $showOverlay,
overlayContent: "This is a real basic overlay, and it should be sliding in from the bottom."
)
}
}
}
}
And here's my CustomOverlay.swift file:
struct CustomOverlay: View {
#Binding var overlayPresented: Bool
let overlayContent: String
var body: some View {
ZStack(alignment: .bottom) {
overlayBackground
overlayCard
}
}
}
extension CustomOverlay {
var overlayBackground: some View {
Color.black.opacity(0.6)
.ignoresSafeArea(.all)
.onTapGesture {
withAnimation(.spring()) {
overlayPresented = false
}
}
}
var overlayCard: some View {
VStack(spacing: 16) {
overlayText
overlayCloseButton
}
.padding()
.frame(maxWidth: .infinity)
.background(.white)
.clipShape(RoundedRectangle(cornerRadius: 24, style: .continuous))
.padding()
.transition(.move(edge: .bottom))
}
var overlayText: some View {
Text(overlayContent)
}
var overlayCloseButton: some View {
Button {
withAnimation(.spring()) {
overlayPresented = false
}
} label: {
Text("Close")
}
}
}
This doesn't appear to work. The entire overlay is fading in/out.
https://imgur.com/a/iRzJCsw
If I move the .transition(.move(edge: .bottom) to the CustomOverlay ZStack the entire overlay slides in from the bottom which looks super goofy.
What am I doing wrong?
After some more experimentation, I've found something pretty cool.
Our main ContentView.swift file:
struct Overlays: View {
#State var showOverlay = false
var body: some View {
NavigationView {
Button {
withAnimation(.easeInOut(duration: 0.25)) {
showOverlay.toggle()
}
} label: {
Text("Open overlay")
}
.navigationTitle("Overlay demo")
}
.overlay {
if showOverlay {
// Here's the overlay background, which we can animate independently
OverlayBackground(
overlayPresented: $showOverlay
)
.transition(.opacity)
// Explicit z-index as per https://stackoverflow.com/a/58512696/1912818
.zIndex(0)
// Here's the overlay content card, which we can animate independently too!
OverlayContent(
overlayPresented: $showOverlay,
overlayContent: "This is a real basic overlay, and it should be sliding in from the bottom."
)
.transition(.move(edge: .bottom).combined(with: .opacity))
// Explicit z-index as per https://stackoverflow.com/a/58512696/1912818
.zIndex(1)
}
}
}
}
And here's OverlayBackground.swift (the background):
struct OverlayBackground: View {
#Binding var overlayPresented: Bool
var body: some View {
Color.black.opacity(0.6)
.ignoresSafeArea(.all)
.onTapGesture {
withAnimation(.easeInOut(duration: 0.25)) {
overlayPresented = false
}
}
}
}
And lastly OverlayContent.swift:
struct OverlayContent: View {
#Binding var overlayPresented: Bool
let overlayContent: String
var body: some View {
VStack {
Spacer()
overlayCard
}
}
}
extension OverlayContent {
var overlayCard: some View {
VStack(spacing: 16) {
overlayText
overlayCloseButton
}
.padding()
.frame(maxWidth: .infinity)
.background(.white)
.clipShape(RoundedRectangle(cornerRadius: 24, style: .continuous))
.padding()
}
var overlayText: some View {
Text(overlayContent)
}
var overlayCloseButton: some View {
Button {
withAnimation(.easeInOut(duration: 0.25)) {
overlayPresented = false
}
} label: {
Text("Close")
}
}
}
The result: https://imgur.com/a/1JoMWcs
I have the following SwiftUI code:
struct ContentView: View {
var body: some View {
NavigationView {
Form {
Section {
NavigationLink {
DetailsView()
} label: {
Text("Show details")
}
}
}
Text("Select details")
}
}
}
struct DetailsView: View {
#State var showModal = false
var body: some View {
ZStack {
Color.gray.ignoresSafeArea()
VStack {
Spacer()
Button {
showModal.toggle()
} label: {
Text("Show modal")
.foregroundColor(.black)
.font(.system(size: 20, weight: .bold))
}
}
}
.fullScreenCover(isPresented: $showModal) {
MyModalView {
showModal = false
}
}
}
}
struct MyModalView: View {
var someAction:()->()
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
Button {
someAction()
} label: {
Text("some action")
.foregroundColor(.white)
}
}
}
}
I am experiencing the following bug, where tapping on "Show details" won't show the DetailsView anymore after rotation...
How can I fix this?
Navigation view is really problematical but will be improved with IOS 16. Here is my solution
struct ContentView: View {
#State var navigate = false
var body: some View {
NavigationView {
Form {
Section {
NavigationLink(destination: DetailsView(navigate: $navigate), isActive: $navigate){
Text("Show details")
}
}
}
Text("Select details")
}
.navigationViewStyle(.automatic)
}
}
struct DetailsView: View {
#Binding var navigate: Bool
#State var showModal = false
var body: some View {
ZStack {
Color.gray.ignoresSafeArea()
VStack {
Spacer()
Button {
showModal.toggle()
} label: {
Text("Show modal")
.foregroundColor(.black)
.font(.system(size: 20, weight: .bold))
}
}
}
.fullScreenCover(isPresented: $showModal) {
MyModalView {
showModal = false
}
}
.onAppear(){
navigate = false
}
}}
struct MyModalView: View {
var someAction:()->()
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
Button {
someAction()
} label: {
Text("some action")
.foregroundColor(.white)
}
}
}}
I try to get( text & color ) from user and add them to the list in SwiftUI I already can pass text data but unfortunately for color I can't while they should be the same, below there is an image of app.To work we should provide a Binding for PreAddTextField .Thanks for your help
here is my Code :
import SwiftUI
struct AddListView: View {
#Binding var showAddListView : Bool
#ObservedObject var appState : AppState
#StateObject private var viewModel = AddListViewViewModel()
var body: some View {
ZStack {
Title(addItem: { viewModel.textItemsToAdd.append(.init(text: "", color: .purple)) })
VStack {
ScrollView {
ForEach(viewModel.textItemsToAdd, id: \.id) { item in //note this is id:
\.id and not \.self
PreAddTextField(textInTextField: viewModel.bindingForId(id: item.id), colorPickerColor: <#Binding<Color>#>)
}
}
}
.padding()
.offset(y: 40)
Buttons(showAddListView: $showAddListView, save: {
viewModel.saveToAppState(appState: appState)
})
}
.frame(width: 300, height: 200)
.background(Color.white)
.shadow(color: Color.black.opacity(0.3), radius: 10, x: 0, y: 10)
}
}
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
AddListView(showAddListView: .constant(false),appState: AppState())
}
}
struct PreAddTextField: View {
#Binding var textInTextField : String
#Binding var colorPickerColor : Color
var body: some View {
HStack {
TextField("Enter text", text: $textInTextField)
ColorPicker("", selection: $colorPickerColor)
}
}
}
struct Buttons: View {
#Binding var showAddListView : Bool
var save : () -> Void
var body: some View {
VStack {
HStack(spacing:100) {
Button(action: {
showAddListView = false}) {
Text("Cancel")
}
Button(action: {
showAddListView = false
// What should happen here to add Text to List???
save()
}) {
Text("Add")
}
}
}
.offset(y: 70)
}
}
struct Title: View {
var addItem : () -> Void
var body: some View {
VStack {
HStack {
Text("Add Text to list")
.font(.title2)
Spacer()
Button(action: {
addItem()
}) {
Image(systemName: "plus")
.font(.title2)
}
}
.padding()
Spacer()
}
}
}
DataModel :
import SwiftUI
struct Text1 : Identifiable , Hashable{
var id = UUID()
var text : String
var color : Color
}
class AppState : ObservableObject {
#Published var textData : [Text1] = [.init(text: "Item 1", color: .purple),.init(text: "Item 2", color: .purple)]
}
class AddListViewViewModel : ObservableObject {
#Published var textItemsToAdd : [Text1] = [.init(text: "", color: .purple)] //start with one empty item
//save all of the new items -- don't save anything that is empty
func saveToAppState(appState: AppState) {
appState.textData.append(contentsOf: textItemsToAdd.filter { !$0.text.isEmpty })
}
//these Bindings get used for the TextFields -- they're attached to the item IDs
func bindingForId(id: UUID) -> Binding<String> {
.init { () -> String in
self.textItemsToAdd.first(where: { $0.id == id })?.text ?? ""
} set: { (newValue) in
self.textItemsToAdd = self.textItemsToAdd.map {
guard $0.id == id else {
return $0
}
return .init(id: id, text: newValue, color: .purple)
}
}
}
}
and finaly :
import SwiftUI
struct ListView: View {
#StateObject var appState = AppState() //store the AppState here
#State private var showAddListView = false
var body: some View {
NavigationView {
VStack {
ZStack {
List(appState.textData, id : \.self){ text in
HStack {
Image(systemName: "square")
.foregroundColor(text.color)
Text(text.text)
}
}
if showAddListView {
AddListView(showAddListView: $showAddListView, appState: appState)
.offset(y:-100)
}
}
}
.navigationTitle("List")
.navigationBarItems(trailing:
Button(action: {showAddListView = true}) {
Image(systemName: "plus")
.font(.title2)
}
)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
Instead of using a Binding for just the String, you could create a Binding for the entire Text1 item:
class AddListViewViewModel : ObservableObject {
#Published var textItemsToAdd : [Text1] = [.init(text: "", color: .purple)]
func saveToAppState(appState: AppState) {
appState.textData.append(contentsOf: textItemsToAdd.filter { !$0.text.isEmpty })
}
func bindingForId(id: UUID) -> Binding<Text1> { //now returns a Text1
.init { () -> Text1 in
self.textItemsToAdd.first(where: { $0.id == id }) ?? Text1(text: "", color: .clear)
} set: { (newValue) in
self.textItemsToAdd = self.textItemsToAdd.map {
guard $0.id == id else {
return $0
}
return newValue
}
}
}
}
struct PreAddTextField: View {
#Binding var item : Text1
var body: some View {
HStack {
TextField("Enter text", text: $item.text) //gets the text property of the binding
ColorPicker("", selection: $item.color) //gets the color property
}
}
}
struct AddListView: View {
#Binding var showAddListView : Bool
#ObservedObject var appState : AppState
#StateObject private var viewModel = AddListViewViewModel()
var body: some View {
ZStack {
Title(addItem: { viewModel.textItemsToAdd.append(.init(text: "", color: .purple)) })
VStack {
ScrollView {
ForEach(viewModel.textItemsToAdd, id: \.id) { item in
PreAddTextField(item: viewModel.bindingForId(id: item.id)) //parameter is changed here
}
}
}
.padding()
.offset(y: 40)
Buttons(showAddListView: $showAddListView, save: {
viewModel.saveToAppState(appState: appState)
})
}
.frame(width: 300, height: 200)
.background(Color.white)
.shadow(color: Color.black.opacity(0.3), radius: 10, x: 0, y: 10)
}
}
I'm trying to build an demo app by swiftUI that get multi text from user and add them to the list, below , there is an image of app every time user press plus button the AddListView show to the user and there user can add multi text to the List.I have a problem to add them to the list by new switUI data Flow I don't know how to pass data.(I comment more information)
Thanks 🙏
here is my code for AddListView:
import SwiftUI
struct AddListView: View {
#State var numberOfTextFiled = 1
#Binding var showAddListView : Bool
var body: some View {
ZStack {
Title(numberOfTextFiled: $numberOfTextFiled)
VStack {
ScrollView {
ForEach(0 ..< numberOfTextFiled, id: \.self) { item in
PreAddTextField()
}
}
}
.padding()
.offset(y: 40)
Buttons(showAddListView: $showAddListView)
}
.frame(width: 300, height: 200)
.background(Color.white)
.shadow(color: Color.black.opacity(0.3), radius: 10, x: 0, y: 10)
}
}
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
AddListView(showAddListView: .constant(false))
}
}
struct PreAddTextField: View {
// I made this standalone struct and use #State to every TextField text be independent
// if i use #Binding to pass data all Texfield have the same text value
#State var textInTextField = ""
var body: some View {
VStack {
TextField("Enter text", text: $textInTextField)
}
}
}
struct Buttons: View {
#Binding var showAddListView : Bool
var body: some View {
VStack {
HStack(spacing:100) {
Button(action: {
showAddListView = false}) {
Text("Cancel")
}
Button(action: {
showAddListView = false
// What should happen here to add Text to List???
}) {
Text("Add")
}
}
}
.offset(y: 70)
}
}
struct Title: View {
#Binding var numberOfTextFiled : Int
var body: some View {
VStack {
HStack {
Text("Add Text to list")
.font(.title2)
Spacer()
Button(action: {
numberOfTextFiled += 1
}) {
Image(systemName: "plus")
.font(.title2)
}
}
.padding()
Spacer()
}
}
}
and for DataModel:
import SwiftUI
struct Text1 : Identifiable , Hashable{
var id = UUID()
var text : String
}
var textData = [
Text1(text: "SwiftUI"),
Text1(text: "Data flow?"),
]
and finally:
import SwiftUI
struct ListView: View {
#State var showAddListView = false
var body: some View {
NavigationView {
VStack {
ZStack {
List(textData, id : \.self){ text in
Text(text.text)
}
if showAddListView {
AddListView(showAddListView: $showAddListView)
.offset(y:-100)
}
}
}
.navigationTitle("List")
.navigationBarItems(trailing:
Button(action: {showAddListView = true}) {
Image(systemName: "plus")
.font(.title2)
}
)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
Because of the multiple-items part of the question, this becomes a lot less trivial. However, using a combination of ObservableObjects and callback functions, definitely doable. Look at the inline comments in the code for explanations about what is going on:
struct Text1 : Identifiable , Hashable{
var id = UUID()
var text : String
}
//Store the items in an ObservableObject instead of just in #State
class AppState : ObservableObject {
#Published var textData : [Text1] = [.init(text: "Item 1"),.init(text: "Item 2")]
}
//This view model stores data about all of the new items that are going to be added
class AddListViewViewModel : ObservableObject {
#Published var textItemsToAdd : [Text1] = [.init(text: "")] //start with one empty item
//save all of the new items -- don't save anything that is empty
func saveToAppState(appState: AppState) {
appState.textData.append(contentsOf: textItemsToAdd.filter { !$0.text.isEmpty })
}
//these Bindings get used for the TextFields -- they're attached to the item IDs
func bindingForId(id: UUID) -> Binding<String> {
.init { () -> String in
self.textItemsToAdd.first(where: { $0.id == id })?.text ?? ""
} set: { (newValue) in
self.textItemsToAdd = self.textItemsToAdd.map {
guard $0.id == id else {
return $0
}
return .init(id: id, text: newValue)
}
}
}
}
struct AddListView: View {
#Binding var showAddListView : Bool
#ObservedObject var appState : AppState
#StateObject private var viewModel = AddListViewViewModel()
var body: some View {
ZStack {
Title(addItem: { viewModel.textItemsToAdd.append(.init(text: "")) })
VStack {
ScrollView {
ForEach(viewModel.textItemsToAdd, id: \.id) { item in //note this is id: \.id and not \.self
PreAddTextField(textInTextField: viewModel.bindingForId(id: item.id))
}
}
}
.padding()
.offset(y: 40)
Buttons(showAddListView: $showAddListView, save: {
viewModel.saveToAppState(appState: appState)
})
}
.frame(width: 300, height: 200)
.background(Color.white)
.shadow(color: Color.black.opacity(0.3), radius: 10, x: 0, y: 10)
}
}
struct PreAddTextField: View {
#Binding var textInTextField : String //this takes a binding to the view model now
var body: some View {
VStack {
TextField("Enter text", text: $textInTextField)
}
}
}
struct Buttons: View {
#Binding var showAddListView : Bool
var save : () -> Void //callback function for what happens when "Add" gets pressed
var body: some View {
VStack {
HStack(spacing:100) {
Button(action: {
showAddListView = false}) {
Text("Cancel")
}
Button(action: {
showAddListView = false
save()
}) {
Text("Add")
}
}
}
.offset(y: 70)
}
}
struct Title: View {
var addItem : () -> Void //callback function for what happens when the plus button is hit
var body: some View {
VStack {
HStack {
Text("Add Text to list")
.font(.title2)
Spacer()
Button(action: {
addItem()
}) {
Image(systemName: "plus")
.font(.title2)
}
}
.padding()
Spacer()
}
}
}
struct ListView: View {
#StateObject var appState = AppState() //store the AppState here
#State private var showAddListView = false
var body: some View {
NavigationView {
VStack {
ZStack {
List(appState.textData, id : \.self){ text in
Text(text.text)
}
if showAddListView {
AddListView(showAddListView: $showAddListView, appState: appState)
.offset(y:-100)
}
}
}
.navigationTitle("List")
.navigationBarItems(trailing:
Button(action: {showAddListView = true}) {
Image(systemName: "plus")
.font(.title2)
}
)
}
}
}
When I tap edit, it will show a delete button (minus icon). When the delete button tapped it will show the orange delete option (as the gif shows). Now I'm trying to reset the state back to the origin (from orange button back to video name and length ) when the Done button is tapped.
I'm trying few options like closures but nothing much.
Any help would be much appreciated!
My child view Video
struct Video: View {
var videoImage : String
var title : String
var duaration : Int
#Binding var deleteActivated : Bool
var body: some View {
HStack {
Image(videoImage)
...
if deleteActivated {
Button(action: {
}) {
ZStack {
Rectangle()
.foregroundColor(.orange)
.cornerRadius(radius: 10, corners: [.topRight, .bottomRight])
Text("Delete")
.foregroundColor(.white)
}
}
} else {
VStack(alignment: .leading){
....
My parent view VideosDirectory
struct VideosDirectory: View {
#State var videos:[DraftVideos] = [
DraftVideos(isSelected: true,title: "Superman workout", duration: 5, imageURL: "test"),
DraftVideos(isSelected: true,title: "Ironman workout", duration: 15, imageURL: "test1"),
DraftVideos(isSelected: true,title: "Ohman workout and long name", duration: 522, imageURL: "test2")
]
init() {
self._deleteActivated = State(initialValue: Array(repeating: false, count: videos.count))
}
#State private var deleteActivated: [Bool] = []
#State private var show = false
// #State private var editing = false
var body: some View {
// VStack {
NavigationView {
ScrollView(.vertical) {
VStack {
ForEach(videos.indices, id: \.self) { i in
HStack {
if self.show {
Button(action: {
withAnimation {
self.deleteActivated[i].toggle()
}
}) {
Image(systemName: "minus.circle.fill")
...
}
}
Video(videoImage: videos[i].imageURL, title: videos[i].title, duaration: videos[i].duration, deleteActivated: $deleteActivated[i])
}
}
}
.animation(.spring())
}
.navigationBarItems(trailing:
HStack {
Button(action: {
self.show.toggle()
}) {
if self.show {
Text("Done")
} else {
Text("Edit")
}
}
})
}
}
}
Provided code is not testable so just an idea:
Button(action: {
self.deleteActivated = Array(repeating: false, count: videos.count)
self.show.toggle()
}) {
or almost the same but as "post-action" in
}
.animation(.spring())
.onChange(of: self.show) { _ in
// most probably condition is not needed here, but is up to you
self.deleteActivated = Array(repeating: false, count: videos.count)
}