In my weather app, I call the weather forecast in a text field but I get the error: No exact matches in call to initializer.
Here is the code that might be related:
ScrollView(.horizontal){
HStack(spacing: 5.0) {
ForEach(0..<25) {_ in
VStack{
Text([weather.hourly.hourly]) //Error
.bold()
.font(.subheadline)
.frame(width: 105, height: 15)
}
}
}
}
struct OneCall: Decodable {
let lat, lon: Double
let timezone : String
let timezone_offset : Int
let current: CurrentResponse
let hourly: HourlyResponse
struct HourlyResponse: Decodable{
let sunrise: Date?
let sunset: Date?
var hourly: Array<CurrentResponse>
}
It should definitely be not an array in Text(). It is not clear what's type of .hourly, but it should be something like
Text("\(weather.hourly.hourly)")
Related
I have a chart showing a 10 day forecast using weatherkit, I want to show min and max temp per day so I use a BarMark with two values in the x axis. I want to show annotation for the min and one for the max. but somehow some of them appear double with different values.
is this an Apple issue or my code issue?
struct TenDayForecastViewChart: View {
let dayWeatherList: [DayWeather]
var body: some View {
VStack(alignment: .leading) {
Text("10-DAY FORECAST")
.font(.caption)
.opacity(0.5)
.padding()
Chart{
ForEach(dayWeatherList, id: \.date) { dailyWeather in
BarMark(xStart: .value("Temperature", dailyWeather.lowTemperature.converted(to: .fahrenheit).value),
xEnd: .value("Temperature", dailyWeather.highTemperature.converted(to: .fahrenheit).value),
y: .value("Day", dailyWeather.date.formatAsAbbreviatedDay())
)
.foregroundStyle(Color.black)
.annotation(position: .overlay, alignment: .leading) {
HStack {
//Image(systemName: "\(dailyWeather.symbolName)").foregroundColor(.white)
Text("\(dailyWeather.lowTemperature.converted(to: .fahrenheit).value, format: .number.precision(.fractionLength(0)))")
.foregroundColor(.white)
}
}
.annotation(position: .overlay, alignment: .trailing) {
Text("\(dailyWeather.highTemperature.converted(to: .fahrenheit).value, format: .number.precision(.fractionLength(0)))")
.foregroundColor(.blue)
}
}
}
//.chartLegend(position: .top, alignment: .bottomTrailing)
.chartXAxis(.hidden)
//.chartYAxis(.hidden)
.frame(height: 250)
.padding(.horizontal)
}
}
}
I suppose in your 10 days you have 2x "Sun", "Mon", "Tue" each. So the y plot value for these data points is identical, and by default BarCharts adds those together.
You can pass the full Date (not only the day String) into the BarChart and use
.value("Day", dailyWeather.date, unit: .day)
and add a custom formatting in .chartYAxis:
.chartYAxis {
AxisMarks(values: .stride(by: .day)) { _ in
AxisGridLine()
AxisTick()
AxisValueLabel(format: .dateTime.weekday(.abbreviated), centered: true)
}
}
I hope you're all well!
I have a (potentially) silly question. I'm attempting to learn SwiftUI development by recreating the Instagram UI.
Right now, I'm working on the user page that shows a single user and their profile statistics. I'm seeing some weird behaviour with my string formatting.
HStack (spacing: 24){
VStack (alignment: .center, spacing: 2) {
Text(postCountString)
.font(.system(size: 16, weight: .bold))
Text("Posts")
.font(.system(size: 13, weight: .regular))
}
VStack (alignment: .center, spacing: 2) {
Text(followerCountString)
.font(.system(size: 16, weight: .bold))
Text("Followers")
.font(.system(size: 13, weight: .regular))
}
VStack (alignment: .center, spacing: 2) {
Text(followsCountString)
.font(.system(size: 16, weight: .bold))
Text("Follows")
.font(.system(size: 13, weight: .regular))
}
}
.frame(maxWidth: .infinity)
.padding(.leading, 16)
.onAppear {
postCountString = UIHelperFunctions.FormatCountNumber(number: creator.posts.count)
followerCountString = UIHelperFunctions.FormatCountNumber(number: creator.followerCount)
followsCountString = UIHelperFunctions.FormatCountNumber(number: creator.followsCount)
}
Above is the View component that shows the main statistics of a creator (Posts, Followers, Follows). When the view is displayed, it should run each statistic through my UIHelperFunction that formats the number into a nice readable string see function
import Foundation
class UIHelperFunctions {
static func FormatCountNumber(number: Int) -> String {
guard number > 1000 else {
return String(number)
}
guard number > 10000 else {
return String(format: "%.1fK", Double(number/1000))
}
guard number > 1000000 else {
return String(format: "%dK", number/1000)
}
return String(format: "%.1fM", Double(number/1000000))
}
}
// Example with input: 32495
print(UIHelperFunctions.FormatCountNumber(number: 32495)) // Output: 32K
However, when I input a value between 1000 and 10000, it returns a close value; however, it leaves the decimal place as 0. Below is an image that shows what I see on the screen when I input the value 6748. I would expect it to output 6.7K, but I'm seeing 6.0K.
Am I doing something silly? If not, am I misunderstanding how floating point maths works in SwiftUI?
Any help would be greatly appreciated; let me know if you need more context or code.
Thanks in advance :)
Int divided on Int will be Int, converting it to Double is too late, you need instead to divide double on double, like
guard number > 10000 else {
return String(format: "%.1fK", Double(number)/Double(1000)))
}
SwiftUI does formatting for us and it automatically updates the UILabel on screen when the region settings change, simply do:
Text(creator.posts.count, format: .number)
or
Text("Count: \(creator.posts.count, format: .number)")
To get the M, K etc. in the future this should work (if this ever gets approved)
extension NumberFormatter {
static var compact: NumberFormatter = {
let f = NumberFormatter()
f.numberStyle = .compact
return f
}()
}
Text(creator.posts.count, formatter: NumberFormatter.compact)
I have two pickers on one screen of my app. What I want to achieve is when one selection is made in either picker, the selection updates in both pickers.
So, for example, if I choose 'heat pump' in the user system picker, the current system picker also updates to 'heat pump'. I would like for this situation to work in both directions.
Here is the first picker struct:
struct CurrentSystemPicker: View {
// Array of dummy data
let currentSystems: [String] = ["Air Conditioning", "Furnace", "Furance Air Conditioning", "Heat Pump"]
#State var selectedCurrentSystem: String = "Current System"
var body: some View {
Menu {
Picker("picker", selection: $selectedCurrentSystem) {
ForEach(currentSystems, id: \.self) { system in
Text(system).tag(system)
}
}
.labelsHidden()
.pickerStyle(InlinePickerStyle())
} label: {
HStack {
Rectangle()
.foregroundColor(Color(.systemBackground))
}
.fixedSize()
.frame(width: ESConstants().IS_IPAD ? 275 : 110, height: ESConstants().IS_IPAD ? 75 : 50)
.padding(.horizontal)
.background(Color.white)
.cornerRadius(ESConstants().IS_IPAD ? 15 : 10)
.overlay(
RoundedRectangle(
cornerRadius: ESConstants().IS_IPAD ? 15 : 10)
.stroke(Color.eSaverGray, lineWidth: 1))
.overlay(
Text("\(selectedCurrentSystem)"))
.foregroundColor(Color.black)
}
}
}
Here is the second picker struct:
struct UserSystemPicker: View {
// Array of dummy data
let userSystems: [String] = ["Air Conditioning", "Furnace", "Furance Air Conditioning", "Heat Pump"]
#State var selectedUserSystem: String = "Future system"
var body: some View {
Menu {
Picker("picker", selection: $selectedUserSystem) {
ForEach(userSystems, id: \.self) { system in
Text(system).tag(system)
}
}
.labelsHidden()
.pickerStyle(InlinePickerStyle())
} label: {
HStack {
Rectangle()
.foregroundColor(Color(.systemBackground))
}
.fixedSize()
.frame(width: ESConstants().IS_IPAD ? 275 : 110, height: ESConstants().IS_IPAD ? 75 : 50)
.padding(.horizontal)
.background(Color.white)
.cornerRadius(ESConstants().IS_IPAD ? 15 : 10)
.overlay(
RoundedRectangle(
cornerRadius: ESConstants().IS_IPAD ? 15 : 10)
.stroke(Color.eSaverGray, lineWidth: 1))
.overlay(
Text("\(selectedUserSystem)"))
.foregroundColor(Color.black)
}
}
}
Are these two structs exist in the same main view?
If they exist in the same main view, you can use #Binding inside both structs, with 1 #State variable in the main view for passing data between these two structs. example:
struct MainView: View {
#State var getData: String = ""
var body: some View {
VStack {
CurrentSystemPicker(bindingData: $getData)
UserSystemPicker(bindingData: $getData)
}
}
How could you achieve Icons like that?
I know that the base is this:
Image(systemName: "person.fill")
And than you could give it a background-Color:
Image(systemName: "person.fill")
.background(Color.blue)
To get rounded corners you could just add cornerRadius:
Image(systemName: "person.fill")
.background(Color.blue)
.cornerRadius(5)
But how would you make it that each of the items is in a square box with the same size?
Because SF Symbols don't have the same size.
And I don't want to make this:
Image(systemName: "person.fill")
.frame(width: 20, height: 20)
.background(Color.blue)
.cornerRadius(5)
The frame modifier would destroy the ability of SF Symbols to match with the preferred Font Size of the User.
Is there an other solution?
Or do you think the Settings App is done with .frame()?
Okay, I found an answer at Medium.
He works with Labels and adds an custom Modifier to them.
The Modifier looks like that:
struct ColorfulIconLabelStyle: LabelStyle {
var color: Color
var size: CGFloat
func makeBody(configuration: Configuration) -> some View {
Label {
configuration.title
} icon: {
configuration.icon
.imageScale(.small)
.foregroundColor(.white)
.background(RoundedRectangle(cornerRadius: 7 * size).frame(width: 28 * size, height: 28 * size).foregroundColor(color))
}
}
}
I did some changes:
struct ColorfulIconLabelStyle: LabelStyle {
var color: Color
func makeBody(configuration: Configuration) -> some View {
Label {
configuration.title
} icon: {
configuration.icon
.font(.system(size: 17))
.foregroundColor(.white)
.background(RoundedRectangle(cornerRadius: 7).frame(width: 28, height: 28).foregroundColor(color))
}
}
}
You can use it like that:
NavigationLink {
//Destination
} label: {
Label("Your Text", systemImage: "Your Image").labelStyle(ColorfulIconLabelStyle(color: .green))
}
This achieves a very native look :)
As I mentioned, full credits to Luca J.
I recommend this down way for you the update for my answer would be reading the device is zoomed or not! Then we could gave correct size for your UI, you can change your wished size in class.
struct ContentView: View {
var body: some View {
CustomButtonView(string: "gear", action: { print("setting!") })
CustomButtonView(string: "lasso.sparkles", action: { print("lasso!") })
CustomButtonView(string: "xmark.bin", action: { print("xmark!") })
CustomButtonView(string: "command", action: { print("command!") })
CustomButtonView(string: "infinity", action: { print("infinity!") })
}
}
struct CustomButtonView: View {
let string: String
let action: (() -> Void)?
init(string: String, action: (() -> Void)? = nil) {
self.string = string
self.action = action
}
#State private var tapped: Bool = Bool()
var body: some View {
Image(systemName: string)
.resizable()
.scaledToFit()
.frame(width: DeviceReader.shared.size - 5.0, height: DeviceReader.shared.size - 5.0)
.foregroundColor(Color.white)
.padding(5.0)
.background(tapped ? Color.blue : Color.gray)
.cornerRadius(10.0)
.onTapGesture { tapped.toggle(); action?() }
.animation(.interactiveSpring(), value: tapped)
}
}
class DeviceReader: ObservableObject {
let size: CGFloat
init() {
switch UIDevice.current.userInterfaceIdiom {
case .phone: self.size = 30.0
case .pad: self.size = 40.0
case .mac: self.size = 50.0
default: self.size = 30.0 }
}
static let shared: DeviceReader = DeviceReader()
}
I was looking at this question and your answer to it because this would be a useful thing to have. However, your answer does not allow the SFFont to scale with user preferences, and the answer you found on the Medium post does not scale well, as you can't just scale up and down with theses things. They look weird. If you run it in the simulator and change the Text setting, your will see what I mean.
I would simply use a .frame that changes it's size based off a preference key on the SF Symbol itself, and giving it a bit of padding extra. You could also simply add .padding() before your .background(), but the background would not necessarily be square. This method will set the width and height of the frame to slightly more than the biggest dimension of the SF Symbol, and it will fluidly change its size, not only allowing you to drop a .font() on it, but also handle the dynamic font sizes. This is a pure SwiftUI answer, using no UIKit.
struct ColoredIconView: View {
let imageName: String
let foregroundColor: Color
let backgroundColor: Color
#State private var frameSize: CGSize = CGSize(width: 30, height: 30)
#State private var cornerRadius: CGFloat = 5
var body: some View {
Image(systemName: imageName)
.overlay(
GeometryReader { proxy in
Color.clear
.preference(key: SFSymbolKey.self, value: max(proxy.size.width, proxy.size.height))
}
)
.onPreferenceChange(SFSymbolKey.self) {
let size = $0 * 1.05
frameSize = CGSize(width:size, height: size)
cornerRadius = $0 / 6.4
}
.frame(width: frameSize.width, height: frameSize.height)
.foregroundColor(foregroundColor)
.background(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(backgroundColor)
)
}
}
fileprivate struct SFSymbolKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue = CGFloat.zero
static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}
Use it like this:
ColoredIconView(imageName: "airplane", foregroundColor: .white, backgroundColor: .orange)
.font(.body)
My goal here is to make a SwiftUI struct which makes a scrolling view of data in a sequence, where the data, and the data_view get put together inside the ScrollView.
I get the title error from this code on the ForEach line. The type of the array doesn't seem to matter, I set it to 'Any', and got the same error.
If said in an English sentence, what is "(_) -> _"?
"A function taking underscore, returning underscore" doesn't make any sense.
What would make this code do what I want?
Is this even the best way to do this?
(I'm planning on working it to make the code more general, such as making a variable for the view function, but I want to get a working thing before going farther)
import SwiftUI
protocol DatedOrPrioritizedStruct {
var id: UUID {get}
var priority: Float {get set} ///Priority must be set
var start: Date? {get set} ///Could be nil. Used in conjunction with priority if set.
}
struct TimeLine : View {
var data: [DatedOrPrioritizedStruct]
var body: some View {
GeometryReader { geometry in
NavigationView {
VStack {
ScrollView {
ForEach(self.data, id: \.self) { item in
//^-ERROR redline here
TaskView(task: item).frame(minWidth: 0, idealWidth: geometry.size.width * 0.80, maxWidth: geometry.size.width, alignment: .leading)
}
}.frame(minWidth: 0, maxWidth: geometry.size.width, alignment: .leading)
.navigationBarTitle("Do The First Thing", displayMode: .large)
}
}.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
}
The Task class:
public class Task: DatedOrPrioritizedStruct, Identifiable, Equatable {
public static func == (lhs: Task, rhs: Task) -> Bool {
return lhs.id == rhs.id
}
public let id = UUID()
var title: String
var creation: Date
var priority: Float
var start: Date?
var deadline: Date?
var completed: Date?
var note: String?
var recur: Double?
var duration: TimeInterval
public init (_ title: String) {
self.title = title
self.creation = Date()
self.priority = 3.0
self.start = nil
self.deadline = nil
self.completed = nil
self.note = nil
self.recur = nil
self.duration = 3600.0
}
public init (_ title: String, priority: Float) {
self.title = title
self.creation = Date()
self.priority = priority
self.start = nil
self.deadline = nil
self.completed = nil
self.note = nil
self.recur = nil
self.duration = TimeInterval(9 * 24 * 3600 + 3 * 3600 + 20 * 60)
}
}
If you replace the
TaskView(task: item).frame(minWidth: 0, idealWidth: geometry.size.width * 0.80, maxWidth: geometry.size.width, alignment: .leading)
line with a simple Text("Testing"), you'll see the error change to
Protocol type 'DatedOrPrioritizedStruct' cannot conform to 'Hashable' because only concrete types can conform to protocols
You can fix this by specifying the ForEach's identifier to be \.id:
ForEach(self.data, id: \.id) { item in
TaskView(task: item).frame(minWidth: 0, idealWidth: geometry.size.width * 0.80, maxWidth: geometry.size.width, alignment: .leading)
}
Make sure that TaskView's task is of type DatedOrPrioritizedStruct, not Task, because since your data array contains DataOrPrioritizedStruct objects, the item in the ForEach will be a DataOrPrioritizedStruct, not a Task.
Side note: Currently, Xcode often gives irrelevant errors, like in this case, and you can try to figure out the true source by simplifying your code (which I did by putting Text("Testing") in the ForEach and commenting out the TaskView line).
Edit:
To clarify, TaskView expects to receive Task type, but the ForEach iterates on an array of DataOrPrioritizedStruct, so when you pass item into TaskView there is no guarantee that the object has the data members that are unique to Task, which is why TaskView's task's type must be DataOrPrioritizedStruct, not Task.