I want to add a tool bar (setting search )to my app just like the apple file app using pure swiftUI, is it possible, if not, can i using a UIKit to implement it.
struct MainView: View {
var body: some View {
TabView {
Tab("View 1", systemImage: "square.grid.3x2") {
View1()
}
Tab("View 2", systemImage: "square.grid.2x2") {
View2()
}
}
.tabViewStyle(.sidebarAdaptable)
}
UIKit
RSS for tagConstruct and manage graphical, event-driven user interfaces for iOS or tvOS apps using UIKit.
Posts under UIKit tag
200 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
in iOS, user can set focus on UItextField and tapping a key in the virtual keyboard updates the text in the textfield. This user action causes the relevant delegates of UITextFieldDelegate to get invoked, i.e the handlers associated with action of user entering some text in the textfield.
I m trying to simulate this user action where I am trying to do this programatically. I want to simulate it in a way such that all the handlers/listeners which otherwise would have been invoked as a result of user typing in the textfield should also get invoked now when i am trying to do it programatically. I have a specific usecase of this in my application.
Below is how I m performing this simulation.
I m manually updating the text field associated(UITextField.text) and updating its value.
And then I m invoking the delegate manually as textField.delegate?.textField?(textField, shouldChangeCharactersIn: nsRange, replacementString: replacementString)
I wanted to know If this is the right way to do this. Is there something better available that can be used, such that simulation has the same affect as the user performing the update?
In macOS application, we are using SwiftUI as an entry point to our application and attaching appdelegate using NSApplicationDelegateAdaptor.
We are using NSViewControllerRepresentable to add a View Controller to the hiracrchy so that we can store intance of viewcontroller and add content to it programatically .
@main
struct TWMainApp: App {
@NSApplicationDelegateAdaptor private var appDelegate: TWAppDelegate
internal var body : some Scene {
TWInitialScene ()
}
}
TWInitialScene :
public struct TWInitialScene : Scene {
public var body : some Scene {
WindowGroup {
TWInitialView ()
}
}
}
TWInitialView :
struct TWInitialView : View {
@Environment(\.scenePhase) private var scenePhase
var body : some View {
TWAppKitToSwiftUIBridge ()
}
}
TWAppKitToSwiftUIBridge :
struct TWNSKitToSwiftUIBridge : NSViewControllerRepresentable {
func makeNSViewController(context: Context) -> TWNSViewController {
let view_hierarchy : TWNSViewController
view_hierarchy = TWStaticContext.sViewController
return view_hierarchy
}
func updateNSViewController(_ nsViewController: TWNSViewController, context: Context) {
}
}
@objc
public class TWStaticContext : NSObject
{
public static let sViewController = TWNSViewController ()
public override init () {}
@objc
public static func GetViewController () -> TWNSViewController
{
return TWStaticContext.sViewController
}
}
public class TWNSViewController : NSViewController {
override public func viewDidLoad ()
{
super.viewDidLoad ()
}
}
To add content to the hirarchy we are accessing viewcontroller's intance and adding content to it like this :
public func PaintInitialScreen () {
let label = NSTextField(labelWithString: "TW window")
label.frame = NSRect(x: 100, y: 200, width: 200, height: 200)
// Adding content to viewcontroller
TWStaticContext.sViewController.view.addSubview(label)
}
We are using this approach because we have a contraint in our application that we have to update UI programatically and on compile time we dont know what we want to show . We will be adding content on runtime based on how many button we want, what label we want , where to place it etc.
When we were using purely appKit application, doing things programatically was simple but since SwiftUI is a declarative application we have to use above approach.
Rational for shifting to SwiftUI entry point is that we want our application to be future safe and since apple is more inclined to SwiffUI, we want to design our entry flow to use SwiftUI entry point . And SwiftUI being declarative, we are using appKit to add content to hiracrchy programtically.
We have used similar apprach in iOS also , where are using UIApplicationDelegateAdaptor inplace of NSApplicationAdaptor . And UIViewControllerReprestable in place of NSViewControllerRepresentable.
Is this right approach to use ?
I have a SwiftUI project which has the following hierarchy:
IOSSceneDelegate (App target) - depends on EntryPoint and Presentation static libs.
Presentation (Static library) - Depends on EntryPoint static lib. Contains UI related logic and updates the UI after querying the data layer.
EntryPoint (Static library) - Contains the entry point, AppDelegate (for its lifecycle aspects) etc.
I've only listed the relevant targets here.
SceneDelegate was initially present in EntryPoint library, because the AppDelegate references it when a scene is created.
public func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Set the SceneDelegate dynamically
let sceneConfig: UISceneConfiguration = UISceneConfiguration(name: "mainWindow", sessionRole: connectingSceneSession.role)
sceneConfig.delegateClass = SceneDelegate.self
return sceneConfig
}
The intent is to move the SceneDelegate to the Presentation library.
When moved, the EntryPoint library fails to compile because it's referencing the SceneDelegate (as shown above).
To remove this reference, I tried to set up the SceneDelegate in the old way - In the info.plist file, mention a SceneConfiguration and set the SceneDelegate in Presentation.
// In the Info.plist file
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>Presentation.SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
// In the AppDelegate
public func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Refer to a static UISceneconfiguration listed in the info.plist file
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
As shown above, the Presentation.SceneDelegate is referred in the Info.plist file and the reference is removed from the AppDelegate (in EntryPoint library).
The app target compiles, but when I run it, the SceneDelegate is not invoked. None of the methods from the SceneDelegate (scene(_:willConnectTo:options:), sceneDidDisconnect(_:), sceneDidEnterBackground(_:) etc.) are invoked. I only get the AppDelegate logs.
It seems like the Configuration is ignored because it was incorrect. Any thoughts? Is it possible to move the SceneDelegate in this situation?
Hello,
I'm writing an EntityAction that animates a material base tint between two different colours. However, the colour that is being actually set differs in RGB values from that requested.
For example, trying to set an end target of R0.5, G0.5, B0.5, results in a value of R0.735357, G0.735357, B0.735357. I can also see during the animation cycle that intermediate actual tint values are also incorrect, versus those being set.
My understanding is the the values of material base colour are passed as a SIMD4. Therefore I have a couple of helper extensions to convert a UIColor into this format and mix between two colours. Note however, I don't think the issue is with this functions - even if their outputs are wrong, the final value of the base tint doesn't match the value being set.
I wondered if this was a colour space issue?
import simd
import RealityKit
import UIKit
typealias Float4 = SIMD4<Float>
extension Float4 {
func mixedWith(_ value: Float4, by mix: Float) -> Float4 {
Float4(
simd_mix(x, value.x, mix),
simd_mix(y, value.y, mix),
simd_mix(z, value.z, mix),
simd_mix(w, value.w, mix)
)
}
}
extension UIColor {
var float4: Float4 {
var r: CGFloat = 0.0
var g: CGFloat = 0.0
var b: CGFloat = 0.0
var a: CGFloat = 0.0
getRed(&r, green: &g, blue: &b, alpha: &a)
return Float4(Float(r), Float(g), Float(b), Float(a))
}
}
struct ColourAction: EntityAction {
let startColour: SIMD4<Float>
let targetColour: SIMD4<Float>
var animatedValueType: (any AnimatableData.Type)? { SIMD4<Float>.self }
init(startColour: UIColor, targetColour: UIColor) {
self.startColour = startColour.float4
self.targetColour = targetColour.float4
}
static func registerEntityAction() {
ColourAction.subscribe(to: .updated) { event in
guard let animationState = event.animationState else { return }
let interpolatedColour = event.action.startColour.mixedWith(event.action.targetColour, by: Float(animationState.normalizedTime))
animationState.storeAnimatedValue(interpolatedColour)
}
}
}
extension Entity {
func updateColour(from currentColour: UIColor, to targetColour: UIColor, duration: Double, endAction: @escaping (Entity) -> Void = { _ in }) {
let colourAction = ColourAction(startColour: currentColour, targetColour: targetColour, endedAction: endAction)
if let colourAnimation = try? AnimationResource.makeActionAnimation(for: colourAction, duration: duration, bindTarget: .material(0).baseColorTint) {
playAnimation(colourAnimation)
}
}
}
The EntityAction can only be applied to an entity with a ModelComponent (because of the material), so it can be called like so:
guard
let modelComponent = entity.components[ModelComponent.self],
let material = modelComponent.materials.first as? PhysicallyBasedMaterial else
{
return
}
let currentColour = material.baseColor.tint
let targetColour = UIColor(_colorLiteralRed: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)
entity.updateColour(from:currentColour, to: targetColour, duration: 2)
I wanted to perform simulation in my application as a self tour guide for my user. For this I want to programatically simulate various user interaction events like button click, keypress event in the UITextField or moving the cursor around in the textField. These are only few examples to state, it can be any user interaction event or other events.
I wanted to know what is the apple recommendation on how should these simulations be performed? Is there something that apple offers like creating an event which can be directly executed for simulations. Is there some library available for this purpose?
Given Apple's new .limited contact authorization introduced in ios18, I want to be able to present the ContactAccessPicker directly from my app, via ionic capacitor. I present the .contactAccessPicker view via a UIHostingController, and I manage the view controller's dismissal accordingly when the ContactAccessPicker completes and is no longer presented.
Bug: After a few searches or interactions with the Contact Access Picker (ex. searching, selecting contacts, clicking the "show selected" button), the contact access picker crashes and the overlay remains. Any interaction with my app is then blocked because I can't detect that the contact access picker has disappeared when it crashes so I can't dismiss the viewController.
Is there a way for me to prevent the contact access picker from crashing, and how can I detect if it does crash, so I can at least dismiss the view controller if that happens?
struct ContactAccessPickerView: View {
@Binding var isPresented: Bool
let completion: @MainActor ([String]) -> Void
var body: some View {
Group {
if #available(iOS 18.0, *) {
Color.clear
.contactAccessPicker(isPresented: $isPresented) { result in
Task { @MainActor in
completion(result)
}
}
} else {
}
}
}
}
@objc func selectMoreContacts(_ call: CAPPluginCall) {
guard isContactsPermissionGranted() else {
call.resolve(["success": false])
return
}
// Save the call to ensure it's available until we finish
self.bridge?.saveCall(call)
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
var isPresented = true
let picker = ContactAccessPickerView(isPresented: .init(get: { isPresented }, set: { isPresented = $0 })) { contacts in
call.resolve(["success": true])
self.dismissAndCleanup(call)
}
let hostingController = UIHostingController(rootView: picker)
hostingController.modalPresentationStyle = .overFullScreen
self.bridge?.viewController?.present(hostingController, animated: true)
}
}
In our application we are using a pop over view and we have enabled the accessibility VoiceOver, When user navigating inside the popover and reached to the last element that time with the right swipe we need to dismiss the popover.
In our application we are using a Search bar in a pop over view and we have enabled Accessibility full keyboard access and we are using external keyboard. Now if the focus is on Searcher that time by next Tab key press Search bar will dismiss and focus needs to shift to the next UIElement.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
do {
let shapeLayer = CAShapeLayer()
shapeLayer.frame = CGRect(x: 50, y: 100, width: 200, height: 108)
let path = UIBezierPath(roundedRect: shapeLayer.bounds, cornerRadius: 36)
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.orange.cgColor
view.layer.addSublayer(shapeLayer)
}
do {
let layer = CALayer()
layer.backgroundColor = UIColor.blue.cgColor
layer.cornerRadius = 36
layer.frame = CGRect(x: 50, y: 300, width: 200, height: 108)
view.layer.addSublayer(layer)
}
}
}
The corner radius is set to 36 through CAShapeLayer, but the actual effect is larger than 36, close to half of the height.
Setting it through CALayer is fine
Can anyone explain it to me? Thank you
I'm having this problem, with this code:
let docPicker = UIDocumentPickerViewController(forOpeningContentTypes: [
.pdf
])
docPicker.delegate = self
docPicker.modalPresentationStyle = .currentContext
view.window?.rootViewController?.present(docPicker, animated: true, completion: nil)
but then when I open the simulator and click the button that calls to the method that has this code...
Cannot pick the pdf document.
Testing in browserstack with real devices is working, but it's a very slow process, why I cannot use simulators to make work this?
I found a memory leak in tvOS 17.4, but it's not happening in tvOS 18.0
here is the code flow
1.I have controller inside which I have tableView which in turn contains a collectionview
here I have passed self to tableViewcell as delegate and then from tableview cell I have passed self again as delegate to collectionViewcell, but memory is not released, because self is retained
"I have passed self as weak every where still memory leak is happening only in tvOS 17.4 and below versions. but in 18.0 and above versions it's fine"
IOS 12 - 18.1.1 - objective C, Xcode 16.0
App runs on both iPhone and iPad, this issue only occurs happens on iPads. For the iPhones I am able to get a decent numeric only keyboard to display.
I pulled down NumericKeypad from GitHub and used that a model on how to implement a custom keypad.
In the inputView of the delegate, a new custom text field is create and then assigned a delegate and other properties then it returns the view to the main ViewController. When the ViewControllers and the correct text field is entered my custom keyboard display and the buttons respond but nothing is displayed in the text field. This has worked for years and all of the sudden it stopped.
The original project for the example 10 key custom keyboard builds and when loaded that works on the iPad. If I comment out condition to only execute if running on an iPad and test with an iPhone the keyboards works.
It is only on a iPad that this happens.
This is the cod that creates creates the keyboard from a .xib file. I am using a storyboard for the main app.
#import "Numeric10KeyTextField.h"
#import "Numeric10KeyViewController.h"
@implementation Numeric10KeyTextField
(UIView *)inputView {
UIView *view = nil;
Numeric10KeyViewController *numVC;
// Add hook here for iPhone or other devices if needed but now return nil if iPhone so it uses the default
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
numVC = [[Numeric10KeyViewController alloc] initWithNibName:@"Numeric10Key" bundle:nil];
[numVC setActionSubviews:numVC.view];
numVC.delegate = self.numeric10KeyDelegate;
numVC.numpadTextField = self;
view = numVC.view;
// }
return view;
}
@end
Is it the default behavior that the standard back swipe (interactivePopGestureRecognizer) does not work when running a designed for iPhone app on an iPad?
To my knowledge, all apps behave this way.
Are there any workarounds?
I'm working on an old iOS app that started with objective-C + UIKit and has being migrated to Swift + SwiftUI. Currently its code is mostly Swift + SwiftUI but it has still some objective-C and some UIKit ViewControllers.
One of the SwiftUI views uses fileImporter to open Files App and select a file from the device. This has been working well until iOS 18 is launched. With iOS 18 the file picker is not launching correctly and is frozen in every simulator (the unique real device I've could test with iOS 18 seemed to work correctly).
I managed to clone my project and leave it with the minimal amount of files to reproduce this error. This is the code:
AppDelegate.h
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate> {}
@property (strong, nonatomic) UIWindow *window;
@end
AppDelegate.m
#import "AppDelegate.h"
#import "MyApp-Swift.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
FirstViewBuilder *viewBuilder = [[FirstViewBuilder alloc] init];
[viewBuilder show];
return YES;
}
@end
FirstViewBuilder.swift
import SwiftUI
@objc class FirstViewBuilder: NSObject {
private var view: UIHostingController<FirstView>
@objc override init() {
self.view = MyHostingController(rootView: FirstView())
}
@objc func show() {
let app = UIApplication.shared.delegate as? AppDelegate
let window = app?.window
window?.backgroundColor = .white
// Use navigationController or view directly depending on use
window?.rootViewController = view
}
}
FirstView.swift
import SwiftUI
struct FirstView: View {
@State var hasToOpenFilesApp = false
var body: some View {
VStack(alignment: .leading, spacing: 0) {
Button("Open Files app") {
hasToOpenFilesApp = true
}.fileImporter(isPresented: $hasToOpenFilesApp, allowedContentTypes: [.text]) { result in
switch result {
case .success(let url):
print(url.debugDescription)
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
}
And finally, MyHostingController
import SwiftUI
class MyHostingController<Content>: UIHostingController<Content> where Content: View {
override init(rootView: Content) {
super.init(rootView: rootView)
}
@objc required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.hidesBackButton = true
}
}
Launching this in an iPhone 13 Pro (18.2) simulator I click on Open Files App, it takes 2 seconds to open it, and it opens full screen (not like a modal). Buttons on the top are behind the status bar and buttons at the bottom are behind the Home indicator. But it's worse because the user can't interact with this view, it's frozen.
I created a fresh SwiftUI project just with this unique view and the fileimport worked as expected so I thought the problem was due to embed the SwiftUI view inside the UIHostingController. So I made these modifications to the minimal project:
Remove the files AppDelegate, FirstViewBuilder and MyHostingController.
Create this SwiftUI App file
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
FirstView()
}
}
}
And again the same problem with iOS 18.
But if I launch this exact project in an iPhone 13 Pro (17.4) simulator and open the files apps (now it opens almost instantly) it works OK and shows the file picker as a modal, as expected, and I can interact with it and select files.
Last thing I've tried is removing LaunchScreen.xib from my project and Launch screen interface file base name key from my info.plist but the problem keeps happening.
I guess it must be due to my project configuration (too old) but I have no more ideas of where to look at.
The possibility of having a fresh SwiftUI project and "move" the old project to the new one could take me several weeks and I discard it by the moment.
Could I use another method to select files from SwiftUI views with iOS 18?
Below is my sample code. On the Home page, when I click "show sheet," the sheet page expands, and the StateObject inside the sheet is initialized once. However, when I click "show Fullscreen" and then click "show sheet" inside the fullscreen page, the sheet gets initialized twice.
However, if I remove navigationDestination, this issue does not occur.
This problem causes the network request in the sheet page to be triggered multiple times.
Can someone tell me the reason?
enum TestRouter: String, Hashable {
case test
var targetView: some View {
Text("test")
}
var title: String {
return "test title"
}
}
@MainActor
struct NavigationInnerView<Content>: View where Content: View {
var contentView: () -> Content
@MainActor public init(@ViewBuilder contentView: @escaping () -> Content) {
self.contentView = contentView
}
var body: some View {
NavigationStack() {
contentView()
.navigationDestination(for: TestRouter.self) { route in
route.targetView
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct ContentView: View {
@State var showFullScreen: Bool = false
@State var showSheet: Bool = false
var contentView: some View {
VStack {
VStack {
Text("Home")
Button {
showFullScreen = true
} label: {
Text("show fullscreen")
}
Button {
showSheet = true
} label: {
Text("show sheet ")
}
}
}
}
var body: some View {
NavigationInnerView {
contentView
.fullScreenCover(isPresented: $showFullScreen) {
NavigationInnerView {
FullScreenContentView()
}
}
.sheet(isPresented: $showSheet) {
NavigationInnerView {
SheetContentView()
}
}
}
}
}
class FullScreenViewModel: ObservableObject {
@Published var content: Bool = false
init() {
print("Full Screen ViewModel init")
}
}
struct FullScreenContentView: View {
@Environment(\.dismiss) var dismiss
@State var showSheet: Bool = false
@StateObject var viewModel: FullScreenViewModel = .init()
init() {
print("Full screen view init")
}
var body: some View {
VStack {
Text("FullScreen")
Button {
dismiss()
}label: {
Text("dismiss")
}
Button {
showSheet = true
} label: {
Text("show sheet")
}
}
.sheet(isPresented: $showSheet) {
NavigationInnerView {
SheetContentView()
}
}
}
}
class SheetViewModel: ObservableObject {
@Published var content: Bool = false
init() {
print("SheetViewModel init")
}
}
struct SheetContentView: View {
@Environment(\.dismiss) var dismiss
@StateObject var viewModel = SheetViewModel()
init() {
print("sheet view init")
}
var body: some View {
Text("Sheet")
Button {
dismiss()
} label: {
Text("dismiss")
}
}
}
#Preview {
ContentView()
}
I have a setup:
Collection view with compositional layout
a self sizing cell inside
a subview inside the cell
and unrelated view outside the collection view
I would like to:
modify the layout (constraints) of the cell inside the collection view with UIView.animate
trigger an animated layout update of collection view
synchronize the position of an unrelated view to the position of one of the subviews of a collection view cell
What I tried:
UIView.animate(withDuration: 0.25) {
cellViewReference.updateState(state: state, animated: false)
collectionView.collectionViewLayout.invalidateLayout()
collectionView.layoutIfNeeded()
someOtherViewOutsideCollectionView.center = cellViewReference.getPositionOfThatOneViewInWindowCoordinateSystem()
}
What I'm expecting:
after invalidateLayout, the layout update of the collection view is merely scheduled, but not yet performed
layoutIfNeeded forces an update on the collectionViewLayout + update on the frames of the views inside the UICollectionViewCells
all the frames become correct to what they will look like after the animation is performed
I call getPositionOfThatOneViewInWindowCoordinateSystem and it gives me the position of the view after the uicollectionview AND the cell's layout has updated
What happens instead:
getPositionOfThatOneViewInWindowCoordinateSystem returns me an old value
I am observing that the bounds of the cell didn't actually change during layoutIfNeeded
And moreover, the bounds change without animation, instantly
Question:
how to animate self sizing cell size change due relayout
how to synchronize outside views with collection views
Modern collection views use UICollectionViewDiffableDataSource with UICollectionView.CellRegistration and UICollectionView.dequeueConfiguredReusableCell(registration:indexPath:item).
There are runtime crashes when passing nil as argument for the item parameter. There's no clear documentation on whether optional items are allowed or not.
The function signature in Swift is:
@MainActor @preconcurrency func dequeueConfiguredReusableCell<Cell, Item>(using registration: UICollectionView.CellRegistration<Cell, Item>, for indexPath: IndexPath, item: Item?) -> Cell where Cell : UICollectionViewCell
Given the Item? type one would assume Optionals are allowed.
In Objective-C the signature is:
- (__kindof UICollectionViewCell *)dequeueConfiguredReusableCellWithRegistration:(UICollectionViewCellRegistration *)registration forIndexPath:(NSIndexPath *)indexPath item:(id)item;
I'm not sure, if there's implicit nullability bridging to the Swift API or if the Objective-C files has some explicit nullability annotation.
The crash is due to a swift_dynamicCast failing with:
Could not cast value of type '__SwiftNull' (0x10b1c4dd0) to 'Item' (0x10d6086e0).
It's possible to workaround this by making a custom Optional type like
enum MyOptional<T> {
case nothing
case something(T)
}
and then wrapping and unwrapping Item? to MyOptional<Item>. But this feels like unnecessary boilerplate.
With the current situation it's easy to ship an app where everything seems to work, but in production only certain edge cases cause nil values being used and then crashing the app.
Please clarify the allowed arguments / types for the dequeueConfiguredReusableCell function.
Either Optionals should be supported and not crash at runtime or the signatures should be changed so there's a compile time error, when trying to use an Item?.
Feedback: FB16494078
There are two issues about SFSafariViewController.
After rotate from landscape to portrait,
The topAnchor is destroyed.
The specified bar tint color and control tint color are invalidated.(Returns to system color)
Regarding the second issue, I’ve found a temporary workaround.
Override the viewWillTransition(to:with:) and keep it empty. Don't call super.viewWillTransition(to:with:).
Since UIKit is not open source, I don’t know the exact cause, but I found something that could be the key to the issue. So, I reported it to Apple Feedback Assistant. You can check the details and the sample project in the GitHub repository below.
https://github.com/ueunli/SafariViewer
So the battery level value is in accurate returns the battery percentage in multiple of 5 values e.g. battery percentage is 42 but the api returns it as 40. So please fix the issue if possible because i checked that the
devices running iOS versions below 17 appear to be working fine.