Why there are duplicated widgets in widgets gallery? - ios

I simply have 6 widgets for my app. And they are displayed as a bundle simple way like this:
#main
struct FirstWidgetBundle: WidgetBundle {
#WidgetBundleBuilder
var body: some Widget {
CurrentMonth()
WidgetComing()
WidgetDailyText()
WidgetOfficialSite()
SecondWidgetBundle().body
}
}
struct SecondWidgetBundle: WidgetBundle {
#WidgetBundleBuilder
var body: some Widget {
WidgetLatestNews()
WidgetLatestVideos()
}
}
And inside gallery they are displayed multiply times (exactly four times each of them). Why? There should be 18 options (6 widgets x 3 sizes) but there is 18 x 4 = 72. They are duplicated in gallery for ios 14 widgets as well as for ios 16 widgets. Why? Is it ios bug or my implementation is wrong?
Each of my six widget is different app extension with different bundle id, the same app group. All swift files in every extension are targeted to everyone. Why it matters if it does?

Related

iOS SwiftUI WidgetKit: My widgets don’t show up in the ‘Add Widgets’ section

I developed an app with 10 widgets but some iPhone, iPad or macOS users report that my widgets or the "App Name" don't appear in the widgets list when they tried to add the widgets of my app.
There's no way I can replicate the problem, but the number of the users that report this issue is growing up every day.
It’s not clear if the problem is caused by the iPhone model, a crash on the widget preview or other factors.
My widgets previews are built reading static data, so this data is the same in every conditions.
I suggest my users to do the following steps:
Start the app and go to check again if "App name" appears among widget List
Close every app and restart the device; then start the app and
go to check again if "App name" appears among widget List
Temporarily change system language and turn on Bold Text and go to check again if "App Name" appears among widget List
Uninstall and reinstall the app, start the app and go to check again if "App Name" appears among widget List
But all users who have tried these steps say that the app still does not appear in the list of widgets.
The following code is the one I use inside the main widgets swift file called TodayWidgetExtension:
import WidgetKit
import SwiftUI
struct PlaceholderView : View {
var body: some View {
Text("loading")
}
}
//MARK: - Main widget bundle configuration
#main
struct WidgetBundle: WidgetBundle {
#WidgetBundleBuilder
var body: some Widget {
//MARK: - 1
Widget1Large() //IntentConfiguration
Widget1Medium() //StaticConfiguration
Widget1Small() //StaticConfiguration
if #available(iOSApplicationExtension 16.0, *) {
Widget1Accessory() //StaticConfiguration
}
//MARK: - 2
Widget2Large() //StaticConfiguration
//MARK: - 3
#if !targetEnvironment(macCatalyst)
Widget3Medium() //StaticConfiguration
#endif
//MARK: - 4
Widget4Large() //StaticConfiguratio
//MARK: - 5
Widget5Medium() //IntentConfiguration
//MARK: - 6
Widget6Large() //IntentConfiguration
Widget6Medium() //IntentConfiguration
}
}
struct todaywidgetextension_Previews: PreviewProvider {
static var previews: some View {
PlaceholderView()
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
The only thing that I've noticed debugging the widgets extension is that, even if I don't add any of them to my iPhone screen, Xcode tells me that widgets occupied 20mb of memory (the limit for widgets is 30mb). I tried removing all the widgets from the above code leaving only one (Widget1Small for example), but Xcode continues to tell me that 20mb of memory are still occupied.
I hope that someone can help me.
Thank you.

Programmatically Collapse NavigationSplitView to One Column

I have developed a SwiftUI NavigationSplitView app with two columns and it works as expected.
I have chosen dynamic fonts and crafted the visual elements to respond to user settings
for visual accessibility. I know that a number of the users who will employ this app will have
the font size set to the largest available. While that enhances their view of the detail
screen, it pretty much makes the sidebar unreadable. I'm trying to find a way to change
the app's behavior based on the size of the dynamic type. Specifically, I'd like to have
the iPad behavior the same as an iPhone behavior based on a font size that I define.
As it is, the app on an iPhone collapses to a single column. I have not been able to
find a way to programmatically do this on an iPad.
I have tried:
Setting the horizontalSizeClass and as expected, this is a getter only.
.horizontalSizeClass = .compact
I have tried setting the column width of the detail to 0.
.navigationSplitViewColumnWidth(0)
that does not work
I have tried using the old stack function.
.navigationViewStyle(StackNavigationViewStyle())
that does not work.
Clearly, this can be done, since it is automatic when the device is an iPhone. I guess
I could create two views - one stacked and one split and choose based on a State variable
but that seems a bit crude.
Any guidance would be appreciated. Xcode 14 beta 6, iOS 16
This is a great idea and can be achieved by overriding the horizontalSizeClass in the environment depending on the environment value of dynamicTypeSize as follows:
struct NavigationSplitViewTest: View {
#Environment(\.dynamicTypeSize) var dynamicTypeSize
var body: some View {
ViewContainingSplitView()
.environment(\.horizontalSizeClass, dynamicTypeSize > .large ? .compact : .none)
}
}
I tested it by launching the app on iPad simulator and it was in 2 column, then switching to Settings to increase the type size by one notch then back to app and it had switched to single column. However when attempting to switch the size back I noticed the show/hide column button disappeared so I think we have some feedback to submit.
I still have not found an elegant way to do this, but my solution here does work.
Basically, I code for NavigationSplitView and for NavigationStack and change based on the
Environment(.dynamicTypeSize). Like this:
if dynamicTypeSize <= .xxLarge {
NavigationSplitView(columnVisibility: $columnVisibility) {
//all the code
} detail: {
//all the code
}
} else {
NavigationStack {
//all the code
}
}
For others, I was confused by the terms involved here. There is DynamicTypeSize, dynamic
fonts and Larger Accessibility Sizes. Dynamic fonts are those pre-built fonts that
support dynamic type out of the box - .title, .headline, .body etc. Dynamic type size is
the slider in settings that allows the user to scale the dynamic fonts to suit their
needs - all the dynamic fonts scale with the slider setting. Then on top of the slider
in settings for these pre-made fonts, is the option for Larger Accessibility sizes
which gives the user even bigger options.
My scheme above supports all of those intermingled options for both iPad and iPhone.

PDFView causes app to freeze on iOS 16, only when autoscales = true

I am using a PDFView to display images in my app (built using SwifUI), simply for quick and easy pinch-to-zoom functionality. This worked perfectly in iOS 15, but since updating to iOS 16, the app freezes when attempting to load the image viewer (PhotoDetailView below). The issue persists across both the simulator and a physical device.
Here is the code I'm using:
import SwiftUI
import PDFKit
struct PhotoDetailView: UIViewRepresentable {
let image: UIImage
func makeUIView(context: Context) -> PDFView {
let view = PDFView()
view.document = PDFDocument()
guard let page = PDFPage(image: image) else { return view }
view.document?.insert(page, at: 0)
view.autoScales = true
return view
}
func updateUIView(_ uiView: PDFView, context: Context) {
// empty
}
}
When I run the code on iOS 16.0 in the simulator, I get 2 errors in the console:
[Assert] -[UIScrollView _clampedZoomScale:allowRubberbanding:]: Must be called with non-zero scale
[Unknown process name] CGAffineTransformInvert: singular matrix.
I have been able to isolate the issue to view.autoscales = true. If I print view.scaleFactor, I can see that it is 1.0 before the autoscale and 0.0 afterward (which is what appears to be prompting the errors). These errors also show up in the console when using iOS 15 in the simulator, but the images load as expected.
When view.autoscales = true is commented out, the image loads, albeit at a size that is much larger than the device screen.
Does anyone have any idea what may be causing this? I'd really like to avoid having to build a custom viewer, since I'm just trying to let users quickly pinch to zoom on images.
I managed to resolve this issue. I'm posting my solution here, in case anyone else runs into the same type of problem.
The issue only occurred when using a NavigationLink to navigate to PhotoDetailView. This led me to look more closely at my navigation stack.
After some digging, I found that the problem was related to my use of .navigationViewStyle(.stack) on the NavigationView. I needed this modifier to get things to display correctly on iOS 15, but the same modifier was breaking my image viewer with iOS 16.
My solution was to create a custom container to replace NavigationView, which conditionally uses NavigationStack for iOS 16, or NavigationView + .navigationViewStyle(.stack) for iOS 15. That worked like a charm and my image viewer is back in action.
I found inspiration for my custom container here: https://developer.apple.com/forums/thread/710377

In the "Start Developing iOS Apps (Swift)" tutorial, as of Xcode 11.0 the Hello World Screen appears over the top of the canvas, hiding it

In April 2019 I got a third of the way through "Start Developing iOS Apps (Swift)" and it was working fine. I recently updated to Xcode 11.0 and started from the beginning again. In the Build a basic UI section, It shows how to draw a Text Field, Button & Label on the canvas. But when I run it in the simulator, it shows the 3 elements for a second, but then the white Hello World screen is drawn over the top of the elements and they can't be seen.
How to I stop the Hello World Screen Appearing.
The Tutorial I'm referring to is here:
https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/BuildABasicUI.html#//apple_ref/doc/uid/TP40015214-CH5-SW1
To get to this point I started a new Single View App and drew the 3 elements on the canvas in the file "LaunchScreen.storyboard", then compiled.
The Hello World screen is in a new file in the template called ContentView.swift which contains the below. The Tutorial was last updated December 2016 and Xcode has been updated many times since and the buttons are not as described, but I was able to find the functions using google. If anyone knows how to remove the Hello World Screen, that would be appreciated. I'm new to iOS development.
//
// ContentView.swift
// FoodTracker2
//
// Created by Steve on 25/9/19.
// Copyright © 2019 FinestSites.net. All rights reserved.
//
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#Finest you created a SwiftUI project which is different from interface builder. Both used to develop UI of the application.
As you mentioned you added elements in LaunchScreen.storyboard which is not correct.
LaunchScreen.storyboard is only presented only start of the application. Once the application is up and running it will be replaced by your main controller which in your case is ContentView. So it is performing the correct flow.
In the tutorial, it is using interface builder. So go and create a new single view app and uncheck Use SwiftUI as highlighted below.
After that locate Main.storyboard and add element into that viewController.
Hope it helps.

AttributeGraph: cycle detected through attribute using #State in playgrounds

I'm trying to use SwiftUI's #State in playground. Here's my code:
import UIKit
import PlaygroundSupport
import SwiftUI
struct ContentView: View {
#State private var showGreeting = false
var body: some View {
Form {
Button(action: {
self.showGreeting.toggle()
}) {
Text("Toggle Greeting")
}
if showGreeting {
Text("Hello World!")
}
}
}
}
let viewController = UIHostingController(rootView: ContentView())
PlaygroundPage.current.liveView = viewController
I can see the button on the live view. But if I click it, it doesn't show the text. A second click does reveal the text. Further clicks don't seem to make any difference visually.
On each click, I get the following message in log area:
=== AttributeGraph: cycle detected through attribute 38 ===
Need some help understanding what this means in this context and why the state is not behaving naturally.
Note: I'm running macOS Mojave, so I don't have option of SwiftUI previews. I'm making do with live preview of playgrounds.
There's nothing wrong with your SwiftUI, and it runs fine when pasted into a new project.
If you change the Form to a VStack, it works fine in the playground. Using a List gives you the same error. Maybe a bug, I'd report it to Apple.
In my experience, if you're stuck on Mojave for the time being, just set up a project where you can run the simulator. It takes just a couple seconds to compile and run, and you wind up saving time when your SwiftUI playgrounds crash, get weird errors like this, etc.

Resources