Is SwiftUI if statement written correctly? - swiftui

I am using Xcode to make an app that has many buttons inside a ForEach loop inside a LazyVGrid. I added an if else statement to my code to make one of the buttons have a different font size, but it is causing Xcode to take a very long time to build the program and causing SourceKitService to take up large amounts of CPU. It was running well before I added the if else statement and I am unsure why it is taking so long to build now.
It is also giving me this error message: The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions.
Is there anything wrong with my code?
This is my code: (it is inside a struct)
var text: String
let columns = [
GridItem(.flexible(), spacing: 0),
GridItem(.flexible(), spacing: 0),
GridItem(.flexible(), spacing: 0),
GridItem(.flexible(), spacing: 0),
GridItem(.flexible(), spacing: 0),
GridItem(.flexible(), spacing: 0)]
let numbers = ["+", "-", "x", "รท", "=", "Carry", "(", ")", "%", "<", ">", "Borrow", "0", "1", "2", "3", "4", ",", "5", "6", "7", "8", "9", "."]
LazyVGrid(columns: columns, spacing: 0) {
ForEach(numbers, id: \.self) { number in
Button(action: {
text += number
}) {
ZStack {
Rectangle()
.frame(width: g.size.width/6, height: g.size.height/2)
.foregroundColor(.white)
.border(.gray, width: 1)
Text(number)
**if (number = "Carry") {
.font(.system(size: g.size.width/40))
} else {
.font(.system(size: g.size.width/20))
}**
.foregroundColor(.black)
}
}
.buttonStyle(.borderless)
}
}

For a start, you probably want the double equals sign to test equality, rather than the single equals for assignment (i.e., number == "Carry").
Also, using if-else statements around modifiers isn't great (I'm surprised it works at all, to be honest). Instead, you should limit the conditionals to the arguments you pass in. For example:
Text(number)
.font(
.system(
size: number == "Carry" ? g.size.width / 40 : g.size.width / 20
)
)

Related

SwiftUI String formatting is rounding my values to nearest whole number?

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)

SwiftUI LazyVGrid missing some content

I have been experimenting with LazyVGrid to produce a table with grid lines. I'm using XCode 13.4 & iOS15. Everything seemed to be going well until I parameterized all the column sizes, line widths, etc. The content consists of an array of row data with 5 pieces of text in each row. Here is the code:
struct StatisticsView: View {
private let columnMinWidths: [CGFloat] = [120, 50, 50, 50, 50]
private let rowHeight: CGFloat = 40
private let thinGridlineSize: CGFloat = 0.5
private let thickGridlineSize: CGFloat = 2
private let lineColour = Color.accentColor
private let columns: [GridItem]
private let rowContents = [
["Player's name", "Graham", "John", "Archie", ""],
["Round 1", "2", "3", "1", ""],
["Round 2", "3", "-", "2", ""],
["Round 3", "1", "2", "4", ""],
["Round 4", "2", "1", "1", ""]
]
init() {
self.columns = [
GridItem(.fixed(thickGridlineSize), spacing: 0),
GridItem(.flexible(minimum: columnMinWidths[0]), spacing: 0),
GridItem(.fixed(thickGridlineSize), spacing: 0),
GridItem(.flexible(minimum: columnMinWidths[1]), spacing: 0),
GridItem(.fixed(thinGridlineSize), spacing: 0),
GridItem(.flexible(minimum: columnMinWidths[2]), spacing: 0),
GridItem(.fixed(thinGridlineSize), spacing: 0),
GridItem(.flexible(minimum: columnMinWidths[3]), spacing: 0),
GridItem(.fixed(thinGridlineSize), spacing: 0),
GridItem(.flexible(minimum: columnMinWidths[4]), spacing: 0),
GridItem(.fixed(thickGridlineSize), spacing: 0),
]
}
var body: some View {
VStack(spacing: 0) {
// top horizontal gridline
lineColour.frame(height: thickGridlineSize)
LazyVGrid(columns: columns, alignment: .leading, spacing: 0) {
ForEach(0..<rowContents.count, id: \.self) { row in
lineColour.frame(height: rowHeight)
ForEach(0..<rowContents.first!.count, id: \.self) { col in
// cell contents with bottom gridline
VStack(spacing: 0) {
Text(rowContents[row][col])
.font(.caption2)
.frame(height: rowHeight - thinGridlineSize)
// tried using a colour fill but same result
// Color.green
// .frame(height: rowHeight - thinGridlineSize)
lineColour.frame(height: thinGridlineSize)
}
// vertical gridline between cells
lineColour.frame(height: rowHeight)
}
}
}
// bottom horizontal gridline
lineColour.frame(height: thickGridlineSize)
}
.padding(.horizontal, 8)
}
}
and here is what is being displayed on the simulator screen:
Can anyone see where I'm going wrong here? Any help would be much appreciated. Thanks.
You have to remove id: \.self from both loops. View Id become duplicated so it's not drawing more than one line.

How to eliminate spacing around LazyHGrid items?

I've set the spacing on the LazyHGrid and the GridItems to 0, yet there is still spacing in the vertical direction. Why is that? How do I eliminate that spacing?
struct ContentView: View {
let strs: [String] = ["aa", "", "bbbb"]
let hgRows: [GridItem] = [GridItem(.fixed(60), spacing: 0), GridItem(.fixed(60), spacing: 0)]
var body: some View {
VStack(spacing: 0) {
Text("Stuff at top").padding().frame(maxWidth: .infinity).background(Color.blue.opacity(0.5))
Divider()
ScrollView(.horizontal) {
LazyHGrid(rows: hgRows, spacing: 0) {
ForEach(1..<10) { i in
Text("B\(i).\(strs[i % strs.count])")
.padding().border(Color.gray, width: 1)
}
}
.padding(0)
.background(Color.green.opacity(0.5)) // Give HGrid a green background so I can see where it is.
}.layoutPriority(1)
Divider()
Spacer().layoutPriority(2)
}
}
}
By adjusting the GridItems, it's possible to eliminate the vertical space between items:
let hgRows: [GridItem] = [GridItem(.fixed(51), spacing: 0), GridItem(.fixed(51), spacing: 0)]
It clearly isn't the best solution, but that's how I fixed it. I hope this answers your question.

SwiftUi how to display a keyboard with numpad in the side

I need to display a full keyboard in my app with a numpad in the side, like the one I screenshot from a Boeing App.
I try the .keyboardType(.numberPad) and many other Modifiers, but I could not get the same keyboard as the App from Boeing mentioned before.
Any thoughts?
Thanks
Keyboard I'm looking for
The basic Apple keyboard includes numbers on the second page. I would just use that if you need characters and numbers, or use the .numberPad if you need only numbers. Unless you're using this keyboard for fullscreen iOS or the iPad, your screenshot looks too wide too for the UI to work. Additionally, building a keyboard would be a lot of work for any benefits your custom keyboard might provide.
Here's a working example of a custom keyboard to get you started. Obviously you'll need to work on the appearance and adding some more buttons, but this will illustrate the basic structure of what you want.
struct CustomKeybaord: View {
#State var text: String = ""
let topCharacters: [String] = ["Q", "W", "E", "R", "T", "Y", "U", "I","O", "P"]
let topMiddleCharacters: [String] = ["A", "S", "D", "F", "G", "H", "J", "K", "L"]
let bottomMiddleCharacters: [String] = ["Z", "X", "C", "V", "B", "N", "M"]
func keyboardTap(character: String) {
self.text += character
}
var body: some View {
VStack {
Text(text)
HStack {
ForEach(topCharacters, id: \.self) { character in
KeyboardButton(character: character, color: .white, function: keyboardTap)
.padding(5)
}
}
HStack {
ForEach(topMiddleCharacters, id: \.self) { character in
KeyboardButton(character: character, color: .white, function: keyboardTap)
.padding(5)
}
}
.padding(.horizontal, 50)
HStack {
ForEach(bottomMiddleCharacters, id: \.self) { character in
KeyboardButton(character: character, color: .white, function: keyboardTap)
.padding(5)
}
}
.padding(.horizontal, 75)
}
.background(Color.init(white: 0.9))
.frame(height: 100)
}
}
struct KeyboardButton: View {
let character: String
let color: Color
var function: (String) -> Void
var body: some View {
Button {
function(character)
} label: {
Text(character)
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(color)
.foregroundColor(.black)
}
}
}
You'll also want to abstract away the HStacked ForEachs into an intermediate view.
The hardest thing left for you to do is find a way to calculate the key button sizes responsibly. Unless you want them to be a fixed size (which would be a bad idea, because it wouldn't work for any but one iPad size) you'll have figure out a way to calculate a universal button size based on the overall screen width using GeometryReader. In my example I use frame(maxWidth: .infinity, maxHeight: .infinity) to give each each button in a row the same size, but this doesn't ensure that buttons on different rows will be the same size.

LazyVGrid where every item takes only the place it needs

I just wondered how you could make a LazyVGrid where every item takes only the place it needs, not less and not more.
I know about .flexible() but the problem is: My Items are different sized, that means I don't know how many of them will fit in a row.
Do you got any ideas?
Thanks for your help!
Boothosh
EDIT:
LazyVGrid(columns: [GridItem(.flexible())]) {
Color.blue
.frame(width: 200, height: 200)
Color.blue
.frame(width: 100, height: 100)
Color.blue
.frame(width: 100, height: 100)
}
This is a example what Im talking about. I want to achieve that this items are placed with evenly space around them. (Not below each other, like they are now)
You just need to specify what you want by using variables.
Try this :
struct ContentView: View {
let data = (1...100).map { "Item \($0)" }
let columns = [
// The number of grid Items here represent the number of columns you will
//see. so you can specify how many items in a row thee are .
// 2 grid Items = 2 items in a row
GridItem(.flexible()),
GridItem(.flexible()),
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}