var body: some View {
ZStack(alignment: .bottom){
NavigationLink(destination: CategoryMenuView(selectedCategory: $selectedCategory,clickNavText: $clickNavText,clickNavIcon: $clickNavIcon,titleColor: $titleColor), isActive: $showCategory) {
EmptyView()
}
VStack(spacing:0){
switch selectedCategory {
case 1:
// CustomNavBarView(data: CustomNavBarView.Data(showBackButton: true, clickTitleControl: true, titleColor: titleColor,clickNavText: clickNavText, clickNavIcon: clickNavIcon),showSideMenu: $hiddenNavRightBar, showCategory: $showCategory, showDetailMenu: $showDetailMenu, isActive: $showingView)
HotelListViewContainer(clickNavText: clickNavText, clickNavIcon: clickNavIcon, titleColor: titleColor, showingView: $showingView)
case 2:
CustomNavBarView(data: CustomNavBarView.Data(showBackButton: true, clickTitleControl: true, titleColor: titleColor,clickNavText: clickNavText, clickNavIcon: clickNavIcon), showSideMenu: $hiddenNavRightBar, showCategory: $showCategory, showDetailMenu: $showDetailMenu,isActive: $showingView)
PreRestaurantListViewContainer()
case 3:
CustomNavBarView(data: CustomNavBarView.Data(showBackButton: true, clickTitleControl: true, titleColor: titleColor,clickNavText: clickNavText, clickNavIcon: clickNavIcon), showSideMenu: $hiddenNavRightBar, showCategory: $showCategory, showDetailMenu: $showDetailMenu,isActive: $showingView)
PresupplierListViewContainer()
case 4:
CustomNavBarView(data: CustomNavBarView.Data(showBackButton: true, clickTitleControl: true, titleColor: titleColor,clickNavText: clickNavText, clickNavIcon: clickNavIcon), showSideMenu: $hiddenNavRightBar, showCategory: $showCategory, showDetailMenu: $showDetailMenu,isActive: $showingView)
PreBanquetViewContainer()
case 5:
CustomNavBarView(data: CustomNavBarView.Data(showBackButton: true, clickTitleControl: true, titleColor: titleColor,clickNavText: clickNavText, clickNavIcon: clickNavIcon), showSideMenu: $hiddenNavRightBar, showCategory: $showCategory, showDetailMenu: $showDetailMenu,isActive: $showingView)
ManagementListViewContainer()
default:
HomeView()
}
}
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
When the calendar appears, the back white side should not appear.
When I remove the vstack in the code, the problem is fixed, but this time the CustomNavBarView above does not appear.
Related
My NavigationBar has gone large. How do you control the height in SwiftUI?
The relevant code is:
.navigationBarBackButtonHidden(false)
.navigationBarTitle("Sunrise Sunset", displayMode: .inline)
.navigationBarColor(backgroundColor: .gray, titleColor: .white)
There seems to be a similar question on this topic but I couldn't get it to work:
(SwiftUI) NavigationBar height issue
The .navigationBarColor struct comes from 56505528.
// https://stackoverflow.com/questions/56505528/swiftui-update-navigation-bar-title-color
struct NavigationBarModifier: ViewModifier {
var backgroundColor: UIColor?
var titleColor: UIColor?
init(backgroundColor: UIColor?, titleColor: UIColor?) {
self.backgroundColor = backgroundColor
let coloredAppearance = UINavigationBarAppearance()
coloredAppearance.configureWithTransparentBackground()
coloredAppearance.backgroundColor = backgroundColor
coloredAppearance.titleTextAttributes = [.foregroundColor: titleColor ?? .white]
coloredAppearance.largeTitleTextAttributes = [.foregroundColor: titleColor ?? .white]
UINavigationBar.appearance().standardAppearance = coloredAppearance
UINavigationBar.appearance().compactAppearance = coloredAppearance
UINavigationBar.appearance().scrollEdgeAppearance = coloredAppearance
}
func body(content: Content) -> some View {
ZStack{
content
VStack {
GeometryReader { geometry in
Color(self.backgroundColor ?? .clear)
.frame(height: geometry.safeAreaInsets.top)
.edgesIgnoringSafeArea(.top)
Spacer()
}
}
}
}
}
extension View {
func navigationBarColor(backgroundColor: UIColor?, titleColor: UIColor?) -> some View {
self.modifier(NavigationBarModifier(backgroundColor: backgroundColor, titleColor: titleColor))
}
}
The NavigationStack! If you have a complex View and your NavigationBar has oversized it can mean that your structures aren't contained/paired correctly.
I changed "NavigationStack" to a "VStack" just to see if the page rendered OK and the NavigationBar went back to its normal, thin size.
Try this if you're having the same issue.
When you press the empty square, you change it to different images. It's for a simple game I created.
Now what I want is to bind these values so that when I go to a different view and come back these images are still there and do not get reset to their original state. So when you have a xmark, you still have the xmark when you come back and so on.
How would I use a binding with these enums?
I know how to use a binding with a boolean or a string but not an enum.
Can someone point me into the right direction?
This is my code:
enum dader1 : String, Identifiable {
case vierkantje
case vinkje
case vraagteken
case kruisje
var id: String {
return self.rawValue }
}
enum dader2 : String, Identifiable {
case vierkantje
case vinkje
case vraagteken
case kruisje
var id: String {
return self.rawValue }
}
enum dader3 : String, Identifiable {
case vierkantje
case vinkje
case vraagteken
case kruisje
var id: String {
return self.rawValue }
}
#State var activeDader1 : dader1? = nil
#State var activeDader2 : dader2? = nil
#State var activeDader3 : dader3? = nil
var body: some View {
ZStack {
Image("Achtergrond")
.resizable()
.ignoresSafeArea()
VStack(alignment: .leading) {
ScrollView {
Text("Some Text")
.frame(width: 350)
.foregroundColor(.black)
.font(.headline)
ZStack {
Image("Resultaat")
.resizable()
.frame(width: 350, height: 250)
VStack {
HStack(spacing: 55){
switch self.activeDader1 {
case .vierkantje:
Image(systemName: "square" )
.foregroundColor(.gray)
.font(.title)
.onTapGesture {
self.activeDader1 = .vinkje
Dader1 = false
}
case .vinkje:
Image(systemName: "checkmark.square" )
.foregroundColor(.blue)
.font(.title)
.onTapGesture {
self.activeDader1 = .vraagteken
Dader1 = true
}
case .vraagteken:
Image(systemName: "questionmark.square" )
.foregroundColor(.gray)
.font(.title)
.onTapGesture {
self.activeDader1 = .kruisje
Dader1 = false
}
case .kruisje:
Image(systemName: "xmark.square" )
.foregroundColor(.red)
.font(.title)
.onTapGesture {
self.activeDader1 = .vierkantje
Dader1 = false
}
case .none:
Image(systemName: "square" )
.foregroundColor(.gray)
.font(.title)
.onTapGesture {
self.activeDader1 = .vinkje
Dader1 = false
}
}
switch self.activeDader2 {
case .vierkantje:
Image(systemName: "square" )
.foregroundColor(.gray)
.font(.title)
.onTapGesture {
self.activeDader2 = .vinkje
Dader2 = true
}
case .vinkje:
Image(systemName: "checkmark.square" )
.foregroundColor(.blue)
.font(.title)
.onTapGesture {
self.activeDader2 = .vraagteken
Dader2 = false
}
case .vraagteken:
Image(systemName: "questionmark.square" )
.foregroundColor(.gray)
.font(.title)
.onTapGesture {
self.activeDader2 = .kruisje
Dader2 = false
}
case .kruisje:
Image(systemName: "xmark.square" )
.foregroundColor(.red)
.font(.title)
.onTapGesture {
self.activeDader2 = .vierkantje
Dader2 = false
}
case .none:
Image(systemName: "square" )
.foregroundColor(.gray)
.font(.title)
.onTapGesture {
self.activeDader2 = .vinkje
Dader2 = true
}
}
switch self.activeDader3 {
case .vierkantje:
Image(systemName: "square" )
.foregroundColor(.gray)
.font(.title)
.onTapGesture {
self.activeDader3 = .vinkje
Dader3 = false
}
case .vinkje:
Image(systemName: "checkmark.square" )
.foregroundColor(.blue)
.font(.title)
.onTapGesture {
self.activeDader3 = .vraagteken
Dader3 = true
}
case .vraagteken:
Image(systemName: "questionmark.square" )
.foregroundColor(.gray)
.font(.title)
.onTapGesture {
self.activeDader3 = .kruisje
Dader3 = false
}
case .kruisje:
Image(systemName: "xmark.square" )
.foregroundColor(.red)
.font(.title)
.onTapGesture {
self.activeDader3 = .vierkantje
Dader3 = false
}
case .none:
Image(systemName: "square" )
.foregroundColor(.gray)
.font(.title)
.onTapGesture {
self.activeDader3 = .vinkje
Dader11 = false
}
}
}
The problem is you are using #State instead of #Binding.
The State of the view resets on appear, but #bindings are stored across the views. You should use like this:
#Binding var activeDader1: dader1
#Binding var activeDader2: dader2
#Binding var activeDader3: dader3
trying to bring data from LeadDetailUI to formUI to be able to edit the data in formUI and for the life of me can't figure this out, not sure on way to go (Bindings or environmentObject). eighthor way can't get it to work. I tried with bindings couldn't get it to work. please help with example on how to do this.
struct LeadDetailUI: View {
#ObservedObject var viewModel: getCustomerData
#State var tbl11 = ""
#State var tbl12 = ""
#State var tbl13 = ""
#State var tbl14 = ""
#State var tbl15 = ""
#State var tbl16 = ""
#State var tbl17 = ""
#State var tbl21 = ""
#State var tbl22 = 0
#State var tbl23 = 0
#State var tbl24 = 0
#State var tbl25 = 0
#State var tbl26 = ""
#State var tbl27 = ""
#State var l11 = ""
#State var l12 = ""
#State var l13 = ""
#State var l14 = ""
#State var l15 = ""
#State var l16 = ""
#State var l17 = ""
#State var l21 = ""
#State var l22 = ""
#State var l23 = ""
#State var l24 = ""
#State var l25 = ""
#State var l26 = ""
#State var l27 = ""
var body: some View {
NavigationView {
VStack() {
ScrollView(self.height > 700 ? .init() : .vertical, showsIndicators: true) {
VStack(alignment: .trailing, spacing: 13) {
HStack {
TextField("Peter Balsamo", text: $name).font(.title)
.padding(.top, 3)
.padding(.leading, 20)
.padding(.bottom, -10)
//.redacted(reason: .placeholder)
Text("Following").font(.headline)
.padding(.top, 10)
Button(action: {
}) {
Image(systemName: "star.fill")
.resizable()
.frame(width: 20, height: 20)
.foregroundColor(Color.orange)
.padding(.top, 7)
.padding(.trailing, 15)
}
}
Divider()
VStack {
HStack {
VStack(alignment: .leading, spacing: 5, content: {
TextField("Amount", text: $amount).font(.largeTitle)
.offset(y: -3)
TextField("Address", text: $address).font(.title3)
TextField("City", text: $city).font(.title3)
TextField("Sale Date:", text: $l1datetext).font(.caption2)
.padding(.top, 15)
TextField("Date:", text: $date).font(.headline)
.padding(.top, -5)
})
.padding(.bottom, 0)
.padding(.leading, 15)
Spacer()
VStack(alignment: .trailing, spacing: 0, content: {
Image("taylor_swift_profile")
.resizable()
.frame(width: 115, height: 115)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 2))
.padding(.top, -25)
TextField("Lead#", text: $id).font(.caption2)
.multilineTextAlignment(.trailing)
.padding(.top, 15)
})
.frame(width: 120)
.padding(.trailing, 10)
Spacer()
}
HStack {
VStack(alignment: .leading, spacing: 0, content: {
HStack {
Toggle("", isOn: $showingSold.animation(.spring()))
.frame(width:80, height: 30)
.toggleStyle(SwitchToggleStyle(tint: .blue))
.clipShape(RoundedRectangle(cornerRadius: 10))
if showingSold {
Text("Priority").font(.headline)
.background(Color.red.cornerRadius(10))
.foregroundColor(.white)
.padding(.leading, 10)
}
}
})
Spacer()
Button(action: {
showFullscreen.toggle()
}) {
Text("Map")
.fontWeight(.bold)
.frame(width:115, height: 30)
.foregroundColor(.white)
.background(Color(.systemBlue))
.clipShape(RoundedRectangle(cornerRadius: 10))
}
.padding(.trailing, 20)
}
.padding(.bottom, 30)
}
.fullScreenCover(isPresented: $showFullscreen, content: {
HomeMap()
})
}
.foregroundColor(self.color == 0 ? Color.black : Color.white)
.background(self.color == 0 ? Color.yellow : Color.purple)
.clipShape(CustomShape(corner: .bottomLeft, radii: 55))
ScrollView(self.height > 800 ? .init() : .vertical, showsIndicators: false) {
let first = DataUI(name: tbl11, label: l11)
let second = DataUI(name: tbl12, label: l12)
let third = DataUI(name: tbl13, label: l13)
let fourth = DataUI(name: tbl14, label: l14)
let fifth = DataUI(name: tbl15, label: l15)
let sixth = DataUI(name: tbl16, label: l16)
let seventh = DataUI(name: tbl17, label: l17)
let eighth = DataUI(name: tbl21, label: l21)
let ninth = DataUI(name: "\(tbl22)", label: l22)
let tenth = DataUI(name: "\(tbl23)", label: l23)
let eleven = DataUI(name: "\(tbl24)", label: l24)
let twelve = DataUI(name: "\(tbl25)", label: l25)
let thirteen = DataUI(name: tbl26, label: l26)
let fourteen = DataUI(name: tbl27, label: l27)
let customers = [first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleven, twelve,thirteen, fourteen]
List(customers) { customer in
CenterViewUI(formData: customer)
}
}
.edgesIgnoringSafeArea(.all)
.statusBar(hidden: true)
.animation(.default)
BottomViewUI(comments: self.$comments, lnewsTitle: self.$lnewsTitle, index: $index)
}
.shadow(color: Color.white.opacity(0.2), radius: 5, x: 0, y: 2)
}
.navigationTitle("Profile")
.navigationBarHidden(false)
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems (
leading:
Button(action: {
showActionSheet.toggle()
}) {
Image(systemName: "square.and.arrow.up")
.resizable()
.frame(width: 20, height: 20)
},
trailing:
//NavigationLink(destination: FormUI(frm11: $tbl11)) {
Button(action: {
showSheet.toggle()
}, label: {
Text("Edit")
.actionSheet(isPresented: $showActionSheet, content: getActionSheet)
.sheet(isPresented: $showSheet, content: {
FormUI()
})
.foregroundColor(self.color == 0 ? Color.yellow : Color.purple)
}
}
struct DataUI: Identifiable {
var id = UUID().uuidString
var name: String
var label : String
}
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
Group {
LeadDetailUI(viewModel: getCustomerData())
.preferredColorScheme(.dark)
}
}
}
public struct FormUI: View {
#Environment(\.presentationMode) var presentationMode
#EnvironmentObject var viewModel: getCustomerData
private var db = Firestore.firestore()
#State var frm11 = ""
#State var frm12 = ""
#State var frm13 = ""
#State var frm14 = ""
#State var frm15 = ""
#State var frm16 = ""
#State var frm17 = ""
#State var frm18 = ""
#State var frm19 = ""
public var body: some View {
NavigationView {
VStack {
ScrollView(self.height > 800 ? .init() : .vertical, showsIndicators: false) {
VStack {
//ForEach(self.viewModel.data) { i in
Form {
Section {
HStack{
VStack{
Image("taylor_swift_profile")
.resizable()
.frame(width: 75, height: 75)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 2))
.padding()
Button(action: {}, label: {
Text("Edit")
.font(.caption)
.padding(.top, -15)
.foregroundColor(self.color == 0 ? Color.purple : Color.red)
})
}
.padding(.leading, -30)
Divider()
Spacer()
VStack(spacing: 12) {
TextField("First", text: $frm11)
TextField("Last", text: $frm12)
Picker(selection: $selection, label:
TextField("Company", text: $callback)) {
ForEach(0 ..< pickContractor.count) {
Text(self.pickContractor[$0])
}
}
}
.font(.system(size: 20.0))
.multilineTextAlignment(.leading)
}
}
.font(.headline)
.padding(.leading, 18)
Section(header: Text("Customer Info")) {
HStack {
Text("Address:")
.formTextStyle()
Spacer()
TextField("address", text: $address)
.formStyle()
}
HStack {
Text("City:")
.formTextStyle()
Spacer()
TextField("city", text: $city)
.formStyle()
}
HStack {
Text("State:")
.formTextStyle()
.multilineTextAlignment(.leading)
Spacer()
TextField("state", text: $state)
.formStyle()
.frame(minWidth: 50, maxWidth: 60)
.autocapitalization(.allCharacters)
.multilineTextAlignment(.leading)
//.padding(.leading, )
Spacer()
Text("Zip:")
.formTextStyle()
TextField("zip", text: $zip)
.formStyle()
.frame(minWidth: 100, maxWidth: 145)
.keyboardType(.numberPad)
}
HStack {
Text("Phone:")
.formTextStyle()
Spacer()
TextField("phone", text: $phone)
.formStyle()
.keyboardType(.numberPad)
}
HStack {
Text("Amount:")
.formTextStyle()
Spacer()
Stepper(
onIncrement: {
stepperValue += 1000
},
onDecrement: {
stepperValue -= 1000
},
label: {
TextField("amount", text: $amount)
.formStyle()
})
}
HStack {
Text("Email:")
.formTextStyle()
Spacer()
TextField("email", text: $email)
.formStyle()
.keyboardType(.emailAddress)
}
}
Section {
HStack {
Text("Salesman:")
.formTextStyle()
Spacer()
TextField("salesman", text: $salesman)
}
HStack {
Text("Job:")
.formTextStyle()
Spacer()
TextField("job", text: $jobName)
.formStyle()
}
HStack {
Text("Product:")
.formTextStyle()
Spacer()
TextField("product", text: $adName)
.formStyle()
}
HStack {
Text("Quantity:")
.formTextStyle()
Spacer()
TextField("quantity", text: $frm25)
.formStyle()
.keyboardType(.numberPad)
}
HStack {
Text("Apt Date:")
.formTextStyle()
Spacer()
DatePicker(selection: $selDate, displayedComponents: .date) {
TextField("", text: $aptdate)
.formStyle()
}
}
HStack {
Text("Comments:")
.formTextStyle()
Spacer()
TextEditor(text: $comment)
}
}
Section(header: Text("Misc")) {
HStack {
Toggle(isOn: $isOn) {
Text("\(self.isOn == true ? "Active":"Not Active")")
.formTextStyle()
}
.toggleStyle(SwitchToggleStyle(tint: .purple))
}
HStack {
Text("Date")
.formTextStyle()
Spacer()
DatePicker(selection: $selDate, displayedComponents: .date, label: {
TextField("", text: $date)
})
}
HStack {
Text("Spouse")
.formTextStyle()
Spacer()
TextField("spouse", text: $spouse)
}
HStack {
Text("Called Back")
.formTextStyle()
Spacer()
Picker(selection: $selection, label:
TextField("", text: $callback)) {
ForEach(0 ..< callbackPicker.count) {
Text(self.callbackPicker[$0])
}
}
}
HStack {
Text("Rate")
.formTextStyle()
Spacer()
Picker(selection: $selection, label:
TextField("", text: $rate)) {
ForEach(0 ..< pickRate.count) {
Text(verbatim: self.pickRate[$0])
}
}
}
HStack {
Text("Photo")
.formTextStyle()
Spacer()
TextField("photo", text: $photo)
}
}
}
.font(.system(size: 20.0))
.padding(.top, -40)
}
}
}
.navigationTitle("Data Entry")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "xmark.circle").font(.largeTitle)
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
//if !frm11.isEmpty && !frm12.isEmpty {
saveData()
resetTextFields()
self.alert.toggle()
}, label: {
Text("Save")
}
}
.alert(isPresented: $alert) {
Alert(title: Text("Upload Complete"), message: Text("Successfully updated the data"), dismissButton: .default(Text("Ok")))
}
}
.accentColor(self.color == 0 ? Color.purple : Color.red)
}
private func saveData() {
let uid = Auth.auth().currentUser!.uid
var ref: DocumentReference? = nil
ref = db.collection("Customers").addDocument(data: [
"active": frm30,
"custId": frm12,
"custNo": custNo,
"leadNo": leadNo,
"first": frm11,
"lastname": frm12,
"contractor": frm13,
"city": city,
"state": state,
"zip": zip,
"phone": phone,
"amount": Int(amount) as Any,
"email": email,
"rate": rate,
"salesNo": saleNo,
"jobNo": jobNo,
"adNo": adNo,
"quan": Int(frm25) as Any,
"start": NSNull(),
"completion": NSNull(),
"lastUpdate": Timestamp(date: Date()),
"creationDate": Timestamp(date: Date()),
"aptdate": aptdate,
"comments": comment,
"spouse": spouse,
"photo": photo,
"uid": uid,
]) { error in
if let error = error {
print("Error adding document: \(error)")
} else {
print("Document added with ID: \(ref!.documentID)")
}
}
}
func updateData() {
db.collection("Customer")
.document()
.setData(["active":self.frm30,"custId":self.frm12,"custNo":self.custNo,"leadNo":self.leadNo,"first":self.frm11,"lastname":self.frm12,"contractor":self.frm13,"city":self.city,"state":self.state,"zip":self.zip,"phone":self.phone,"amount":self.amount,"email":self.email,"rate":self.rate,"salesNo":self.saleNo,"jobNo":self.jobNo,"adNo":self.adNo,"start":self.start,"lastUpdate":Timestamp(date:Date()),"aptdate":self.aptdate,"comments":self.comment,"spouse":self.spouse,"photo":self.photo]) { (error) in
if error != nil{
print((error?.localizedDescription)!)
return
}
//self.presentation.wrappedValue.dismiss()
}
}
}
struct FormUI_Previews: PreviewProvider {
static var previews: some View {
FormUI()
.preferredColorScheme(.dark)
}
}
I have a LazyVGrid, every item with is favorite button. and use combine to debounce user input($isFavoriteI), when isFavoriteO changed, then modify the items.
it works fine, but when i scroll the list, log will print: "X, isFavorite changed as false/true)", what cause isFavoriteO changed and why? because of item reusing in list? how to avoid it?
index 7, isFavorite changed as true
index 7, isFavorite changed as true
index 7, isFavorite changed as true
index 7, isFavorite changed as true
index 7, isFavorite changed as true
index 7, isFavorite changed as true
index 7, isFavorite changed as true
index 7, isFavorite changed as true
import SwiftUI
import Combine
struct Item {
var index: Int
var favorite: Bool
}
var items = [
Item(index: 0, favorite: true),
Item(index: 1, favorite: false),
Item(index: 2, favorite: true),
Item(index: 3, favorite: false),
Item(index: 4, favorite: true),
Item(index: 5, favorite: false),
Item(index: 6, favorite: true),
Item(index: 7, favorite: false),
// Item(index: 8, favorite: true),
// Item(index: 9, favorite: false),
// Item(index: 10, favorite: true),
// Item(index: 11, favorite: false),
// Item(index: 12, favorite: true),
// Item(index: 13, favorite: false),
// Item(index: 14, favorite: true),
// Item(index: 15, favorite: false),
// Item(index: 16, favorite: true),
// Item(index: 17, favorite: false),
// Item(index: 18, favorite: true),
// Item(index: 19, favorite: false),
]
struct ViewModelInListTestView: View {
var body: some View {
ScrollView(showsIndicators: false) {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 200), spacing: 4, alignment: .center)], spacing: 4) {
ForEach(items, id: \.index) { item in
ItemView(item: item)
}
}
}.navigationTitle("ViewModel In List")
}
}
struct ItemView: View {
let item: Item
#ObservedObject var viewModel: ViewModel
init(item: Item) {
print("ItemView.init, \(item.index)")
self.item = item
self.viewModel = ViewModel(item: item)
}
var body: some View {
HStack {
Text("index \(item.index)")
Spacer()
Image(systemName: viewModel.isFavoriteI ? "heart.fill" : "heart")
.foregroundColor(viewModel.isFavoriteI ? .red : .white)
.padding()
.onTapGesture { onFavoriteTapped() }
.onChange(of: viewModel.isFavoriteO) { isFavorite in
setFavorite(isFavorite)
}
}
.frame(width: 200, height: 150)
.background(Color.gray)
}
func onFavoriteTapped() {
viewModel.isFavoriteI.toggle()
}
func setFavorite(_ isFavorite: Bool) {
print("index \(item.index), isFavorite changed as \(isFavorite)")
items[item.index].favorite = isFavorite
}
class ViewModel: ObservableObject {
#Published var isFavoriteI: Bool = false
#Published var isFavoriteO: Bool = false
private var subscriptions: Set<AnyCancellable> = []
init(item: Item) {
print("ViewModel.init, \(item.index)")
let isFavorite = item.favorite
isFavoriteI = isFavorite; isFavoriteO = isFavorite
$isFavoriteI
.print("index \(item.index) isFavoriteI:")
.dropFirst()
.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)
.removeDuplicates()
.eraseToAnyPublisher()
.print("index \(item.index) isFavoriteO:")
.receive(on: DispatchQueue.main)
.assign(to: \.isFavoriteO, on: self)
.store(in: &subscriptions)
}
}
}
update # 4.15
according to #Cenk Bilgen, i re-write the code, but strange thing happened. print("set favorite as (favorite)") will not present if adding removeDuplicates. why?
import SwiftUI
import Combine
struct Item: Identifiable {
var index: Int
var favorite: Bool
var id: Int { index }
}
class Model: ObservableObject {
#Published var items = [
Item(index: 0, favorite: true),
Item(index: 1, favorite: false),
Item(index: 2, favorite: true),
Item(index: 3, favorite: false),
Item(index: 4, favorite: true),
Item(index: 5, favorite: false),
Item(index: 6, favorite: true),
Item(index: 7, favorite: false),
]
}
struct ViewModelInListTestView: View {
#StateObject var model = Model()
var body: some View {
print("ViewModelInListTestView refreshing"); return
ScrollView(showsIndicators: false) {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 200), spacing: 4, alignment: .center)], spacing: 4) {
ForEach(model.items.indices) { index in
ItemView(item: model.items[index])
.environmentObject(model)
}
}
}.navigationTitle("ViewModel In List")
}
struct ItemView: View {
#EnvironmentObject var model: Model
let item: Item
#State private var updateFavourite = PassthroughSubject<Bool, Never>()
#State private var favorite: Bool = false
init(item: Item) {
self.item = item
self._favorite = State(initialValue: item.favorite)
}
var body: some View {
print("ItemView \(item.index) refreshing"); return
HStack {
Text("index \(item.index)")
Spacer()
Image(systemName: favorite ? "heart.fill" : "heart")
.foregroundColor(favorite ? .red : .white)
.padding()
.onTapGesture {
favorite.toggle()
updateFavourite.send(favorite)
}
.onReceive(
updateFavourite
.debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
// .removeDuplicates() <------ HERE
// .eraseToAnyPublisher()
) { favorite in
print("set favorite as \(favorite)")
model.items[item.index].favorite = favorite
}
}
.frame(width: 200, height: 150)
.background(Color.gray)
}
}
}
struct Item: Identifiable {
var index: Int
var favorite: Bool
var id: Int { index }
}
class Model: ObservableObject {
#Published var items = [
Item(index: 0, favorite: true),
Item(index: 1, favorite: false),
Item(index: 2, favorite: true),
Item(index: 3, favorite: false),
Item(index: 4, favorite: true),
Item(index: 5, favorite: false),
Item(index: 6, favorite: true),
Item(index: 7, favorite: false),
]
}
struct SimplerView: View {
#StateObject var model = Model()
var body: some View {
ScrollView(showsIndicators: false) {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 200), spacing: 4, alignment: .center)], spacing: 4) {
ForEach(items.indices) { index in
ItemView(item: $model.items[index])
.environmentObject(model)
}
}
}.navigationTitle("ViewModel In List")
}
struct ItemView: View {
#EnvironmentObject var model: Model
#Binding var item: Item
#State private var updateFavourite = PassthroughSubject<Bool, Never>()
var body: some View {
HStack {
Text("index \(item.index)")
Spacer()
Image(systemName: item.favorite ? "heart.fill" : "heart")
.foregroundColor(item.favorite ? .red : .white)
.padding()
.onTapGesture {
updateFavourite.send(item.favorite)
}
.onReceive(updateFavourite
.debounce(for: .seconds(0.5), scheduler: RunLoop.main)) { value in
item.favorite = !value
}
}
.frame(width: 200, height: 150)
.background(Color.gray)
}
}
}
I don't really understand how isFavoriteI and isFavoriteO work, but you
could try this:
in ItemView remove the
.onChange(of: viewModel.isFavoriteO) { isFavorite in
setFavorite(isFavorite)
}
and change:
func onFavoriteTapped() {
viewModel.isFavoriteI.toggle()
print("\(item.index), isFavorite changed as \(viewModel.isFavoriteI)")
items[item.index].favorite = viewModel.isFavoriteI
}
The best of doing this is in this way in down code, SwiftUI would stop unnecessary render, and it will render if it needs!
You had some issue that you should use id for your items, plus combine does not work well in this case, so use better and easier way in down:
import SwiftUI
struct ContentView: View {
#StateObject var itemModel: ItemModel = sharedItemModel
var body: some View {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 200), spacing: 4, alignment: .center)], spacing: 4) {
ForEach(Array(itemModel.items.enumerated()), id:\.element.id) { (offset, element) in
ItemView(index: offset, favorite: element.favorite)
}
}
}
Button("append new random element") { itemModel.items.append(Item(favorite: Bool.random())) }
.padding()
}
}
struct ItemView: View {
let index: Int
let favorite: Bool
init(index: Int, favorite: Bool) {
self.index = index
self.favorite = favorite
}
var body: some View {
print("rendering item: " + index.description)
return HStack {
Text("index " + index.description)
.bold()
.padding()
Spacer()
Image(systemName: favorite ? "heart.fill" : "heart")
.foregroundColor(Color.red)
.padding()
.onTapGesture { sharedItemModel.items[index].favorite.toggle() }
}
.frame(width: 200, height: 150)
.background(Color.gray)
.cornerRadius(10.0)
}
}
struct Item: Identifiable {
let id: UUID = UUID()
var favorite: Bool
}
class ItemModel: ObservableObject {
#Published var items: [Item] = [Item]()
}
let sharedItemModel: ItemModel = ItemModel()
I would like to add Image in UIAlertbox so I add the following codes.
let alertController = UIAlertController(title: "Gender", message: "" , preferredStyle: .alert)
// Create the actions
let okAction = UIAlertAction(title: "Female", style: UIAlertActionStyle.default) {
UIAlertAction in
// exit(0)
debugPrint("Press OK")
}
let cancelAction = UIAlertAction(title: "Male", style: UIAlertActionStyle.cancel) {
UIAlertAction in
}
// Add the actions
okAction.setValue(#imageLiteral(resourceName: "female_image"), forKey: "image")
cancelAction.setValue(#imageLiteral(resourceName: "male_image"), forKey: "image")
alertController.addAction(okAction)
alertController.addAction(cancelAction)
When I run the app, only one Image appear. What's wrong with this?
Please anyone help me?
Try this code its working in swift3
let alertMessage = UIAlertController(title: "Gender", message: "", preferredStyle: .alert)
let image = UIImage(named: "blanckstar")
let action = UIAlertAction(title: "Male", style: .default)
{
UIAlertAction in
// exit(0)
debugPrint("Press Male")
}
action.setValue(image, forKey: "image")
let image2 = UIImage(named: "blanckstar")
let action2 = UIAlertAction(title: "Female", style: .default)
{
UIAlertAction in
// exit(0)
debugPrint("Press Female")
}
action2.setValue(image2, forKey: "image")
alertMessage.addAction(action)
alertMessage.addAction(action2)
self.present(alertMessage, animated: true, completion: nil)
Happy Coading :-)
for changing size of image you can try with fontAwesome just install pod
pod 'FontAwesome.swift' and import FontAwesome_swift
here is the code.....
let alertMessage = UIAlertController(title: "Gender", message: "", preferredStyle: .alert)
let image = UIImage.fontAwesomeIcon(name: .male, textColor: UIColor.black, size: CGSize(width: 50, height: 50))
let action = UIAlertAction(title: "Male", style: .default)
{
UIAlertAction in
// exit(0)
debugPrint("Press Male")
}
action.setValue(image, forKey: "image")
let image2 = UIImage.fontAwesomeIcon(name: .female, textColor: UIColor.black, size: CGSize(width: 50, height: 50))
let action2 = UIAlertAction(title: "Female", style: .default)
{
UIAlertAction in
// exit(0)
debugPrint("Press Female")
}
action2.setValue(image2, forKey: "image")
alertMessage.addAction(action)
alertMessage.addAction(action2)
self.present(alertMessage, animated: true, completion: nil)