Related
The code below is in my var body: some View {. When I run this, the app builds and runs exactly how I want. The problem is, if I remove one of the commented rows I get "Extra argument in call", and if I remove the comment for both I get the error "Extra arguments at positions #11, #12 in call". I really only need to call it 11 times, I only tried the 12 to see if the error was limited to 10 or just the last value.
Why would this work calling BarView() 10 times but not 11 or 12? I have tried to put this into a 1...10 loop instead but also could not figure out how to get that to work.
I am using xCode 12.2. I have been able to find a lot of people with the same error I get, but none of them are for the same reason so I have been unable to diagnose on my own. I really only build apps for fun and often stumble through the process and I'm just really stuck on this so any help would be really appreciated. Thanks.
HStack{
BarView(barGradeValue: 0, barSendValue: 10, barFlashValue: 10)
BarView(barGradeValue: 1, barSendValue: 20, barFlashValue: 10)
BarView(barGradeValue: 2, barSendValue: 30, barFlashValue: 10)
BarView(barGradeValue: 3, barSendValue: 40, barFlashValue: 10)
BarView(barGradeValue: 4, barSendValue: 50, barFlashValue: 10)
BarView(barGradeValue: 5, barSendValue: 60, barFlashValue: 10)
BarView(barGradeValue: 6, barSendValue: 50, barFlashValue: 10)
BarView(barGradeValue: 7, barSendValue: 40, barFlashValue: 10)
BarView(barGradeValue: 8, barSendValue: 30, barFlashValue: 10)
BarView(barGradeValue: 9, barSendValue: 20, barFlashValue: 10)
//BarView(barGradeValue: 10, barSendValue: 10, barFlashValue: 10)
//BarView(barGradeValue: 10, barSendValue: 10, barFlashValue: 10)
}
Then to show what this is calling, BarView() is the code below. Again, it works just how I want when I call it 10 times, just not more than that.
struct BarView: View {
var barGradeValue: Int
var barSendValue: CGFloat
var barFlashValue: CGFloat
//reference to main struct above
var content = ContentView()
var body: some View {
VStack {
Text(content.gradesV[barGradeValue])
ZStack (alignment: .top){
//Full height
Capsule().frame(width: 20, height: 100)
.foregroundColor(Color.gray)
//Flash Value
Capsule().frame(width: 20, height: barSendValue+barFlashValue)
.foregroundColor(Color.yellow)
//Send Value
Capsule().frame(width: 20, height: barSendValue)
.foregroundColor(content.gradesColor[barGradeValue])
}
}
}
}
The closure that HStack (and many other views) accept is a kind of function builder called ViewBuilder. It only supports up to 10 Views as arguments. These are all hardcoded in the SwiftUI module:
static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8, _ c9: C9) -> TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)> where C0 : View, C1 : View, C2 : View, C3 : View, C4 : View, C5 : View, C6 : View, C7 : View, C8 : View, C9 : View
The designers could hardcode more, but decided not to. This is probably because you can simplify your view's code to not need to put that many views in a view builder.
In this case, you can use a ForEach:
HStack {
ForEach(0..<11) { i in
BarView(barGradeValue: i, barSendValue: i < 6 ? (i + 1) * 10 : (11 - i) * 10, barFlashValue: 10)
}
}
The #ViewBuilder system in SwiftUI is limited to 10 views within any given view container. There's no argument for an 11th view, so you get that error.
The solution is to use Group or separate the views
Hey iam trying to figure out how i could limit my combinations of objects.
My problem:
I want to generate x teams with the most "point" outcome of 432 players, max team size is 7 players. Each player has an "position" attribute, every team must have each position one time and max 3 times. Also an player has an "worth" attribute. Each team cant be more worth than 70. Every player can only be one time in an team.
Data example:
{ name: 'James Harden', position: 'PG', weight: 15.9, points: 62.63 },
{ name: 'Russell Westbrook', position: 'PG', weight: 14.9, points: 56.12 },
{ name: 'LeBron James', position: 'SF', weight: 15.9, points: 55.67 },
{ name: 'Bradley Beal', position: 'SG', weight: 14.8, points: 52.69 },
{ name: 'Anthony Davis', position: 'PF', weight: 14.6, points: 52.16 },
{ name: 'Damian Lillard', position: 'PG', weight: 13.5, points: 49.34 },
{ name: 'Nikola Vucevic', position: 'C', weight: 12.9, points: 47.97 },
{ name: 'Domantas Sabonis', position: 'PF', weight: 12.8, points: 47.6 }
My try:
Well i tried to use the combination method from ruby:
b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
print b.combination(7).to_a
that take 52 minutes for my laptop so solve all combinations. so i search for a way to do it faster an to apply more options like mentioned above to limit the result of teams.
Later if i find an solution i want to build this thing into an rails app with picture of the players and so on. Hopefully someone can give me an hint or has done something similar to give advices.
Also how would i controll to use more cpu kernels to speed up the proccess ?
Imagine this array of timestamps [Double] :
hourlyTimes": [1551344400, 1551348000, 1551351600, 1551355200, 1551358800, 1551362400, 1551366000, 1551369600, 1551373200 ... ]
It corresponds to the hours for which I have data to display.
To keep it simple, here is the full array when I display only the hours (UTC) :
Hours = [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, 3, 6, 9, 12, 15, 18, 21, 0, 3, 6, 9, 12, 15, 18, 0, 6, 12, 18, 0, 6, 12, 18, 0, 6, 12, 18, 0, 6, 12, 18, 0, 6, 12, 18, 0]
Now here is what I want to achieve, I need to filter this array of timestamp, in order to only keep these specific hours on any day (three-hourly).
[0, 3, 6, 9, 12, 15, 18, 21]
Ok, now let's have a look at the code :
let arrayOfTimestamp = time.hourlyTimes
let arrayOfHours = arrayOfTimestamp.map({ Date.init(timeIntervalSince1970: $0).hoursUTC})
let hoursToKeep = [0, 3, 6, 9, 12, 15, 18, 21]
let filtered = arrayOfHours.intersection(with: hoursToKeep)
Some explanations here:
time in the first line is the response from the backend, returning me all the available timestamps.
struct MultiForecastTimeModel: Codable {
let hourlyTimes: [Double]
let dailyTimes: [Double]
}
.hoursUTC is just a Date extension, in order to retrieve the hour component of the Date object.
var hoursUTC: Int {
var calendar = Calendar.current
let timezone = TimeZone(abbreviation: "UTC")!
calendar.timeZone = timezone
return calendar.component(.hour, from: self)
}
And finally, .intersection is an extension too, in order to do the same as a classic intersection, but also keeping the index and the occurrences.
extension Collection where Element: Equatable {
func intersection(with filter: [Element]) -> [Element] {
return self.filter { element in filter.contains(element) }
}
}
Everything is working fine, my only issue is that I now have to associate all these values as an Array of tuples.
Like this :
let tuples = Array(zip(filtered, filtered.dropFirst()))
But with an actual timestamp (corresponding to real date), not just an array of Int (hour component).
Because in the end, here what I have to do :
self.hourlyMapDataSource.data.value = tuples
and my datasource is expecting a tuple of timestamp (Double, Double)
class HourlyMapDataSource : GenericDataSource<(Double, Double)>, UICollectionViewDataSource { }
Do you have any pieces of advice on how should I improve my code and/or my logic?
Thank you in advance.
EDIT: My tuple array should only contain timestamps whose time has been "validated" through the hoursToKeep array so that it has 3 hours difference between hours.
I will show you with the hours to keep it simple, but it's an actual corresponding timestamp that I want :
[(9, 12), (12, 15), (15, 18) ...]
var tupleArray = hourlyTimes.map { (time: $0, hour: Date.init(timeIntervalSince1970: $0).hoursUTC)}
Now you can run the reduce function on this
var selectedHours = tupleArray.filter { return hoursToKeep.contains($0.hour) }
This will give you an array of tuples which have the validated hours,
[(1551344400, 9), (1551348000, 12)]
I set up an array like this:
let modelArray = [
"Casual": ["health": 17, "weapon": 8, "crafting": 15, "social": 30],
"Soldier": ["health": 25, "weapon": 32, "crafting": 8, "social": 5],
"Doctor": ["health": 35, "weapon": 5, "crafting": 15, "social": 15],
"Dorothy": ["health": 15, "weapon": 15, "crafting": 20, "social": 20],
"Asian": ["health": 13, "weapon": 5, "crafting": 7, "social": 45],
"Police": ["health": 23, "weapon": 22, "crafting": 5, "social": 20]
]
How do I access the String (for example "Casual") value when looping?
for (index, model) in character.modelArray.enumerate()
{
print("\(index) carries: \(model[0]")
}
This gives me Type '(String, Dictionary)' has no subscript members
As Josh points out, your modelArray object is a dictionary of dictionaries. (let modelArray : [String: [String:Int]] is the full type information).
The dictionary within can't be subscripted using an Int, only a String.
Here's a version of your code, which will get some the health stat of each character:
for statDictionary in characters.modelArray {
let health = statDictionary["health"]
print(health)
}
further suggestion
Storing data like this in a dictionary is fine for some purposes, but you may find a cleaner, safer API can be made by creating structs (or classes) for holding this state information.
struct CharacterStats {
let health : Int
let weaponNumber : Int
// etc.
}
Then enumerating would be even simpler and require no loose string keys (which could be mistyped).
for stat in characters {
let health = stat.health
}
Just my point of view.
A dictionary of dictionaries is ugly
Create a model type
struct Model {
let name: String
let health: Int
let weapon: Int
let crafting: Int
let social: Int
}
and then your array
let models = [
Model(name: "Casual", health: 17, weapon: 8, crafting: 15, social: 30),
Model(name: "Soldier", health: 25, weapon: 32, crafting: 8, social: 5),
Model(name: "Doctor", health: 35, weapon: 5, crafting: 15, social: 15),
Model(name: "Dorothy", health: 15, weapon: 15, crafting: 20, social: 20),
Model(name: "Asian", health: 13, weapon: 5, crafting: 7, social: 45),
Model(name: "Police", health: 23, weapon: 22, crafting: 5, social: 20),
]
Looping
Now you can simply
for model in models {
print(model.name)
}
Update: Searching
if let doctor = models.filter({ $0.name == "Doctor" }).first {
print(doctor.health)
}
Background
I was trying to show the average traffic with respect to the hours DYNAMICALLY using highchart. That is to say I want it show the traffic repeatedly like this, when the time comes to 23:00 next I want it back to 0:00.
I am doing this by setting 24 categories ['0:00', '1:00'...,'23:00'], and adding points when the data is updated by ajax, say every 1 second.
var chart = new Highcharts.Chart({
...//some options
xAxis: {
categories:['0:00','1:00','2:00',...,'23:00']
},
load: function() {setInterval(updateData,1000)}
series: [] //empty series here, adding dynamically by ajax
})
the updateData is defined as a function
function updateData(){
var data = $.ajax(...)// get the data
var series = chart.series[0];
if(series.data.length < 24){ //when data.length is < 24 add directly
chart.series[0].addPoint(data,true,false);
}else{
chart.series[0].addPoint(data,true,true);//set the 3rd option to true to remove the first data point, actually here this data is equal to the first one since this is a circle, when it comes to 24:00 it is actually 0:00, and I should update the xAxis.
//code updating the axis categories to [1:00, 2:00...23:00,0:00]
xAxis.setCategories(categories);
}
}
the x-axis turns out to be [2:00, 3:00, ...23:00, 0:00, 24], That is to say the point that I add this time does NOT correspond to the categories[24]:0:00, It is actually corresponding to the categories[25] which is not exist, so it is set to 24 in default.
A solution (quick and dirty)
do not ring shift the categories but push a new circle to it,like:
categories.push("time");//time is 0:00-23:00
xAxis.setCategories(categories);
but this will make the categories larger and larger..which is bad.
How can I fix this.
Another solution(also with some problems)
By using datetime as the type of x-axis, there is another problem. My data format is as follws
time count
8:00 23
9:00 56
... ...
and I can construct Points like [time, count], the question is I have time only. Even if I construct data by manually adding a date like
time = (new Date("2012-11-17 "+time)).getTime()
seems feasible. But when it got through 24 hours, the spline comes back to the left of the chart since the x-axis value here is equal to the first one.
BTW: how can I make the x-axis show only the time, the image above showed date at the left side, and the time interval is automatically display how to make it display all?
Thank You for your attention!!!
I followed your advice #Ruchit Rami and revised my code:
/*update part*/
var time = series.points.length > 0 ? series.points[series.points.length-1].category+3600 : 0;
//from 1970-1-1 0:00 And **add 1 hour everytime**
if (series.data.length < 24) {
series.addPoint([time, sum],true,false);
} else {
series.addPoint([time, sum],true,true);
}
/*chart part*/
xAxis: {
type: 'datetime',
dateTimeLabelFormat: {
second: '%H:%M',
minute: '%H:%M',
hour: '%H:%M',
day: '%H:%M',
week: '%H:%M',
month: '%H:%M',
year: '%H:%M'
}
tickInterval: 3600
}
The result
The date label format seems not affected though I specify it
And the time is not correct. Why? Thanks!
still didnt displayed right
To display only time on the X-axis you can use dateTimeLabelFormats property of xaxis and you can use tickInterval for setting tick interval on xaxis.
//Sets tickInterval to 24 * 3600 * 1000 if display is by day
tickInterval : 24 * 3600 * 1000,
dateTimeLabelFormats : {
hour : '%H:%M',
day : "%H:%M"
}
Now, for the issue of chart redraw from the first point, you will need to increase the date of the xaxis point. I am unable to come up with any elegant solution but you can check for time "0:00" while adding new points dynamically and before adding that point increase its date part by one compared to last point in the series.You can find the last point of the series by series.points[series.points.length-1]. Hope i haven't missed anything.
Now, this may not be what you are looking for but I am making some assumptions about what you want to show:
Data by hour for one "day" 0000-2300
Do not care to show the date just the time
Data fluctuates across "days" for a given time
I have created an example here of the chart iterating through 4 "days" of data. I did this by creating an empty data series first to draw the chart, then on load of the chart I iterate through an array of data sets every 1.5 seconds. It then loops back over to the beginning. I have it updating a DIV with what the "day" is. Please let me know if I have missed some condition you need.
Here is the looping code:
var data1 = [5, 8, 2, 5, 7, 4, 1, 2, 30, 20, 50, 30, 150, 130, 70, 50, 20, 47, 32, 18, 20, 15, 16, 8];
var data2 = [9, 15, 3, 4, 1, 1, 0, 0, 60, 75, 112, 190, 267, 365, 258, 164, 168, 190, 47, 16, 20, 18, 5, 8];
var data3 = [15, 18, 12, 5, 7, 4, 1, 2, 130, 20, 150, 130, 150, 130, 70, 50, 20, 47, 32, 18, 20, 15, 16, 8];
var data4 = [19, 15, 13, 4, 11, 11, 0, 0, 60, 175, 112, 190, 267, 365, 258, 164, 168, 190, 47, 16, 20, 18, 15, 18];
var dataSet = [data1, data2, data3, data4];
var dataIter = 0;
$(function() {
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
events: {
load: function() {
var series = this.series[0];
setInterval(function() {
if (dataIter >= dataSet.length) dataIter = 0;
series.setData(dataSet[dataIter], true);
$("#dayLabel").text("Day - " +dataIter.toString());
dataIter += 1;
}, 1500);
}
}
},
xAxis: {
categories: ['0000', '0100', '0200', '0300', '0400', '0500', '0600', '0700', '0800', '0900', '1000', '1100', '1200', '1300', '1400', '1500', '1600', '1700', '1800', '1900', '2000', '2100', '2200', '2300'],
labels: {
rotation: 90
}
},
yAxis: {
min: 0,
max: 500
},
series: [{
data: []}]
});
});