When I expand the PickerInlineRow, I want to be able to select the first option. For cases where there is only one option, I cannot call the onChange method so it can never be picked.
This is a hack.. But I select the first row by setting the row value = the first element in the array on the network callback.
if let row = self.form.rowBy(tag: "Row") as? PickerInlineRow<RowType> {
row.value = rowTypes.first
row.reload()
}
let assume you've created array or objects
let items = ["1","2","3","4","5","6"]
<< PickerInlineRow<String>(){
row in
row.tag = "inlineRow"
row.options = items
row.title = "Tap to select"
row.displayValueFor = {
guard let teacherName = $0 else {return nil}
return teacherName.name
}
}.onChange({ (row) in
guard let value = row.value else {return}
print(value)
self.selectedTeacher = value
print("Selected teacher name :\(self.selectedTeacher.name) , ID : \(self.selectedTeacher.id)")
}).onCellSelection({ (cell, row) in
row.value = items.first
row.reload()
})
Here .onCellSelection is called when user taps on the row so we will get the first value from our array and put the value in row and reload the row .
Related
I have a tableview with values. The database is made with core data. You can set the values to true or false. I only want to sum the values with true. To sum the values i have this code.
func printData() {
//Shorthand Argument Names
//let request: NSFetchRequest<Gegenstand> = Gegenstand.fetchRequest()
//let records = try! context.fetch(request) as [NSManagedObject]
let sum = ViewController.liste.reduce(0) { $0 + ($1.value(forKey: "gewicht") as? Double ?? 0) }
print("Gesamtgewicht: \(sum) kg")
gewicht = sum
if gewicht > 3500 {
gewichtLabel.textColor = .red
gewichtLabel.text = "\(gewicht) kg"
}
}
I tried it with an if-function but i don't know to use it with core data.
Create a coreData fetchRequest with isValue=true,
Calculate the sum of return fetchRequest
func fetchAllWithTrue() -> [Gegenstand] {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: Gegenstand)
fetchRequest.predicate = NSPredicate(format: “isValue== YES")
do {
let fetchedObjects = try coreDataManager.context.fetch(fetchRequest) as? [Gegenstand]
return fetchedObjects
} catch {
print(error.localizedDescription)
return [Gegenstand]()
}
}
You can do all of it in Core Data if you want. Filter for true filtering with an NSPredicate, and have Core Data calculate the sum using NSExpression.
First set up the fetch request to get only entries where the property is true and make sure it returns dictionary-type results (I don't know what your boolean is called, so here I'm calling it flag. Put your property name there):
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Gegenstand")
fetchRequest.resultType = .dictionaryResultType
fetchRequest.predicate = NSPredicate(format: "flag = true")
Then set up an NSExpressionDescription that can get the sum of gewicht values:
let sumExpression = NSExpression(format: "sum:(gewicht)")
let sumExpressionDescription = NSExpressionDescription()
sumExpressionDescription.expression = sumExpression
sumExpressionDescription.resultType = .double
sumExpressionDescription.name = "gewichtSum"
What this does is create a new kind of property that Core Data understands where the value is the sum the values of gewicht and is named gewichtSum. Fetch requests know how to use that kind of property. You do it like this:
fetchRequest.propertiesToFetch = [ sumExpressionDescription ]
Since the fetch uses dictionaryResultType, running the fetch returns an array of dictionaries. It's an array because fetch always returns an array, but here there's only one entry. The dictionary in that array entry has a key called gewichtSum for a Double property that was calculated from the expression above. You get the value like this:
do {
let result = try context.fetch(fetchRequest)
if result.count > 0,
let sumInfo = result[0] as? [String:Double],
let gewichtSum: Double = sumInfo["gewichtSum"] {
print("Sum: \(gewichtSum)")
}
} catch {
...
}
The print statement above prints the sum of all gewicht values where flag is true. It only includes true values for flag because of the NSPredicate, and it contains the sum because of the expression description.
Search should sort results based on a number of matched keywords. It should show the most matched keyword results on top of the list, then the rest records going in descending order. I have a UITableView wherein I have implemented UISearchBar which is going to search my tableview cells as per query inserted in the search-box. Let me now recreate a scenario to explain it. Lets say my Api fetched different key value datas and among them is a key named as "tags" which will contain a string of comma separated values as "dog, cat, pug" now whenever user enters dog into the search-box as this value is shown in one of my tableview cell it will be shown as my tags contain value dog, again if we insert second keyword as cat the above record needs to be fetched and shown to the top and after that if there exists a record which contains only dog in one of the tag values or cat as one of the tag values then it needs to be shown after it. Similarly for third word.
Query -> Cat Dog Pug
It should show on the top only those records which have all of the above keywords
After that, it should show records where any two of the above values are present
After that, it should show single single records if there exists any from above keywords.
Please help.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print("\(searchText)")
let arrSearchTexts = searchText.components(separatedBy: " ")
filteredStorage = originalStorage1.filter { (obj) -> Bool in
if let tags = obj["tags"] as? String{
var hasTag = false
for searchedTag in arrSearchTexts{
if tags.contains(searchedTag.lowercased()){
hasTag = true
break
}
}
return hasTag //tags.contains(searchText.lowercased())
}
return false
}
//Sorting data
filteredStorage.sort { (obj1, obj2) -> Bool in
if let tags1 = obj1["tags"] as? String{
var hasAllTags = true
for tagVal in arrSearchTexts{
if !tags1.contains(tagVal.lowercased()){
hasAllTags = false
break
}
}
return hasAllTags
}
return obj1.contains { (obj) -> Bool in
if let val = obj.value as? String,
let firstTag = arrSearchTexts.last{
return val.hasPrefix(firstTag)
}
return false
}
}
if searchText == "" {
filteredStorage = originalStorage1
}
tblView.reloadData()
}
for i in 0..<filteredStorage.count {
var dict = filteredStorage[i]
dict["priority"] = 0
filteredStorage[i] = dict
}
for i in 0..<filteredStorage.count{
let tagInArr : String = filteredStorage[i]["tags"] as! String
var priorityInArr : Int = 0
for tagVal in arrSearchTexts{
if tagInArr.contains(tagVal){
priorityInArr += 1
}
}
filteredStorage[i]["priority"] = priorityInArr
}
filteredStorage.sort{
((($0 as Dictionary<String, AnyObject>)["priority"] as? Int)!) > ((($1 as Dictionary<String, AnyObject>)["priority"] as? Int)!)
}
I'm trying to filter my json data by IDs (trying mark some favourites and filter using it)
struct workoutList : Codable {
let id : Int
let title : String
let tag : String
}
func selectedWorkoutGroup(libraryFilter: Int, jsonErgWorkouts:[workoutList], workoutGroupBox: UITextField) -> [workoutList] {
var selectedGroup = [workoutList]()
let workoutFav = [1,10,100]
if libraryFilter == 0 {
// This works because I'm filtering based on 1 specific item
selectedGroup = jsonErgWorkouts.filter { $0.tag == workoutGroupBox.text }
} else if libraryFilter == 1 {
// Here I want to filter and show only the favorites
selectedGroup = jsonErgWorkouts.filter { $0.id } //
print("selectedGroup:\(selectedGroup)")
}
return selectedGroup
}
in the above code, the filter works when I have 1(one) something specific item to filter and then I get the entire json array with that tag.
Now I want to implement a favorite list, where the user selects for example ID == [1, 10 ,100] as their favourite.
How can I use the filter command to do it? I tried a few things and searched through SO (but doesn't work). Most of the answers are based on filtering based on specific items eg:
selectedGroup = jsonErgWorkouts.filter { workoutFav?.contains($0.id) }
edit: (omitted that I am using/storing the favourites in userDefaults. This code gives the error of "type of expression is ambiguous without more context"
func selectedWorkoutGroup(libraryFilter: Int, jsonErgWorkouts:[workoutList], workoutGroupBox: UITextField) -> [workoutList] {
var selectedGroup = [workoutList]()
UserDefaults.standard.set([1,10,100], forKey: "workoutFavorite")
/// This one gets stored as [Any] so I cast it to [Int]
let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as? [Int]
if libraryFilter == 0 {
// This works because I'm filtering based on 1 specific item
selectedGroup = jsonErgWorkouts.filter { $0.tag == workoutGroupBox.text }
} else if libraryFilter == 1 {
selectedGroup = workoutFav.flatMap { favouriteId in // for each favourite ID
jsonErgWorkouts.filter { $0.id == favouriteId } // This returns Error "type of expression is ambiguous without more context"
} // flatMap joins all those arrays returns by "filter" together, no need to do anything else
print("selectedGroup:\(selectedGroup)")
}
return selectedGroup
}
Final Solution:
Changing from This
let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as? [Int]
to This (notice the as! instead of as?)
let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as! [Int]
works using #sweeper's answer. Thanks
Update:
Figured out why this error occurred "type of expression is ambiguous without more context" when casting the output of UserDefaults as? [Int] and had to use as! [Int]
But using as! [Int] force unwrapping it causes app to crash if the user did not have any favorites saved into the UserDefault. (Which I then had to code around) like below
var workoutFav = [Int]()
if !(UserDefaults.standard.array(forKey: "workoutFavorite") == nil) {
workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as! [Int]
}
Which was then simplified and removed the force unwrapping based on this SO https://stackoverflow.com/a/37357869/14414215 to become this one-line
let workoutFav = UserDefaults.standard.array(forKey: "workoutFavorite") as? [Int] ?? [Int]()
You need to do that filter for each id in the favourites array. You get an array of arrays as a result. To get the final array, you need to join those arrays to a single array. This "map each thing to an array and join the arrays" operation is what a flatMap does:
workoutFav.flatMap { favouriteId in // for each favourite ID
jsonErgWorkouts.filter { $0.id == favouriteId } // find workouts that match the ID
} // flatMap joins all those arrays returns by "filter" together, no need to do anything else
First thing first please give a struct name with a capital so you can distinguish between instance of it. Second you need to have new array where you will store each favorite, and store permanently that array, core data or some base on server, form there you will fetch favorites.
The better way is to add property like isFavorite: Bool that is false by default, and if user change it you can set it to be true, in that way you can avoid using ids for that and you can store whole workout's in one array to core data or base that you use, after that you can fetch from there with
let favorites = workouts.compactMap { $0.isFavorite == true }
Here you go in that way, but just to mention it highly recommended that you store those type of data outside User defaults.
struct Fav {
let name: String
let id: String
}
let df = UserDefaults.standard
let jk = ["aaa", "bbb", "cccc"]
df.setValue(jk, forKey: "favorites")
let fav1 = Fav(name: "zzz", id: "aaa")
let fav2 = Fav(name: "bbb", id: "qqq")
let favs = [fav1, fav2]
let favIDs = df.value(forKey: "favorites") as? [String]
favIDs?.forEach({ (id) in
let f = favs.filter({$0.id == id}) // here it is
})
Environment:
Eureka: 5.2.1
Xcode: 11.4
iOS version: 13.4
TLDR; When I try to print the values out from the form, the text changes to the initial text
When row is tapped on, I programmatically enter in the first item in an array. This populates the value of the cell and I can move on to a different row.
let fruit = ["Apple", "Peach", "Pear"]
<<< PickerInlineRow<String>(){
$0.title = "Fruits"
$0.options = fruit
$0.tag = "pickerTag"
$0.value = "Select your favorite"
}
.onExpandInlineRow({ (cell, inlineRow, row) in
cell.detailTextLabel?.text = fruit[0]
})
When I click my "Review" button I try to print out the response from the pickerRow and I do not get what is actually in the cell I get a response of the initial text (i.e. Select your favorite)
<<< ButtonRow("Review") {
$0.title = "Review"
}
.onCellSelection { [weak self] (cell, row) in
self?.printoutValues()
}
in printoutValues func
let values = form.values()
print("picklerValue: \(values["pickerTag"] as? String)")
If a user actually moves the picker view instead of just tapped on the row and then to the next row then the value is printed correctly.
How can I populate the row when a user first taps on the row and have it save?
thanks
I believe this only stopped working in newer versions of Eureka, but anyways I figured it out.
In .onExpandInlineRow
you have to add inlineRow.value = fruit[0]
I'm having trouble understanding why my ordered set is only saving 1 entry.
my code is as follows:
let newInovice = NSEntityDescription.insertNewObjectForEntityForName("Invoice", inManagedObjectContext: managedObjectContext) as! InvoiceMO
let itemDetail = NSEntityDescription.insertNewObjectForEntityForName("InvoiceDetail", inManagedObjectContext: managedObjectContext) as! InvoiceDetailMO
// Data Entry
// Add invoice number to Invoice
newInovice.invoiceNumber = getInvoiceNum()
newInovice.date = invoiceDate
// Create mutable set to add details
for items in details {
itemDetail.detail = items[PropKeys.itemDescription]
let item = items[PropKeys.itemPrice] ?? "0"
let itemDouble = { return Double(item) ?? 0 }()
itemDetail.price = itemDouble
let quantity = items[PropKeys.itemQuantity] ?? "1"
let quantityInt = { return Int(quantity) ?? 0 }()
itemDetail.quantity = quantityInt
print("This is the item detail before insertion: \(itemDetail)")
newInovice.mutableOrderedSetValueForKey("invoiceDetails").addObject(itemDetail)
subtotal += itemDouble * Double(quantityInt)
}
print("details.Count = \(details.count)")
print("Start Mutable \(newInovice.invoiceDetails?.count) End Mutable Set")
// Save the Data
do {
try newCustomer.managedObjectContext?.save()
} catch {
print(error)
}
I've read through the docs and it seems like I'm doing this correctly but it only adds one entry to the ordered set even though I've iterated through the array of objects.
Here is my debugger showing the entries before they are added and the count of the managed object after adding the objects.
debuggerWindow
my relationships are set up as follows:
relationships
You are only creating one InvoiceDetail outside your for loop. Obviously, you will have to create one for each detail that is contained in the data array.