pass multidimensional array in collection view inside the table view - ios

Please find the photo i want this type of view tableview with collection view
here members array is every time different means members are not fix in every cell, please help me out, ... thanks

Although it is based on how your application getting the data, for such a case, -in general- I would suggest to create your own custom Model instead of just declaring arrays/dictionaries of arrays. Example:
struct Family {
var title: String
var thumbnail: UIImage
var members: [Member]
}
struct Member {
var firstName: String
var lastName: String
var thumbnail: UIImage
}
so forth You could declare such an array of families:
let families = [Family(title: "YOGA", thumbnail: UIImage(named: "YOGA.jpg"), members: [Member(firstName: "John", lastName: "Smith", thumbnail: UIImage(named: "John.jpg"))]),
Family(title: "DEMO1", thumbnail: UIImage(named: "DEMO_1.jpg"), members: [Member(firstName: "Foo", lastName: "Bar", thumbnail: UIImage(named: "Foo.jpg"))])]
// and so on...
thus the data source for the table view would be the families, each collection view in the table view cell data source would be the members of the current family.
By following this approach you could easily templating your data to be more expressive.

Related

How can I initialize attributes in SwiftUI?

So this block of code allows me to initialize a data model
struct MockMessage: Identifiable {
let id: Int
let imageName: String
let messageText: String
let isCurrentUser: Bool
}
let MOCK_MESSAGES: [MockMessage] = [
.init(id: 0, imageName: "user", messageText: "Hey, what's up?", isCurrentUser: false),
.init(id: iCouldSetANewOne, imageName: iCouldSetANewOne, messageText: iCouldSetANewOne, isCurrentUser: iCouldSetANewOne)]
My question is
1- How can I replicate/initialize attributes instead of manually writing the same attributes over and over again
2- How can I modify it, let's say, the default rotation effect is set to a degree: 25 and I'd like to change it.
Example of Image attributes
Image(systemName: tabs[index].image)
.frame(height: 20)
.rotationEffect(.degrees(-25))
.foregroundColor(.yellow).scaleEffect(1.25)
If I understand correctly, you want to group a commonly used set of modifiers into 1 simple modifier. You can easily do it like this:
extension View {
func myStyle() -> some View {
self
.frame(...)
.rotationEffect(...)
.foregroundColor(...)
}
}
You can even parametrize this function if the values can be different.
About the "changing the rotation effect that is set to 25 degrees" I believe you can have multiple of the same effects, so if you want to rotate some view that is already rotated, just put another .rotationEffect() view modifier on top.

SwiftUI Struct for Dictionary entry

I have decided after several years of development to restart my project using SwiftUI to future proof as much as I can.
In my current project I have my data in several .CSV's which I then process into dictionaries and then create a list of entries on screen using an Array of keys which are generated programmatically from user input.
All examples I've seen for SwiftUI use JSON. However the structure of these files are identical to an Array of Dictionaries. My question is; is it possible to create a Struct of a dictionary entry to pass in a forEach watching an Array of Keys (data inside the dictionary will never change and I am not looking to iterate or watch the dictionary).
My main goal is to reuse as much as possible but am willing to change what I have to get full benefit of SwiftUI. Obviously if I change the way I store my data almost everything will be useless. If there's a real benefit to converting my data to JSON or even start using something like CoreData I will.
If I'm understanding correctly, you are looking to
Take some user input
Transform that into keys that correspond to your data dictionary
Extract the data for the matching keys into some struct
Display a list of those structs using SwiftUI
Here is a simple implementation of those steps.
import SwiftUI
// A dictionary containing your data
let data: [String: Int] = [
"apples": 5,
"bananas": 3,
"cherries": 12
]
// A struct representing a match from your data
struct Item {
var name: String
var quantity: Int
}
// A view that displays the contents of your struct
struct RowView: View {
var item: Item
var body: some View {
Text("\(item.quantity) \(item.name)")
}
}
struct ContentView: View {
#State private var searchText: String = ""
func items(matching search: String) -> [Item] {
// 2 - split the user input into individual keys
let split = search.split(separator: " ", omittingEmptySubsequences: true).map { $0.lowercased() }
// 3 - turn any matching keys/values in your dictionary to a view model
return split.compactMap { name in
guard let quantity = data[name] else { return nil }
return Item(name: name, quantity: quantity)
}
}
var body: some View {
VStack {
// 1 - get user input
TextField("Search", text: $searchText)
.padding()
// 4 - display the matching values using ForEach (note that the id: \.name is important)
List {
ForEach(items(matching: searchText), id: \.name) { item in
RowView(item: item)
}
}
}
}
}
You'll see that as you type in the text field, if you enter any of the strings "apples", "bananas", or "cherries", a corresponding row will pop into your list.
Depending on the size of your list, and what kind of validation you are performing on your users search queries, you might need to be a little more careful about doing the filtering/searching in an efficient way (e.g. using Combine to only split and search after the user stops typing).

SwiftUI - data source as struct - performance and redundancy through copies - why still use it?

I have a minimal working example of something I'm still not sure about:
import SwiftUI
struct Car {
var name: String
}
class DataModel: ObservableObject {
#Published var cars: [Car]
init(_ cars: [Car]) {
self.cars = cars
}
}
struct TestList: View {
#EnvironmentObject var dataModel: DataModel
var body: some View {
NavigationView {
List(dataModel.cars, id: \.name) { car in
NavigationLink(destination: TestDetail(car: car).environmentObject(self.dataModel)) {
Text("\(car.name)")
}
}
}
}
}
struct TestDetail: View {
#EnvironmentObject var dataModel: DataModel
var car: Car
var carIndex: Int {
dataModel.cars.firstIndex(where: {$0.name == self.car.name})!
}
var body: some View {
Text(car.name)
.onTapGesture {
self.dataModel.cars[self.carIndex].name = "Changed Name"
}
}
}
struct TestList_Previews: PreviewProvider {
static var previews: some View {
TestList().environmentObject(DataModel([.init(name: "A"), .init(name: "B")]))
}
}
It's about the usage of structs as data models. The example is similar to the official SwiftUI tutorial by Apple (https://developer.apple.com/tutorials/swiftui/handling-user-input).
Basically, we have a DataModel class that is passed down the tree as EnvironmentObject. The class wraps the basic data types of our model. In this case, it's an array of the struct Car:
class DataModel: ObservableObject {
#Published var cars: [Car]
...
}
The example consists of a simple list that shows the names of all cars. When you tap on one, you get to a detail view. The detail view is passed the car as property (while the dataModel is passed as EnvironmentObject):
NavigationLink(destination: TestDetail(car: car).environmentObject(self.dataModel)) {
Text("\(car.name)")
}
The property car of the detail view is used to populate it. However, if you want to e.g. change the name of the car from within the detail view you have to go through the dataModel because car is just a copy of the original instance found in dataModel. Thus, you first have to find the index of the car in the dataModel's cars array and then update it:
struct TestDetail: View {
...
var carIndex: Int {
dataModel.cars.firstIndex(where: {$0.name == self.car.name})!
}
...
self.dataModel.cars[self.carIndex].name = "Changed Name"
This doesn't feel like a great solution. Searching for the index is a linear operation you have to do whenever you want to change something (the array could change at any time, so you have to constantly repeat the index search).
Also, this means that you have duplicate data. The car property of the detail view exactly mirrors the car of the viewModel. This separates the data. It doesn't feel right.
If car was a class instead of a struct, this would no be a problem because you pass the instance as reference. It would be much simpler and cleaner.
However, it seems that everyone wants to use structs for these things. Sure, they are safer, there can't be reference cycles with them but it creates redundant data and causes more expensive operations. At least, that's what it looks like to me.
I would love to understand why this might not be a problem at all and why it's actually superior to classes. I'm sure I'm just having trouble understanding this as a new concept.

Creating a dynamic List with section headers in SwiftUI?

I'm experimenting with SwiftUI and I'm trying to build a workout tracker app that I've already sketched in UIKit. I am trying to build an exercise list for the user to consult, so when the app starts I load some exercises in CoreData, and the Exercise has the following properties.
#NSManaged public var name: String
#NSManaged public var muscleGroup: String
#NSManaged public var exerciseDescription: String
#NSManaged public var type: String
#NSManaged public var id: UUID
When I'm building the list view, I retrieve an array of Exercise from CoreData and load them in a list. This work fine with a basic list, the thing is I would like to create a List with section headers in alphabetical order. In UIKit I did this by building a dictionary of the form [ "Prefix" : [Exercise]], and using the keys as section headers. This is practical because I could give the user sorting option just by changing the dictionary and reloading the data. In SwiftUI, I can't seem to make it work because I can't work on the fetch request object before view creation, and I can't iterate over dictionaries. Here is my code:
import SwiftUI
import CoreData
struct ExerciseListUIView: View {
#Environment(\.managedObjectContext) var managedObjectContext
#FetchRequest(
entity: Exercise.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \Exercise.name, ascending: true)
]
) var exerciseList: FetchedResults<Exercise>
#State private var prefixList = [String]()
var body: some View {
return NavigationView {
VStack{
List(exerciseList, id: \.self) { exercise in
Text(exercise.name)
}
}
.navigationBarTitle("Exercises")
}
}
}
I tried a bunch of things but nothing seem to work. The most promising solution seems to store exercises directly in a different data structure, ExercisesByLetter(prefix: "String", exercises: [Exercise]), retrieve an array of [ExercisesByLetter] and iterate on that for building the list, but that would mean changing the way I store data, adding more work in the data storage functions and being forced to add different storages for each sorting option, like ExercisesByMuscleGroup, ExercisesByEquipment, and so on.
Let me know what you think,
Thanks.
For anyone interested in the solution, I came up with the following. I created a ViewModel for my main View to manipulate the data. In that view model I retrieve all the Exercise as an array of [Exercise] from CoreData, and store them in a property of the ViewModel. I created then an helper function to iterate through the list of [Exercise] and create an array of [ExercisesBy]. This type contains a property which stores the sorting criterion (first letter, muscle group, equipment, etc) and another property which stores the array of Exercise which adhere to that criterion.
This array of ExercisesBy is then iterated from my view to construct the sectioned list.

Core Data One-To-Many Swift

The question i have is in regards to a Core Data one-to-many relationship as of right now i have my app being able to let the user input employee information and saving to core data, then updating the employee table view. The problem i face is the relationship between the employee and delivery. Im currently trying to display a specific employees deliveries when clicking on an employee in the employee table view. After selecting an employee from the employee tableView i want it to segue to another tableview and display the employees deliveries in another UITableview.
What I'm trying to Accomplish:
1) Display Specific Employee's Deliveries
2) Add deliveries to the NSSet
Here are my two managedObjects
extension Delievery {
#NSManaged var address: String?
#NSManaged var phoneNumber: String?
#NSManaged var subTotal: NSNumber?
#NSManaged var total: NSNumber?
#NSManaged var employee: Employee?
}
extension Employee {
#NSManaged var first: String?
#NSManaged var last: String?
#NSManaged var phoneNumber: String?
#NSManaged var wage: NSNumber?
#NSManaged var id: NSNumber?
#NSManaged var delievery: NSSet?
}
how i prepared for segue from employeeTableViewController to deliveryTableViewContorller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue == "DelieverySegue"{
let employeeDeliveries = segue.destinationViewController as! DelieveryTableViewController
let indexPath = self.tableView.indexPathForSelectedRow
let selectedEmployee = employees[indexPath!.row]
employeeDeliveries.employee = selectedEmployee
}
}
The variables of the deliveryTableViewController are
var employee: NSManagedObject!
var deliveries : [NSManagedObject]!
var managedObjectContext : NSManagedObjectContext!
In this photo it shows the rest of my deliveryTableViewController the problem i have is how do i return the amount of deliveries for a specific employee in the numberOfRowsInSection function and how do i fetch the deliveries of the employee.
deliveryTableViewController set up
In this last photo my question is how to i add this delivery entry to the employee selected? this is how I'm currently saving my delivery entries
how i save my delivery entries
If you made it this far i appreciate you looking through my problem. If anyone can help me the slightest with this issue i'd appreciate it if you feel i've left some information out that is needed please let me know.
UPDATE:
Here is the picture of the DelieveryTableViewController (yes i know i spelt delivery wrong)
also need to set the NSPredicate
this is home I'm preparing for segue in EmployeeTableViewController
these are my variables for EmployeeTableViewController
Setting the relationship
With one to many relationships, it is easiest to set the to-one relationship:
delivery.employee = employee
Put this line in your completeOrder method (you may need to pass the employee reference from a previous VC). CoreData will automatically set the inverse relationship for you, adding delivery to the deliveries set for the employee.
Showing the Deliveries in a Table View
Having set the relationship, the deliveries property of employee contains a set of Delivery objects to which it is related. To display these in a table view, create your deliveries array from this set (eg. in viewDidLoad):
deliveries = employee.delivery.allObjects()
Your numberOfRowsInSection can then just use deliveries.count and the cellForRowAtIndexPath can use deliveries[indexPath.row] to determine which Delivery to display in each cell.
(An alternative is to fetch the deliveries array in the normal way, but to use a predicate to restrict the results to those that are related to your employee:
fetch.predicate = NSPredicate(format:"employee == %#", employee)
Longer term, you should consider using NSFetchedResultsController which is designed for displaying CoreData objects in table view.)
Update
You don't need the thisEmployee variable. Just change the Employee variable to be Employee class:
var employee : Employee!
Then you should be able to set
deliveries = employee.deliveries?.allObjects as! [NSManagedObject]
And in your fetchDelivery() method, set the predicate with
fetchRequest.predicate = NSPredicate(format:"employee == %#", employee)
(after let fetchRequest = ....).
Update 2
It's difficult to see where the nil value is. To track it down, try printing the value of employee in the viewDidLoad method of the DelieveryTableViewController. If it's nil, there is a problem with passing the value in prepareForSegue. If not, print employee.deliveries, etc. Post your results.

Resources