From 8d5daf3edd29d0eafbcf3060e5cdc4f7a8ecec36 Mon Sep 17 00:00:00 2001 From: Shawn Date: Thu, 25 Jan 2024 15:01:16 -0700 Subject: [PATCH] Cleanup home_widget views, add comments, fix rounding issue --- ios/Runner.xcodeproj/project.pbxproj | 8 +++-- ios/WonderousWidget/Colors.swift | 1 + ios/WonderousWidget/FlutterAssets.swift | 22 ++++++++++++ .../WonderWidgetViewComponents.swift | 6 ++-- ios/WonderousWidget/WonderousWidget.swift | 14 +++++--- .../WonderousWidgetBundle.swift | 10 ++---- ios/WonderousWidget/WonderousWidgetView.swift | 34 ++++++++----------- .../widgets/_collection_footer.dart | 3 +- pubspec.yaml | 2 +- 9 files changed, 62 insertions(+), 38 deletions(-) create mode 100644 ios/WonderousWidget/FlutterAssets.swift diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 1e9d1359..75d81261 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 296251252AE7410D00D574FF /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 296251242AE7410D00D574FF /* Colors.swift */; }; + 2978ECDD2B62D00C00E36CE8 /* FlutterAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2978ECDC2B62D00C00E36CE8 /* FlutterAssets.swift */; }; 297F6FC72AD06E0D00FF159E /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 297F6FC62AD06E0D00FF159E /* WidgetKit.framework */; }; 297F6FC92AD06E0D00FF159E /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 297F6FC82AD06E0D00FF159E /* SwiftUI.framework */; }; 297F6FCC2AD06E0D00FF159E /* WonderousWidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 297F6FCB2AD06E0D00FF159E /* WonderousWidgetBundle.swift */; }; @@ -73,6 +74,7 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 296251242AE7410D00D574FF /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; + 2978ECDC2B62D00C00E36CE8 /* FlutterAssets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlutterAssets.swift; sourceTree = ""; }; 297F6FC52AD06E0D00FF159E /* Wonderous WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; name = "Wonderous WidgetExtension.appex"; path = WonderousWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 297F6FC62AD06E0D00FF159E /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; 297F6FC82AD06E0D00FF159E /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; @@ -141,6 +143,7 @@ 297FD5732AE18011008D8BFE /* WonderousWidgetView.swift */, 297FD5752AE19BD9008D8BFE /* WonderWidgetViewComponents.swift */, 296251242AE7410D00D574FF /* Colors.swift */, + 2978ECDC2B62D00C00E36CE8 /* FlutterAssets.swift */, ); path = WonderousWidget; sourceTree = ""; @@ -451,6 +454,7 @@ 297FD5762AE19BD9008D8BFE /* WonderWidgetViewComponents.swift in Sources */, 296251252AE7410D00D574FF /* Colors.swift in Sources */, 297F6FD32AD06E0F00FF159E /* WonderousWidget.intentdefinition in Sources */, + 2978ECDD2B62D00C00E36CE8 /* FlutterAssets.swift in Sources */, 297FD5742AE18011008D8BFE /* WonderousWidgetView.swift in Sources */, 297F6FCE2AD06E0D00FF159E /* WonderousWidget.swift in Sources */, 297F6FCC2AD06E0D00FF159E /* WonderousWidgetBundle.swift in Sources */, @@ -640,14 +644,14 @@ CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_ENTITLEMENTS = "WonderousWidgetExtension.entitlements"; + CODE_SIGN_ENTITLEMENTS = WonderousWidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = S3TL5AY6Y3; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "WonderousWidget/Info.plist"; + INFOPLIST_FILE = WonderousWidget/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Wonderous Widget"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; IPHONEOS_DEPLOYMENT_TARGET = 16.4; diff --git a/ios/WonderousWidget/Colors.swift b/ios/WonderousWidget/Colors.swift index c362b248..21a32c4a 100644 --- a/ios/WonderousWidget/Colors.swift +++ b/ios/WonderousWidget/Colors.swift @@ -1,6 +1,7 @@ import Foundation import SwiftUI +/// Define some custom extensions on the Color class, so we can use the shorthand syntax `..myColor` extension Color { public static let accent = Color(red: 0.89, green: 0.58, blue: 0.36) public static let offWhite = Color(red: 0.97, green: 0.92, blue: 0.9) diff --git a/ios/WonderousWidget/FlutterAssets.swift b/ios/WonderousWidget/FlutterAssets.swift new file mode 100644 index 00000000..719403af --- /dev/null +++ b/ios/WonderousWidget/FlutterAssets.swift @@ -0,0 +1,22 @@ +import Foundation + +struct FlutterImages { + static let bgEmpty = getAssetPath("/assets/images/widget/background-empty.jpg") + static let icon = getAssetPath("/assets/images/widget/wonderous-icon.png") +} + +func getAssetPath(_ path : String) -> String { + return assetBundleUrl.appending(path: path).path() +} + +// Returns a file path to the location of the flutter assetBundle +var assetBundleUrl: URL { + let bundle = Bundle.main + if bundle.bundleURL.pathExtension == "appex" { + // Peel off two directory levels - MY_APP.app/PlugIns/MY_APP_EXTENSION.appex + var url = bundle.bundleURL.deletingLastPathComponent().deletingLastPathComponent() + url.append(component: "Frameworks/App.framework/flutter_assets") + return url + } + return bundle.bundleURL +} diff --git a/ios/WonderousWidget/WonderWidgetViewComponents.swift b/ios/WonderousWidget/WonderWidgetViewComponents.swift index 042f599f..c6c9b31d 100644 --- a/ios/WonderousWidget/WonderWidgetViewComponents.swift +++ b/ios/WonderousWidget/WonderWidgetViewComponents.swift @@ -10,15 +10,14 @@ struct BgImage : View { var uiImage:UIImage?; // If there is no saved imageData, use the default bg image if(entry.imageData.isEmpty){ - let defaultImage = flutterAssetBundle.appending(path: "/assets/images/widget/background-empty.jpg").path(); - uiImage = UIImage(contentsOfFile: defaultImage); + uiImage = UIImage(contentsOfFile: FlutterImages.bgEmpty); } // Load a base64 encoded image that has been written by the flutter app else { uiImage = UIImage(data: Data(base64Encoded: entry.imageData)!) } if(uiImage != nil){ - // Use geometry reader to prevent the image from pushing the other content out of the widgets bounds (https://stackoverflow.com/questions/57593552/swiftui-prevent-image-from-expanding-view-rect-outside-of-screen-bounds) + // Use geometry reader to prevent an oversized bg image from pushing the other content out of the widgets bounds (https://stackoverflow.com/questions/57593552/swiftui-prevent-image-from-expanding-view-rect-outside-of-screen-bounds) let image = GeometryReader { geometry in Image(uiImage: uiImage!) .resizable() @@ -34,6 +33,7 @@ struct BgImage : View { } +// Declares a restyled version of the native ProgressView struct GaugeProgressStyle: ProgressViewStyle { func makeBody(configuration: Configuration) -> some View { let fractionCompleted = configuration.fractionCompleted ?? 0 diff --git a/ios/WonderousWidget/WonderousWidget.swift b/ios/WonderousWidget/WonderousWidget.swift index 64bc89f6..3a83b234 100644 --- a/ios/WonderousWidget/WonderousWidget.swift +++ b/ios/WonderousWidget/WonderousWidget.swift @@ -2,19 +2,20 @@ import WidgetKit import SwiftUI import Intents -/// Entry, is passed into the view and defines the data it needs +/// Every home-widget requires a TimelineEntry. This is passed into the view and propvides any data it needs struct WonderousTimelineEntry : TimelineEntry { + // Date is a mandatory field for all TimelineEntries let date: Date + // Custom field for the wonderous view let discoveredCount:Int; var title:String = ""; var subTitle:String = ""; var imageData:String = ""; } -// Widget, defines the display name and description and also declared the main View +/// Widget, defines some high level configuration options as well as the primary view that will display the widget. struct WonderousWidget: Widget { let kind: String = "WonderousWidget" - var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: WonderousTimelineProvider()) { entry in WonderousWidgetView(entry: entry) @@ -26,7 +27,12 @@ struct WonderousWidget: Widget { } } -// Provider,returns various WonderousEntry configs based on current context +struct WonderousConfig { + let iosKey = "group.com.gskinner.flutter.wonders.widget" + let discoveredCountKey = "dicoveredCount" +} + +/// TimelineProvider, returns various WonderousTimelineEntry configurations for different contexts struct WonderousTimelineProvider: TimelineProvider { // Provide an entry for a placeholder version of the widget func placeholder(in context: Context) -> WonderousTimelineEntry { diff --git a/ios/WonderousWidget/WonderousWidgetBundle.swift b/ios/WonderousWidget/WonderousWidgetBundle.swift index 09a92c69..badada5a 100644 --- a/ios/WonderousWidget/WonderousWidgetBundle.swift +++ b/ios/WonderousWidget/WonderousWidgetBundle.swift @@ -1,13 +1,9 @@ -// -// WonderousWidgetBundle.swift -// Wonderous Widget -// -// Created by Shawn on 2023-10-06. -// - import WidgetKit import SwiftUI +// WonderousWidgetBundle +// -> WonderousWidgetView +// -> WonderousWidgetViewComponents @main struct WonderousWidgetBundle: WidgetBundle { var body: some Widget { diff --git a/ios/WonderousWidget/WonderousWidgetView.swift b/ios/WonderousWidget/WonderousWidgetView.swift index b0fa9809..bea81659 100644 --- a/ios/WonderousWidget/WonderousWidgetView.swift +++ b/ios/WonderousWidget/WonderousWidgetView.swift @@ -2,7 +2,7 @@ import WidgetKit import SwiftUI import Intents -// Defines the view / layout of the widget +/// Defines the view / layout of the widget struct WonderousWidgetView : View { @Environment(\.widgetFamily) var family: WidgetFamily var entry: WonderousTimelineProvider.Entry @@ -10,14 +10,13 @@ struct WonderousWidgetView : View { let showTitle = family == .systemLarge let showIcon = family != .systemSmall let showTitleAndDesc = family != .systemSmall - - let progress = Double(entry.discoveredCount) / 24.0 - let iconImage = flutterAssetBundle.appending( - path: "/assets/images/widget/wonderous-icon.png" - ).path() + let progressPct = Double(entry.discoveredCount) / 24.0 + let iconImage = FlutterImages.icon; let title = entry.title.isEmpty ? "Wonderous" : entry.title; let subTitle = entry.subTitle.isEmpty ? "Search for hidden artifacts" : entry.subTitle; + let content = VStack{ + // Top row with optional Title and Icon HStack { if(showTitle) { Text("Collection") @@ -32,7 +31,10 @@ struct WonderousWidgetView : View { .frame(height: 24) } } + Spacer(); + + // Bottom hz row with title, desc and progress gauge HStack { if(showTitleAndDesc) { VStack(alignment: .leading){ @@ -46,14 +48,16 @@ struct WonderousWidgetView : View { } Spacer(); ZStack{ - ProgressView(value: progress) + ProgressView(value: progressPct) .progressViewStyle(GaugeProgressStyle()) .frame(width: 48, height: 48) - Text("\(Int(progress * 100))%").font(.system(size: 13)).foregroundColor(.white) + + Text("\(Int((progressPct * 100).rounded()))%").font(.system(size: 13)).foregroundColor(.white) } } } + // Stack content on top of the background image and a gradient return ZStack{ BgImage(entry: entry).opacity(0.8) LinearGradient( @@ -62,21 +66,11 @@ struct WonderousWidgetView : View { endPoint: .bottom) content.padding(16) } + // Ios requires that widgets have a background color .widgetBackground(Color.darkGrey) + // Deeplink into collections view when tapped .widgetURL(URL(string: "wonderous:///home/collection")) } } -// Todo: Refactor to getFlutterAsset(String path), include /assets, or maybe just getFlutterImage(String path), include assets/images -// Returns a file path to the location of the flutter assetBundle -var flutterAssetBundle: URL { - let bundle = Bundle.main - if bundle.bundleURL.pathExtension == "appex" { - // Peel off two directory levels - MY_APP.app/PlugIns/MY_APP_EXTENSION.appex - var url = bundle.bundleURL.deletingLastPathComponent().deletingLastPathComponent() - url.append(component: "Frameworks/App.framework/flutter_assets") - return url - } - return bundle.bundleURL -} diff --git a/lib/ui/screens/collection/widgets/_collection_footer.dart b/lib/ui/screens/collection/widgets/_collection_footer.dart index fa0ddc11..82685133 100644 --- a/lib/ui/screens/collection/widgets/_collection_footer.dart +++ b/lib/ui/screens/collection/widgets/_collection_footer.dart @@ -42,9 +42,10 @@ class _CollectionFooter extends StatelessWidget { } Widget _buildProgressRow(BuildContext context) { + int percent = (count / total * 100).round(); return Row(children: [ Text( - $strings.collectionLabelDiscovered((count / total * 100).round()), + $strings.collectionLabelDiscovered(percent), style: $styles.text.body.copyWith(color: $styles.colors.accent1), ), Spacer(), diff --git a/pubspec.yaml b/pubspec.yaml index c5dc1b7f..5ea91820 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: wonders description: Explore the famous wonders of the world. publish_to: "none" -version: 2.2.0 +version: 2.2.1 environment: sdk: ">=2.17.0 <3.0.0"