How can I loop from last item to first in a SwiftUi view?
I've tried the following
let myarray = ["1", "2", "3", "4"]
ForEach(stride(from: myarray.count, to: 1, by: -1), id: \.self) { i in
print(myarray[i])
}
//desired output: 4 3 2 1
Thanks!!
You can use reversed:
ForEach(myarray.reversed(), id: \.self) { item in
Text("\(item)")
}
Note that I'm using Text, not print -- this is SwiftUI, so you should have View code inside a ForEach.
Also note that \.self is generally dangerous in ForEach, but I'm assuming this is just sample code. Generally, you want something truly uniquely Identifiable
Related
In Swift Charts the signature for chartForegroundStyleScale to set the ShapeStyle for each data series is:
func chartForegroundStyleScale<DataValue, S>(_ mapping: KeyValuePairs<DataValue, S>) -> some View where DataValue : Plottable, S : ShapeStyle
The KeyValuePairs initialiser (init(dictionaryLiteral: (Key, Value)...)) only takes a variadic parameter so any attempt to initialise a foreground style from an array (in my case <String, Color>) results in the error:
Cannot pass array of type '[(String, Color)]' as variadic arguments of type '(String, Color)'
In my application the names of the chart series are set dynamically from the data so although I can generate a [String : Color] dictionary or an array of (String, Color) tuples I can't see that it's possible to pass either of these into chartForegroundStyleScale? Unless I'm missing something this seems like a odd limitation in Swift charts that the series names need to be hard coded for this modifier?
OK I've found an approach that works as long as an arbitrary limitation to the number of entries is acceptable (example below with max size of 4:
func keyValuePairs<S, T>(_ from: [(S, T)]) -> KeyValuePairs<S, T> {
switch from.count {
case 1: return [ from[0].0 : from[0].1 ]
case 2: return [ from[0].0 : from[0].1, from[1].0 : from[1].1 ]
case 3: return [ from[0].0 : from[0].1, from[1].0 : from[1].1, from[2].0 : from[2].1 ]
default: return [ from[0].0 : from[0].1, from[1].0 : from[1].1, from[2].0 : from[2].1, from[3].0 : from[3].1 ]
}
In my case I know that there won't be more than 20 mappings so this func can just be extended to accommodate that number.
Not ideal, but it works...
You could also pass an array of colors to .chartForegroundStyleScale(range:). As long as you add the colors to the array in the same order you add your graph marks it should work fine.
Not incredibly elegant either, but this approach works with an arbitrary number or entries.
struct GraphItem: Identifiable {
var id = UUID()
var label: String
var value: Double
var color: Color
}
struct ContentView: View {
let data = [
GraphItem(label: "Apples", value: 2, color: .red),
GraphItem(label: "Pears", value: 3, color: .yellow),
GraphItem(label: "Melons", value: 5, color: .green)
]
var body: some View {
Chart {
ForEach(data, id: \.label) { item in
BarMark(
x: .value("Count", item.value),
y: .value("Fruit", item.label)
)
.foregroundStyle(by: .value("Fruit", item.label))
}
}
.chartForegroundStyleScale(range: graphColors(for: data))
}
func graphColors(for input: [GraphItem]) -> [Color] {
var returnColors = [Color]()
for item in input {
returnColors.append(item.color)
}
return returnColors
}
}
I have this struct created.
struct MedItem: Identifiable {
let id = UUID() //Generates a unique string for each item listed below
let medThumbnail: String
let medTitle: String
let medDose: String
let medDoseval: String
let purpose: String
let takeAt: String
let refillDate: String
let refillRem: String
}
With this data added:
struct MedData {
static let mListData = [
MedItem(medThumbnail: "Levo", medTitle: "Levothyroxine", medDose: "15", takeAt: "8 am", medDoseval: "mcg", purpose: "Hypothyroidism", refillDate: "December 3, 2021", refillRem: "Refill Remaning: 1"),
] //Imagine more sample data here
I'm trying to sort values based on time range linked to the timeAt value.
ForEach (meds, id: \.id) { MedItem in
HStack(spacing: 10.0){
}
If time is between 2am - 12pm then morning, 12pm - 3pm then noon, 3pm - 6pm evening, and 7pm - 2am then night. Some meds will not have a time and they need to be catoegorized in a different bucket.
Thank you for your help :)
I build a todo app by using #SectionedFetchRequest to fetch the CoreData in IOS 15, I have two entity, one is "group" , the other is "Item", the relationship such as below:
the code is here:
// fetch request
#SectionedFetchRequest(
sectionIdentifier: \TodoListGroup.group,
sortDescriptors: [SortDescriptor(\TodoListGroup.date, order: .forward)],
animation: .default
) private var todos: SectionedFetchResults<String, TodoListGroup>
// view
ForEach(todos) { section in
Section(header:
HStack {
Text(section.id)
Spacer()
}) {
ForEach(section) { group in
ForEach(group.itemsArray) { item in
Text(item.content)
}
}
}
}
}
I can add, delete group or items ,it's no problem and the console debug show:
▿ Section
▿ results : 1 element
- 0 : <TodoListGroup: 0x600001ca9220> (entity: TodoListGroup; id: 0x944fd1b9b585c7fc <x-coredata://670F71DD-39DA-47BD-987D-7075311C04A1/TodoListGroup/p6>; data: {
date = "2021-11-05 03:20:59 +0000";
group = SwiftUI;
items = (
"0x944fd1b9b405c7ec <x-coredata://670F71DD-39DA-47BD-987D-7075311C04A1/ToDoListItem/p10>",
"0x944fd1b9b425c7ec <x-coredata://670F71DD-39DA-47BD-987D-7075311C04A1/ToDoListItem/p11>"
);
})
- id : "SwiftUI"
but when i reopen my app in Xcode, the section.id changed ,like this:
▿ Section
▿ results : 1 element
- 0 : <TodoListGroup: 0x600001a2e6c0> (entity: TodoListGroup; id: 0xb1980459e3a37372 <x-coredata://670F71DD-39DA-47BD-987D-7075311C04A1/TodoListGroup/p6>; data: {
date = "2021-11-05 03:20:59 +0000";
group = SwiftUI;
items = "<relationship fault: 0x6000039185a0 'items'>";
})
- id : "Drawing"
the id changed to another group's name, why ?
In this list, I want to display out all the items that contains this specific name.
My list items: ['US', 'SG', 'US']
print(list.contains("US"));
Using .contains() returns me a true or false but it doesn’t return me the list of strings that contains that. I want to only extract out the items that has 'US' from the list. In this case, there's 2 of it. Please help!
You can try doing it the following way -
List<String> myList = ['US', 'SG', 'US'];
print(myList.where((item) => item.contains("US")));
You can also display it directly inside a Text widget in the following way -
Text(
myList.where((item) => item.contains("US")).join(" "),
//The join function joins the elements of the Iterable into a single string with the separator provided as an argument.
),
Hope this helps!
UPDATE:
To display each of the word separately as a list you can display them inside a Column in the following way -
Column(
children: myList.map((value) {
if(value.contains("US")){
return Text(value,);
} else {
return Container();
//Return an empty Container for non-matching case
}
}).toList(),
)
The same thing can be used inside a ListView instead of Column if you want it to be scrollable.
Something like this?
var myList = ['US', 'SG', 'US'];
myList.forEach((w){
if(w == "US")
print(w);
});
To show:
class SO extends StatelessWidget {
var myList = ['US', 'SG', 'US'];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: myList.where((w) => w == "US").map((w) => Text(w)).toList(),
),
);
}
}
or for a single line display use, Text instead of Column widget mentioned above
Text(myList.where((w) => w.contains("US")).join(" "))
If you are using "list.contains". It will only show the list exist or not , If you want to print the value you have to use follow :
var fruits = [‘banana’, ‘pineapple’, ‘watermelon’];fruits.forEach((fruit) => print(fruit)); // => banana pineapple watermelon
If you want to print just banana then you can use this
var fruits = [‘banana’, ‘pineapple’, ‘watermelon’];fruits.(fruit) => print(fruit[0]); // => banana
I have a row structure like this
c:[
{ v: 'somevalue'},
{ v: 'somevalue'},
{
v: 'somevalue',
link: 'abc.com'
}
]
now I need all the rows which has link property present in 3rd column, is it possible using getFillteredRows function ?
first, to use cell properties correctly, the structure would resemble the following...
c:[
{ v: 'somevalue'},
{ v: 'somevalue'},
{
v: 'somevalue',
p: {
link: 'abc.com'
}
}
]
to get or set the properties, use the following methods...
getProperty(rowIndex, columnIndex, name)
setProperty(rowIndex, columnIndex, name, value)
adding in getFilteredRows (spelling - one L in filter)...
use the test function, to find all the rows which has link property present in 3rd column
var rowsFound = data.getFilteredRows([{
column: 2,
test: function (value, row, column, table) {
return (table.getProperty(row, column, 'link') !== null);
}
}]);