Needed some easy wins today. So I worked on adding some simple pages to screencred.app for support, press kit, and terms. Press kit still needs screenshots and stuff.
I also found some issues with the App Clip—wrong colors and a placeholder view. I will get those fixed tomorrow.
I knew I had seen. Feature in Swift strings that could automatically pluralize words depending on a value. Took me a while to find the actual thing I wanted. Turns out it’s called morphology. Jordan Morgan has a great post about it.
It works nicely when using Text in SwiftUI:
Text("Last compared \(timestamp.formatted()) and seen ^[\(historyItems.count) time](inflect: true)")
It’s not the nicest syntax, but better than doing it all myself.
As part of my efforts to improve accessibility in ScreenCred, I’ve been working at improving layouts for large Dynamic Type sizes. To help with this, I’m trying to use ViewThatFits. It works as I’d expect in most places. However, I had a lot of issues when trying to use it within a ScrollView for repeating items. I’m sure that a lot of my issues are from not really knowing how ViewThatFits chooses which view to use. What is “fits”? I’m not 100% sure.
In ScreenCreds, I have a couple places with lists and I want every item in that list to either be a horizontal layout or a vertical layout, but not a mix of both.
On an individual view, ViewThatFits works real well:
If the text gets too long, it will switch to use the VStack View. But, if I put that in a ForEach, which view that fits is dependent on the content, so some items may use HStack and others will VStack. Not what I want.
Unfortunately, this did not work. No matter what I tried, it always picked the second View. The only way I found to fix this is to switch between ScrollViews:
The way my views are constructed, it wasn’t as simple as this. It would’ve been tricky to refactor things to work like this. So I came up with a solution to use an Environment value.
enumLayout{
case horizontal, vertical
}
privatestructLayoutKey: EnvironmentKey{
staticlet defaultValue = Layout.horizontal
}
extensionEnvironmentValues{
var layout: Layout {
get { self[LayoutKey.self] }
set { self[LayoutKey.self] = newValue }
}
}
ViewThatFits {
Main()
.environment(\.layout, .horizontal)
Main()
.environment(\.layout, .vertical)
}
In this case, the ScrollView is a few views deep in Main. Each repeated item is a few views deeper. But, at the point I need to decide which layout to use, I can grab my layout Environment value.
So far, this seems to work the way I want! I’m not totally sure if this is performant or not.
Screen recording of ScreenCred showing the layout changing when using large Dynamic Type
I was really glad I was able to get ViewThatFits to work. The only other alternative was changing layouts at some arbitrary sizeCategory. That would be gross because it would depend on screen size and all that.
SO hopefully this will continue to work well. I should probably rewatch the WWDC video about ViewThatFits.
P.S. In places I couldn’t use the Xcode Previews, I’ve been using Sim Genie to easily change Dynamic Type in the simulator. Fantastic app.
Not sure what happened, but I was no longer able to access https://screencred.app. I’m hosting it on fly.io, and using Cloudflare in front of it. Seemed like the fly.io certificate expired.
The trick seemed to be that I needed to make sure my _acme-challengeCNAME record is unproxied, but keep the A and AAAA records proxied in Cloudflare. I now see that the fly.io certificate has been renewed. So hopefully that’s all it is.
Quick report. Been going through ScreenCred with Voice Over and on an iPhone 13 mini simulator with Accessibility Extra Extra Extra Large Dynamic Type enabled. So far I have found about 30 things that need to be fixed and improved. Most are small, simple things. Others are going to require new designs at larger text sizes. I’ll get those fixed and then go through it all again.
Screenshot of ScreenCred showing a tip jar view with 4 tip options. $1, $3, $5, and $10.Screenshot of a thank you with confetti falling that is shown after a tip is purchased.
I went with the new nice round numbers $1.00, $3.00, $5.00, and $10.00. To me, those seem a little more personal for some reason. Went with the generic copy iPhone naming for the tip names.
I thought about writing my own confetti view for the thank you, but found ConfettiSwiftUI. Was simple to add and looks really nice!
The tips are consumable in-app purchases and don’t unlock anything. But, when the purchase is completed, I do store in NSUbiquitousKeyValueStore that a tip was purchased. I’m hoping I can use this in the future to unlock things like alternate app icons or something. Never used it before, so hopefully it works…
With the tip jar done, I’m moving on to going through the app as carefully as I can to make sure it’s as accessible as I can make it, including Dynamic Type sizes. I’m getting close to ScreenCred being at a place where I would be comfortable releasing it. I decided I want to release it before I start working on my other app idea.