iOS client for Grain grain.social
ios photography atproto
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

Round F-number to 2 decimals, drop trailing zeros

Add formatAperture(_:) helper that emits "f/2", "f/2.5", "f/2.83" instead
of "f/2.0", "f/2.50", etc. GrainExif gains a formattedFNumber computed
property that re-parses the server-supplied string and routes it through
the helper, so settingsLine and any consumer using settingsLine picks up
the trimmed format automatically.

+23 -1
+23 -1
Grain/Models/Views/PhotoModels.swift
··· 1 1 import Foundation 2 2 3 + /// Format a Double aperture value as "f/N" with up to 2 fraction digits, dropping 4 + /// trailing zeros. e.g., 2.0 → "f/2", 2.5 → "f/2.5", 2.83 → "f/2.83". 5 + func formatAperture(_ value: Double) -> String { 6 + var str = String(format: "%.2f", value) 7 + while str.hasSuffix("0") { 8 + str.removeLast() 9 + } 10 + if str.hasSuffix(".") { str.removeLast() } 11 + return "f/" + str 12 + } 13 + 3 14 /// social.grain.photo.defs#photoView 4 15 struct GrainPhoto: Codable, Sendable, Identifiable { 5 16 let uri: String ··· 45 56 return parts.isEmpty ? nil : parts.joined(separator: " ") 46 57 } 47 58 59 + /// Re-formats the server-supplied fNumber string to drop trailing zeros. 60 + /// "f/2.0" → "f/2", "2.0" → "f/2", "f/2.83" → "f/2.83". 61 + var formattedFNumber: String? { 62 + guard let fNumber else { return nil } 63 + let cleaned = fNumber 64 + .replacingOccurrences(of: "f/", with: "") 65 + .trimmingCharacters(in: .whitespaces) 66 + guard let value = Double(cleaned) else { return fNumber } 67 + return formatAperture(value) 68 + } 69 + 48 70 var settingsLine: String? { 49 71 let parts = [ 50 72 focalLengthIn35mmFormat, 51 - fNumber, 73 + formattedFNumber, 52 74 exposureTime, 53 75 iSO.map { "ISO \($0)" }, 54 76 ].compactMap(\.self).filter { !$0.isEmpty }