Popular New Releases in Uikit
mdb-ui-kit
Hero
1.6.1 - Bug fixes and cleaner Swift PM
vant
v4.0.0-alpha.2
Eureka
5.3.5
ios-oss
5.1.0
Popular Libraries in Uikit
by vsouza swift
37808 MIT
A curated list of awesome iOS ecosystem, including Objective-C and Swift Projects
by mdbootstrap javascript
21896 NOASSERTION
Bootstrap 5 & Material Design 2.0 UI KIT
by HeroTransitions swift
20722 MIT
Elegant transition library for iOS & tvOS
by youzan typescript
19479 MIT
Lightweight Mobile UI Components built on Vue
by designmodo javascript
14862 MIT
Flat UI Free - Design Framework (html/css3/less/js). Flat UI is based on Bootstrap, a comfortable, responsive, and functional framework that simplifies the development of websites.
by CosmicMind swift
11774 MIT
A UI/UX framework for creating beautiful applications.
by xmartlabs swift
11392 MIT
Elegant iOS form builder in Swift
by Ramotion swift
10851 MIT
:octocat: RAMAnimatedTabBarController is a Swift UI module library for adding animation to iOS tabbar items and icons. iOS library made by @Ramotion
by IBAnimatable swift
8600 MIT
Design and prototype customized UI, interaction, navigation, transition and animation for App Store ready Apps in Interface Builder with IBAnimatable.
Trending New libraries in Uikit
by jordansinger swift
1687 MIT
A SwiftUI system components and interactions demo app
by hauptrolle typescript
781 MIT
A growing collection of responsive Chakra UI Templates ready to drop into your React project.
by themesberg html
671 MIT
Neumorphism inspired UI Kit: web components, sections and pages in neumorphic style built with Bootstrap CSS Framework
by dasautoooo swift
655 MIT
A SwiftUI view for displaying Markdown with customizable appearances.
by microsoft swift
641 MIT
UIKit and AppKit controls for building native Microsoft experiences
by jphong1111 swift
545 MIT
List of awesome iOS & Swift stuff!!
by bannzai swift
473 MIT
Gedatsu provide readable format about AutoLayout error console log
by AvdLee swift
451 MIT
Easily use UIKit views in your SwiftUI applications. Create Xcode Previews for UIView elements
by niw swift
429 MIT
A modern, real iOS keyboard system notifications handler framework that Just Works.
Top Authors in Uikit
1
11 Libraries
33659
2
8 Libraries
1186
3
8 Libraries
10688
4
8 Libraries
25419
5
8 Libraries
199
6
6 Libraries
42
7
6 Libraries
149
8
6 Libraries
210
9
5 Libraries
16
10
5 Libraries
125
1
11 Libraries
33659
2
8 Libraries
1186
3
8 Libraries
10688
4
8 Libraries
25419
5
8 Libraries
199
6
6 Libraries
42
7
6 Libraries
149
8
6 Libraries
210
9
5 Libraries
16
10
5 Libraries
125
Trending Kits in Uikit
No Trending Kits are available at this moment for Uikit
Trending Discussions on Uikit
Xcode 13.3 warning: "'self' refers to the method '{object}.self', which may be unexpected
Flutter on iOS: redefinition of module 'Firebase'
PHPickerViewController tapping on Search gets error... "Unable to load photos"
NPM CI error bindings not accessible from watchpack-chokidar2:fsevents
ScrollViewReader scrollTo with .center anchor bug?
Subclassing UIView from Kotlin Native
UIImageView Is Blending Colors With No Alpha
additionalSafeAreaInsets is not accounted for during view controller dismissal, using custom UIViewControllerTransitioningDelegate
Method 'UIKit.UIApplication.Main' is obsolete: Use the overload with 'Type' instead of 'String' parameters for type safety
Document browser app in iOS 15 keeps creating iCloud drive folder
QUESTION
Xcode 13.3 warning: "'self' refers to the method '{object}.self', which may be unexpected
Asked 2022-Mar-22 at 10:04I just updated to Xcode 13.3 and I'm seeing several instances of a new warning that I've not seen with previous versions of Xcode. As an example, I have a simple table view cell named LabelAndSwitchTableViewCell that looks like this:
1import UIKit
2
3class LabelAndSwitchTableViewCell: UITableViewCell {
4
5 private let label: UILabel = {
6 let label = UILabel()
7 label.translatesAutoresizingMaskIntoConstraints = false
8 return label
9 }()
10
11 private let _switch: UISwitch = {
12 let _switch = UISwitch()
13 _switch.translatesAutoresizingMaskIntoConstraints = false
14 _switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
15 return _switch
16 }()
17
18 override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
19 super.init(style: style, reuseIdentifier: reuseIdentifier)
20
21 contentView.addSubview(label)
22 contentView.addSubview(_switch)
23
24 // layout constraints removed for brevity
25 }
26
27 required init?(coder: NSCoder) {
28 fatalError("init(coder:) has not been implemented")
29 }
30
31 @objc private func didToggleSwitch() {
32 print("Switch was toggled...")
33 }
34}
35
As you can see, I'm adding a target to the switch that I want to be called when the value of the switches changes:
1import UIKit
2
3class LabelAndSwitchTableViewCell: UITableViewCell {
4
5 private let label: UILabel = {
6 let label = UILabel()
7 label.translatesAutoresizingMaskIntoConstraints = false
8 return label
9 }()
10
11 private let _switch: UISwitch = {
12 let _switch = UISwitch()
13 _switch.translatesAutoresizingMaskIntoConstraints = false
14 _switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
15 return _switch
16 }()
17
18 override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
19 super.init(style: style, reuseIdentifier: reuseIdentifier)
20
21 contentView.addSubview(label)
22 contentView.addSubview(_switch)
23
24 // layout constraints removed for brevity
25 }
26
27 required init?(coder: NSCoder) {
28 fatalError("init(coder:) has not been implemented")
29 }
30
31 @objc private func didToggleSwitch() {
32 print("Switch was toggled...")
33 }
34}
35_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
36
After updating to Xcode 13.3, I'm now seeing a new warning on this line:
1import UIKit
2
3class LabelAndSwitchTableViewCell: UITableViewCell {
4
5 private let label: UILabel = {
6 let label = UILabel()
7 label.translatesAutoresizingMaskIntoConstraints = false
8 return label
9 }()
10
11 private let _switch: UISwitch = {
12 let _switch = UISwitch()
13 _switch.translatesAutoresizingMaskIntoConstraints = false
14 _switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
15 return _switch
16 }()
17
18 override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
19 super.init(style: style, reuseIdentifier: reuseIdentifier)
20
21 contentView.addSubview(label)
22 contentView.addSubview(_switch)
23
24 // layout constraints removed for brevity
25 }
26
27 required init?(coder: NSCoder) {
28 fatalError("init(coder:) has not been implemented")
29 }
30
31 @objc private func didToggleSwitch() {
32 print("Switch was toggled...")
33 }
34}
35_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
36'self' refers to the method 'LabelAndSwitchTableViewCell.self', which may be unexpected
37
Xcode's suggestion to silence this warning is to replace:
1import UIKit
2
3class LabelAndSwitchTableViewCell: UITableViewCell {
4
5 private let label: UILabel = {
6 let label = UILabel()
7 label.translatesAutoresizingMaskIntoConstraints = false
8 return label
9 }()
10
11 private let _switch: UISwitch = {
12 let _switch = UISwitch()
13 _switch.translatesAutoresizingMaskIntoConstraints = false
14 _switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
15 return _switch
16 }()
17
18 override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
19 super.init(style: style, reuseIdentifier: reuseIdentifier)
20
21 contentView.addSubview(label)
22 contentView.addSubview(_switch)
23
24 // layout constraints removed for brevity
25 }
26
27 required init?(coder: NSCoder) {
28 fatalError("init(coder:) has not been implemented")
29 }
30
31 @objc private func didToggleSwitch() {
32 print("Switch was toggled...")
33 }
34}
35_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
36'self' refers to the method 'LabelAndSwitchTableViewCell.self', which may be unexpected
37_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
38
...with...
1import UIKit
2
3class LabelAndSwitchTableViewCell: UITableViewCell {
4
5 private let label: UILabel = {
6 let label = UILabel()
7 label.translatesAutoresizingMaskIntoConstraints = false
8 return label
9 }()
10
11 private let _switch: UISwitch = {
12 let _switch = UISwitch()
13 _switch.translatesAutoresizingMaskIntoConstraints = false
14 _switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
15 return _switch
16 }()
17
18 override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
19 super.init(style: style, reuseIdentifier: reuseIdentifier)
20
21 contentView.addSubview(label)
22 contentView.addSubview(_switch)
23
24 // layout constraints removed for brevity
25 }
26
27 required init?(coder: NSCoder) {
28 fatalError("init(coder:) has not been implemented")
29 }
30
31 @objc private func didToggleSwitch() {
32 print("Switch was toggled...")
33 }
34}
35_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
36'self' refers to the method 'LabelAndSwitchTableViewCell.self', which may be unexpected
37_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
38_switch.addTarget(LabelAndSwitchTableViewCell.self, action: #selector(didToggleSwitch), for: .valueChanged)
39
Making this change does silence the warning but it also causes the app to crash (unrecognized selector) when I toggle the switch. Here's the dump from that crash:
1import UIKit
2
3class LabelAndSwitchTableViewCell: UITableViewCell {
4
5 private let label: UILabel = {
6 let label = UILabel()
7 label.translatesAutoresizingMaskIntoConstraints = false
8 return label
9 }()
10
11 private let _switch: UISwitch = {
12 let _switch = UISwitch()
13 _switch.translatesAutoresizingMaskIntoConstraints = false
14 _switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
15 return _switch
16 }()
17
18 override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
19 super.init(style: style, reuseIdentifier: reuseIdentifier)
20
21 contentView.addSubview(label)
22 contentView.addSubview(_switch)
23
24 // layout constraints removed for brevity
25 }
26
27 required init?(coder: NSCoder) {
28 fatalError("init(coder:) has not been implemented")
29 }
30
31 @objc private func didToggleSwitch() {
32 print("Switch was toggled...")
33 }
34}
35_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
36'self' refers to the method 'LabelAndSwitchTableViewCell.self', which may be unexpected
37_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
38_switch.addTarget(LabelAndSwitchTableViewCell.self, action: #selector(didToggleSwitch), for: .valueChanged)
39[app_mockup.LabelAndSwitchTableViewCell didToggleSwitch]: unrecognized selector sent to class 0x1043d86e8
40*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[app_mockup.LabelAndSwitchTableViewCell didToggleSwitch]: unrecognized selector sent to class 0x1043d86e8'
41
Making the didToggleSwitch()
method static
will prevent the crash but I'm not sure why I'd want to do that. I can obviously revert the change (from LabelAndSwitchTableViewCell.self
back to just self
) but I'm wondering if there's something else that I should be doing to address this?
ANSWER
Answered 2022-Mar-21 at 16:34You can fix by changing the lets to lazy var's
1import UIKit
2
3class LabelAndSwitchTableViewCell: UITableViewCell {
4
5 private let label: UILabel = {
6 let label = UILabel()
7 label.translatesAutoresizingMaskIntoConstraints = false
8 return label
9 }()
10
11 private let _switch: UISwitch = {
12 let _switch = UISwitch()
13 _switch.translatesAutoresizingMaskIntoConstraints = false
14 _switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
15 return _switch
16 }()
17
18 override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
19 super.init(style: style, reuseIdentifier: reuseIdentifier)
20
21 contentView.addSubview(label)
22 contentView.addSubview(_switch)
23
24 // layout constraints removed for brevity
25 }
26
27 required init?(coder: NSCoder) {
28 fatalError("init(coder:) has not been implemented")
29 }
30
31 @objc private func didToggleSwitch() {
32 print("Switch was toggled...")
33 }
34}
35_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
36'self' refers to the method 'LabelAndSwitchTableViewCell.self', which may be unexpected
37_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
38_switch.addTarget(LabelAndSwitchTableViewCell.self, action: #selector(didToggleSwitch), for: .valueChanged)
39[app_mockup.LabelAndSwitchTableViewCell didToggleSwitch]: unrecognized selector sent to class 0x1043d86e8
40*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[app_mockup.LabelAndSwitchTableViewCell didToggleSwitch]: unrecognized selector sent to class 0x1043d86e8'
41private lazy var _switch2: UISwitch = {
42 let _switch = UISwitch()
43 _switch.translatesAutoresizingMaskIntoConstraints = false
44 _switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
45 return _switch
46}()
47
The Xcode fix-it suggestion is just wrong.
QUESTION
Flutter on iOS: redefinition of module 'Firebase'
Asked 2022-Mar-11 at 10:40I've been trying to build my flutter app on iOS but flutter run
is throwing the following error:
1/Users/<MyUser>/Desktop/projects/app/ios/Pods/Firebase/CoreOnly/Source
2s/module.modulemap:1:8: error: redefinition of module 'Firebase'
3module Firebase {
4 ^
5/Users/<MyUser>/Library/Developer/Xcode/DerivedData/Runner-dbkgurnsasbvieahfnk
6dontejqss/SourcePackages/checkouts/firebase-ios-sdk/CoreOnly/Sources/module.
7modulemap:1:8: note: previously defined here
8module Firebase {
9
I've imported the firebase-ios-sdk as per the instructions here. The imported modules are FirebaseCore, FirebaseAuth and FirebaseMessaging. I have not made any modifications to iOS-specific code (anything under /ios) apart from importing Firebase in the AppDelegate.swift file. The updated file now contains the following code:
1/Users/<MyUser>/Desktop/projects/app/ios/Pods/Firebase/CoreOnly/Source
2s/module.modulemap:1:8: error: redefinition of module 'Firebase'
3module Firebase {
4 ^
5/Users/<MyUser>/Library/Developer/Xcode/DerivedData/Runner-dbkgurnsasbvieahfnk
6dontejqss/SourcePackages/checkouts/firebase-ios-sdk/CoreOnly/Sources/module.
7modulemap:1:8: note: previously defined here
8module Firebase {
9import UIKit
10import Flutter
11import Firebase
12
13@UIApplicationMain
14@objc class AppDelegate: FlutterAppDelegate {
15 override func application(
16 _ application: UIApplication,
17 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
18 ) -> Bool {
19 FirebaseApp.configure()
20 GeneratedPluginRegistrant.register(with: self)
21 return super.application(application, didFinishLaunchingWithOptions: launchOptions)
22 }
23}
24
After searching the error I've tried the following steps:
flutter clean
pod deintegrate
andpod install
- Cleaning DerivedData (both through XCode and manually)
- Remove and re-add firebase-ios-sdk
But to no avail.
ANSWER
Answered 2022-Jan-20 at 08:43If you use M1 try this
arch -x86_64 pod install
QUESTION
PHPickerViewController tapping on Search gets error... "Unable to load photos"
Asked 2022-Feb-10 at 17:27I'm trying to implement a PHPickerViewController
using SwiftUI and The Composable Architecture. (Not that I think that's particularly relevant but it might explain why some of my code is like it is).
I've been playing around with this to try and work it out. I created a little sample Project on GitHub which removes The Composable Architecture and keeps the UI super simple.
https://github.com/oliverfoggin/BrokenImagePickers/tree/main
It looks like iOS 15 is breaking on both the UIImagePickerViewController and the PHPickerViewController. (Which makes sense as they both use the same UI under the hood).
I guess the nest step is to determine if the same error occurs when using them in a UIKit app.
My codeMy code is fairly straight forward. It's pretty much just a reimplementation of the same feature that uses UIImagePickerViewController
but I wanted to try with the newer APIs.
My code looks like this...
1public struct ImagePicker: UIViewControllerRepresentable {
2
3// Vars and setup stuff...
4 @Environment(\.presentationMode) var presentationMode
5
6 let viewStore: ViewStore<ImagePickerState, ImagePickerAction>
7
8 public init(store: Store<ImagePickerState, ImagePickerAction>) {
9 self.viewStore = ViewStore(store)
10 }
11
12// UIViewControllerRepresentable required functions
13 public func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> some UIViewController {
14
15 // Configuring the PHPickerViewController
16 var config = PHPickerConfiguration()
17 config.filter = PHPickerFilter.images
18
19 let picker = PHPickerViewController(configuration: config)
20 picker.delegate = context.coordinator
21 return picker
22 }
23
24 public func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
25
26 public func makeCoordinator() -> Coordinator {
27 Coordinator(self)
28 }
29
30// This is the coordinator that acts as the delegate
31 public class Coordinator: PHPickerViewControllerDelegate {
32 let parent: ImagePicker
33
34 init(_ parent: ImagePicker) {
35 self.parent = parent
36 }
37
38 public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
39 picker.dismiss(animated: true)
40
41 guard let itemProvider = results.first?.itemProvider,
42 itemProvider.canLoadObject(ofClass: UIImage.self) else {
43 return
44 }
45
46 itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
47 if let image = image as? UIImage {
48 DispatchQueue.main.async {
49 self?.parent.viewStore.send(.imagePicked(image: image))
50 }
51 }
52 }
53 }
54 }
55}
56
I can present the ImagePicker
view and select a photo and it's all fine. I can cancel out of it ok. I can even scroll down the huge collection view of images that I have. I can even see the new image appear in my state object and display it within my app. (Note... this is still WIP and so the code is a bit clunky but that's only to get it working initially).
The problem is that when I tap on the search bar in the PHPickerView
(which is a search bar provided by Apple in the control, I didn't create it or code it). It seems to start to slide up the keyboard and then the view goes blank with a single message in the middle...
Unable to Load Photos
[Try Again]
I also get a strange looking error log. (I removed the time stamps to shorten the lines).
1public struct ImagePicker: UIViewControllerRepresentable {
2
3// Vars and setup stuff...
4 @Environment(\.presentationMode) var presentationMode
5
6 let viewStore: ViewStore<ImagePickerState, ImagePickerAction>
7
8 public init(store: Store<ImagePickerState, ImagePickerAction>) {
9 self.viewStore = ViewStore(store)
10 }
11
12// UIViewControllerRepresentable required functions
13 public func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> some UIViewController {
14
15 // Configuring the PHPickerViewController
16 var config = PHPickerConfiguration()
17 config.filter = PHPickerFilter.images
18
19 let picker = PHPickerViewController(configuration: config)
20 picker.delegate = context.coordinator
21 return picker
22 }
23
24 public func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
25
26 public func makeCoordinator() -> Coordinator {
27 Coordinator(self)
28 }
29
30// This is the coordinator that acts as the delegate
31 public class Coordinator: PHPickerViewControllerDelegate {
32 let parent: ImagePicker
33
34 init(_ parent: ImagePicker) {
35 self.parent = parent
36 }
37
38 public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
39 picker.dismiss(animated: true)
40
41 guard let itemProvider = results.first?.itemProvider,
42 itemProvider.canLoadObject(ofClass: UIImage.self) else {
43 return
44 }
45
46 itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
47 if let image = image as? UIImage {
48 DispatchQueue.main.async {
49 self?.parent.viewStore.send(.imagePicked(image: image))
50 }
51 }
52 }
53 }
54 }
55}
56// These happen on immediately presenting the ImagePicker
57AppName[587:30596] [Picker] Showing picker unavailable UI (reason: still loading) with error: (null)
58AppName[587:30596] Writing analzed variants.
59
60
61// These happen when tapping the search bar
62AppName[587:30867] [lifecycle] [u A95D90FC-C77B-43CC-8FC6-C8E7C81DD22A:m (null)] [com.apple.mobileslideshow.photospicker(1.0)] Connection to plugin interrupted while in use.
63AppName[587:31002] [lifecycle] [u A95D90FC-C77B-43CC-8FC6-C8E7C81DD22A:m (null)] [com.apple.mobileslideshow.photospicker(1.0)] Connection to plugin invalidated while in use.
64AppName[587:30596] [Picker] Showing picker unavailable UI (reason: crashed) with error: (null)
65AppName[587:30596] viewServiceDidTerminateWithError:: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted}
66
Tapping the "Try Again" button reloads the initial scroll screen and I can carry on using it. But tapping the search bar again just shows the same error.
I'm usually the first one to point out that the error is almost definitely not with the Apple APIs but I'm stumped on this one. I'm not sure what it is that I'm doing that is causing this to happen?
Is it the fact that it's in a SwiftUI view?
Recreated the project in UIKitI remade the same project using UIKit... https://github.com/oliverfoggin/UIKit-Image-Pickers
And I couldn't replicate the crash at all.
Also... if you are taking any sort of screen recording of the device the crash will not happen. I tried taking a recording on the device itself and couldn't replicate it. I also tried doing a movie recording from my Mac using the iPhone screen and couldn't replicate the crash. But... the instant I stopped the recording on QuickTime the crash was replicable again.
ANSWER
Answered 2021-Sep-26 at 14:32Well.. this seems to be an iOS bug.
I have cerated a sample project here that shows the bug... https://github.com/oliverfoggin/BrokenImagePickers
And a replica project here written with UIKit that does not... https://github.com/oliverfoggin/UIKit-Image-Pickers
I tried to take a screen recording of this happening but it appears that if any screen recording is happening (whether on device or via QuickTime on the Mac) this suppresses the bug from happening.
I have filed a radar with Apple and sent them both projects to have a look at and LOTS of detail around what's happening. I'll keep this updated with any progress on that.
Hacky workaroundAfter a bit of further investigation I found that you can start with SwiftUI and then present a PHPickerViewController without this crash happening.
From SwiftUI if you present a UIViewControllerRepresentable... and then from there if you present the PHPickerViewController it will not crash.
So I came up with a (very tacky) workaround that avoids this crash.
I first create a UIViewController
subclass that I use like a wrapper.
1public struct ImagePicker: UIViewControllerRepresentable {
2
3// Vars and setup stuff...
4 @Environment(\.presentationMode) var presentationMode
5
6 let viewStore: ViewStore<ImagePickerState, ImagePickerAction>
7
8 public init(store: Store<ImagePickerState, ImagePickerAction>) {
9 self.viewStore = ViewStore(store)
10 }
11
12// UIViewControllerRepresentable required functions
13 public func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> some UIViewController {
14
15 // Configuring the PHPickerViewController
16 var config = PHPickerConfiguration()
17 config.filter = PHPickerFilter.images
18
19 let picker = PHPickerViewController(configuration: config)
20 picker.delegate = context.coordinator
21 return picker
22 }
23
24 public func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
25
26 public func makeCoordinator() -> Coordinator {
27 Coordinator(self)
28 }
29
30// This is the coordinator that acts as the delegate
31 public class Coordinator: PHPickerViewControllerDelegate {
32 let parent: ImagePicker
33
34 init(_ parent: ImagePicker) {
35 self.parent = parent
36 }
37
38 public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
39 picker.dismiss(animated: true)
40
41 guard let itemProvider = results.first?.itemProvider,
42 itemProvider.canLoadObject(ofClass: UIImage.self) else {
43 return
44 }
45
46 itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
47 if let image = image as? UIImage {
48 DispatchQueue.main.async {
49 self?.parent.viewStore.send(.imagePicked(image: image))
50 }
51 }
52 }
53 }
54 }
55}
56// These happen on immediately presenting the ImagePicker
57AppName[587:30596] [Picker] Showing picker unavailable UI (reason: still loading) with error: (null)
58AppName[587:30596] Writing analzed variants.
59
60
61// These happen when tapping the search bar
62AppName[587:30867] [lifecycle] [u A95D90FC-C77B-43CC-8FC6-C8E7C81DD22A:m (null)] [com.apple.mobileslideshow.photospicker(1.0)] Connection to plugin interrupted while in use.
63AppName[587:31002] [lifecycle] [u A95D90FC-C77B-43CC-8FC6-C8E7C81DD22A:m (null)] [com.apple.mobileslideshow.photospicker(1.0)] Connection to plugin invalidated while in use.
64AppName[587:30596] [Picker] Showing picker unavailable UI (reason: crashed) with error: (null)
65AppName[587:30596] viewServiceDidTerminateWithError:: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted}
66class WrappedPhotoPicker: UIViewController {
67 var picker: PHPickerViewController?
68
69 override func viewDidLoad() {
70 super.viewDidLoad()
71
72 if let picker = picker {
73 present(picker, animated: false)
74 }
75 }
76}
77
Then in the SwiftUI View I create this wrapper and set the picker in it.
1public struct ImagePicker: UIViewControllerRepresentable {
2
3// Vars and setup stuff...
4 @Environment(\.presentationMode) var presentationMode
5
6 let viewStore: ViewStore<ImagePickerState, ImagePickerAction>
7
8 public init(store: Store<ImagePickerState, ImagePickerAction>) {
9 self.viewStore = ViewStore(store)
10 }
11
12// UIViewControllerRepresentable required functions
13 public func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> some UIViewController {
14
15 // Configuring the PHPickerViewController
16 var config = PHPickerConfiguration()
17 config.filter = PHPickerFilter.images
18
19 let picker = PHPickerViewController(configuration: config)
20 picker.delegate = context.coordinator
21 return picker
22 }
23
24 public func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
25
26 public func makeCoordinator() -> Coordinator {
27 Coordinator(self)
28 }
29
30// This is the coordinator that acts as the delegate
31 public class Coordinator: PHPickerViewControllerDelegate {
32 let parent: ImagePicker
33
34 init(_ parent: ImagePicker) {
35 self.parent = parent
36 }
37
38 public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
39 picker.dismiss(animated: true)
40
41 guard let itemProvider = results.first?.itemProvider,
42 itemProvider.canLoadObject(ofClass: UIImage.self) else {
43 return
44 }
45
46 itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
47 if let image = image as? UIImage {
48 DispatchQueue.main.async {
49 self?.parent.viewStore.send(.imagePicked(image: image))
50 }
51 }
52 }
53 }
54 }
55}
56// These happen on immediately presenting the ImagePicker
57AppName[587:30596] [Picker] Showing picker unavailable UI (reason: still loading) with error: (null)
58AppName[587:30596] Writing analzed variants.
59
60
61// These happen when tapping the search bar
62AppName[587:30867] [lifecycle] [u A95D90FC-C77B-43CC-8FC6-C8E7C81DD22A:m (null)] [com.apple.mobileslideshow.photospicker(1.0)] Connection to plugin interrupted while in use.
63AppName[587:31002] [lifecycle] [u A95D90FC-C77B-43CC-8FC6-C8E7C81DD22A:m (null)] [com.apple.mobileslideshow.photospicker(1.0)] Connection to plugin invalidated while in use.
64AppName[587:30596] [Picker] Showing picker unavailable UI (reason: crashed) with error: (null)
65AppName[587:30596] viewServiceDidTerminateWithError:: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted}
66class WrappedPhotoPicker: UIViewController {
67 var picker: PHPickerViewController?
68
69 override func viewDidLoad() {
70 super.viewDidLoad()
71
72 if let picker = picker {
73 present(picker, animated: false)
74 }
75 }
76}
77struct WrappedPickerView: UIViewControllerRepresentable {
78 @Environment(\.presentationMode) var presentationMode
79 @Binding var photoPickerResult: PHPickerResult?
80
81 let wrappedPicker = WrappedPhotoPicker()
82
83 func makeUIViewController(context: Context) -> WrappedPhotoPicker {
84 var config = PHPickerConfiguration()
85 config.filter = .images
86 config.selectionLimit = 1
87
88 let picker = PHPickerViewController(configuration: config)
89 picker.delegate = context.coordinator
90
91 wrappedPicker.picker = picker
92 return wrappedPicker
93 }
94
95 func updateUIViewController(_ uiViewController: WrappedPhotoPicker, context: Context) {}
96
97 func makeCoordinator() -> Coordinator {
98 Coordinator(self)
99 }
100
101 class Coordinator: PHPickerViewControllerDelegate {
102 let parent: WrappedPickerView
103
104 init(_ parent: WrappedPickerView) {
105 self.parent = parent
106 }
107
108 func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
109 parent.presentationMode.wrappedValue.dismiss()
110 parent.wrappedPicker.dismiss(animated: false)
111
112 parent.photoPickerResult = results.first
113 }
114 }
115}
116
This is far from ideal as I'm presenting at the wrong time and stuff. But it works until Apple provide a permanent fix for this.
QUESTION
NPM CI error bindings not accessible from watchpack-chokidar2:fsevents
Asked 2022-Feb-02 at 12:53When I run npm ci
on Github Actions I got the error:
1Run npm ci
2npm ERR! bindings not accessible from watchpack-chokidar2:fsevents
3
4npm ERR! A complete log of this run can be found in:
5npm ERR! /home/runner/.npm/_logs/2021-09-17T15_18_42_465Z-debug.log
6Error: Process completed with exit code 1.
7
What can be?
My .github/workflows/eslint.yaml
1Run npm ci
2npm ERR! bindings not accessible from watchpack-chokidar2:fsevents
3
4npm ERR! A complete log of this run can be found in:
5npm ERR! /home/runner/.npm/_logs/2021-09-17T15_18_42_465Z-debug.log
6Error: Process completed with exit code 1.
7name: ESLint
8
9on: [push, pull_request]
10
11jobs:
12 build:
13 runs-on: ubuntu-latest
14 steps:
15 - uses: actions/checkout@v2
16 - name: Use Node.js
17 uses: actions/setup-node@v1
18 with:
19 node-version: '14.x'
20 - run: npm ci
21 - run: npm run lint
22
My package.json
1Run npm ci
2npm ERR! bindings not accessible from watchpack-chokidar2:fsevents
3
4npm ERR! A complete log of this run can be found in:
5npm ERR! /home/runner/.npm/_logs/2021-09-17T15_18_42_465Z-debug.log
6Error: Process completed with exit code 1.
7name: ESLint
8
9on: [push, pull_request]
10
11jobs:
12 build:
13 runs-on: ubuntu-latest
14 steps:
15 - uses: actions/checkout@v2
16 - name: Use Node.js
17 uses: actions/setup-node@v1
18 with:
19 node-version: '14.x'
20 - run: npm ci
21 - run: npm run lint
22{
23 "name": "@blinktrade/uikit",
24 "version": "1.0.0",
25 "main": "dist/index.js",
26 "license": "MIT",
27 "devDependencies": {
28 "@babel/plugin-transform-react-jsx": "^7.14.9",
29 "@babel/preset-env": "^7.15.6",
30 "@babel/preset-react": "^7.14.5",
31 "@babel/preset-typescript": "^7.15.0",
32 "@storybook/addon-essentials": "^6.3.8",
33 "@storybook/react": "^6.3.8",
34 "@testing-library/jest-dom": "^5.14.1",
35 "@testing-library/react": "^12.1.0",
36 "@testing-library/user-event": "^13.2.1",
37 "@types/jest": "^27.0.1",
38 "@types/react": "^17.0.21",
39 "@typescript-eslint/eslint-plugin": "^4.31.1",
40 "@typescript-eslint/parser": "^4.31.1",
41 "eslint": "^7.32.0",
42 "eslint-plugin-react": "^7.25.2",
43 "husky": "^7.0.2",
44 "jest": "^27.2.0",
45 "prettier": "^2.4.1",
46 "pretty-quick": "^3.1.1",
47 "react": "^17.0.2",
48 "react-dom": "^17.0.2",
49 "rimraf": "^3.0.2",
50 "typescript": "^4.4.3"
51 },
52 "husky": {
53 "hooks": {
54 "pre-push": "npm run lint",
55 "pre-commit": "pretty-quick --staged"
56 }
57 },
58 "scripts": {
59 "build": "tsc -p .",
60 "clear": "rimraf dist/",
61 "format": "prettier '**/*' --write --ignore-unknown",
62 "lint": "eslint --max-warnings=0 .",
63 "storybook": "start-storybook -p 4000",
64 "test": "jest"
65 }
66}
67
ANSWER
Answered 2021-Sep-20 at 20:57Solved removing packages-lock.json and running again using NodeJS 14 (was 10)
QUESTION
ScrollViewReader scrollTo with .center anchor bug?
Asked 2022-Jan-31 at 10:38So I'm try to use ScrollViewReader to programmatically scroll a horizontal scroll view. I thought it would work like scrollToItem with .centeredHorizontally in UIKit, and for the most part it does, but the last few elements in the scroll view are being forcefully scrolled to the center of the screen, despite the fact that the scroll view isn't normally able to scroll that far over (without snapping back after releasing the drag, at least). This ends up creating white space across the trailing half of the screen.
I've seen some other questions about this and it seems like the general opinion is that it's not a bug? On the one hand I suppose we're telling the scroll view to center the item, and it's doing just that -- so, not a bug? On the other hand, that's not how similar functionality worked in UIKit. Also, this behavior is only happening on the trailing end of the scroll view! If it was the intended behavior I would expect that scrolling to the first element in the scroll view using .center anchor would force it into the center of the screen and leave leading white space, but this doesn't happen.
Is there an elegant solution to this? Or do we have to calculate the width of our elements + spacing and figure out based on the screen width whether we should anchor .center or just scroll to the last element with anchor .trailing in order to replicate the UIKit behavior?
ANSWER
Answered 2021-Jul-25 at 06:31I found a package (Amzd/ScrollViewProxy) that was made before ScrollViewReader was released that functions much the same as ScrollViewReader, but also seems to not have the bug (if it is a bug) detailed in the question.
Usage examples can be seen on the repository page, but here's a quick minimal example.
1ScrollView(.horizontal) { scrollProxy in
2 ForEach(sections) { section in
3 Text(section.text)
4 .scrollId(section.id)
5 }
6 .onChange(of: index) {
7 scrollProxy.scrollTo(
8 sections[index].id,
9 alignment: .center
10 )
11 }
12}
13
The package adds a convenience init to ScrollView to give access to the scrollProxy.
QUESTION
Subclassing UIView from Kotlin Native
Asked 2022-Jan-28 at 00:10UIKit is designed to be used through subclasses and overridden methods.
Typically, the drawRect
objective-C method of UIView is implemented like this in SWIFT:
1import UIKit
2import Foundation
3
4class SmileView: UIView {
5 override func draw(_ rect: CGRect) {
6 super.draw(rect)
7
8 let smile = ":)" as NSString
9 smile.draw(in: rect, withAttributes: nil)
10 }
11}
12
Unfortunately, the UIKit import in Kotlin defines these functions as extensions function that cannot be overridden.
Did anybody succeed in subclassing an UIView from Kotlin through a custom cinterop
configuration?
ANSWER
Answered 2021-Sep-22 at 09:10So we managed to make it work.
1. Add a cinterop configuration task in the build.gradle.kts
1import UIKit
2import Foundation
3
4class SmileView: UIView {
5 override func draw(_ rect: CGRect) {
6 super.draw(rect)
7
8 let smile = ":)" as NSString
9 smile.draw(in: rect, withAttributes: nil)
10 }
11}
12kotlin {
13 android()
14 ios {
15 binaries {
16 framework {
17 baseName = "shared"
18 }
19 }
20 compilations.getByName("main") {
21 val uikit by cinterops.creating {
22 }
23
24 }
25 }
26
2. Add a `src/nativeinterop/cinterop/uikit.def` file.
1import UIKit
2import Foundation
3
4class SmileView: UIView {
5 override func draw(_ rect: CGRect) {
6 super.draw(rect)
7
8 let smile = ":)" as NSString
9 smile.draw(in: rect, withAttributes: nil)
10 }
11}
12kotlin {
13 android()
14 ios {
15 binaries {
16 framework {
17 baseName = "shared"
18 }
19 }
20 compilations.getByName("main") {
21 val uikit by cinterops.creating {
22 }
23
24 }
25 }
26package = demo.cinterop
27language = Objective-C
28---
29
30#import <Foundation/Foundation.h>
31#import <UIKit/UIView.h>
32
33@protocol UIViewWithOverrides
34- (void) drawRect:(CGRect)aRect;
35- (void) layoutSubviews;
36@end
37
38
3. Create a custom UIView class
The class extends the UIView from UIKit and implements the previously created UIViewWithOverridesProtocol (the suffix is automatically added)
1import UIKit
2import Foundation
3
4class SmileView: UIView {
5 override func draw(_ rect: CGRect) {
6 super.draw(rect)
7
8 let smile = ":)" as NSString
9 smile.draw(in: rect, withAttributes: nil)
10 }
11}
12kotlin {
13 android()
14 ios {
15 binaries {
16 framework {
17 baseName = "shared"
18 }
19 }
20 compilations.getByName("main") {
21 val uikit by cinterops.creating {
22 }
23
24 }
25 }
26package = demo.cinterop
27language = Objective-C
28---
29
30#import <Foundation/Foundation.h>
31#import <UIKit/UIView.h>
32
33@protocol UIViewWithOverrides
34- (void) drawRect:(CGRect)aRect;
35- (void) layoutSubviews;
36@end
37
38package demo
39
40import demo.cinterop.UIViewWithOverridesProtocol
41import kotlinx.cinterop.*
42import platform.CoreGraphics.*
43import platform.UIKit.*
44
45@ExportObjCClass
46class MyView() : UIView(frame = CGRectMake(.0, .0, .0, .0)), UIViewWithOverridesProtocol {
47
48 override fun layoutSubviews() {
49 println("layoutSubviews")
50 setNeedsDisplay()
51 }
52
53 override fun drawRect(aRect: CValue<CGRect>) {
54 val rectAsString = aRect.useContents {
55 "" + this.origin.x + ", " + this.origin.y + ", " + (this.origin.x +this.size.width) + ", " + (this.origin.y +this.size.height)
56 }
57 println("drawRect:: Rect[$rectAsString]")
58
59 val context: CPointer<CGContext>? = UIGraphicsGetCurrentContext()
60 CGContextSetLineWidth(context, 2.0)
61 val components = cValuesOf(0.0, 0.0, 1.0, 1.0)
62 CGContextSetFillColor(context, components)
63 val square = CGRectMake(100.0, 100.0, 200.0, 200.0)
64 CGContextFillRect(context, square)
65
66 }
67
68}
69
70fun createMyView(): UIView = MyView()
71
72
4. Use it from Swift
1import UIKit
2import Foundation
3
4class SmileView: UIView {
5 override func draw(_ rect: CGRect) {
6 super.draw(rect)
7
8 let smile = ":)" as NSString
9 smile.draw(in: rect, withAttributes: nil)
10 }
11}
12kotlin {
13 android()
14 ios {
15 binaries {
16 framework {
17 baseName = "shared"
18 }
19 }
20 compilations.getByName("main") {
21 val uikit by cinterops.creating {
22 }
23
24 }
25 }
26package = demo.cinterop
27language = Objective-C
28---
29
30#import <Foundation/Foundation.h>
31#import <UIKit/UIView.h>
32
33@protocol UIViewWithOverrides
34- (void) drawRect:(CGRect)aRect;
35- (void) layoutSubviews;
36@end
37
38package demo
39
40import demo.cinterop.UIViewWithOverridesProtocol
41import kotlinx.cinterop.*
42import platform.CoreGraphics.*
43import platform.UIKit.*
44
45@ExportObjCClass
46class MyView() : UIView(frame = CGRectMake(.0, .0, .0, .0)), UIViewWithOverridesProtocol {
47
48 override fun layoutSubviews() {
49 println("layoutSubviews")
50 setNeedsDisplay()
51 }
52
53 override fun drawRect(aRect: CValue<CGRect>) {
54 val rectAsString = aRect.useContents {
55 "" + this.origin.x + ", " + this.origin.y + ", " + (this.origin.x +this.size.width) + ", " + (this.origin.y +this.size.height)
56 }
57 println("drawRect:: Rect[$rectAsString]")
58
59 val context: CPointer<CGContext>? = UIGraphicsGetCurrentContext()
60 CGContextSetLineWidth(context, 2.0)
61 val components = cValuesOf(0.0, 0.0, 1.0, 1.0)
62 CGContextSetFillColor(context, components)
63 val square = CGRectMake(100.0, 100.0, 200.0, 200.0)
64 CGContextFillRect(context, square)
65
66 }
67
68}
69
70fun createMyView(): UIView = MyView()
71
72struct ChartView: View {
73
74 var body: some View {
75
76 VStack {
77 Text("Chart View")
78 MyView()
79 .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
80 }
81 }
82
83}
84
85struct ChartView_Previews: PreviewProvider {
86 static var previews: some View {
87 ChartView()
88 }
89}
90
91struct MyView: UIViewRepresentable {
92
93 func makeUIView(context: Context) -> UIView {
94 UIChartViewKt.createMyView()
95 }
96
97 func updateUIView(_ uiView: UIView, context: Context) {
98 }
99
100}
101
102
QUESTION
UIImageView Is Blending Colors With No Alpha
Asked 2021-Dec-21 at 20:08I have been stumped by this for a few days now and can't find a solution. I have a UIImageView with a transparent background. It is on top of another view (in the example just a UIView with blue background color). For some reason, it will blend a color even if it has an alpha value of 0 (in this case full red no alpha values come out pink). The desired output is that it will show clear just like black with no alpha value.
If over white it behaves as intended (it is clear and shows white through). I have tried different ways of creating the CGContext and different CALayer blend modes and can't get it to not blend colors that don't have alpha values on them.
Here is some sample code to replicate the issue.
1import UIKit
2import PlaygroundSupport
3import CoreGraphics
4
5class MyViewController : UIViewController {
6 var pixelPointer: UnsafeMutablePointer<UInt8>!
7
8 let imageWidth = 20
9 let imageHeight = 20
10
11 override func loadView() {
12 let view = UIView()
13 view.backgroundColor = .blue
14
15 let viewSize: CGFloat = 150
16
17 // Test Image
18 pixelPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: imageWidth * imageWidth * 4)
19 let ctx = CGContext(data: pixelPointer,
20 width: imageWidth,
21 height: imageHeight,
22 bitsPerComponent: 8,
23 bytesPerRow: 4 * imageWidth,
24 space: CGColorSpaceCreateDeviceRGB(),
25 bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
26 for x in 0 ..< imageWidth {
27 for y in 0 ..< imageHeight {
28 if y == 7 || y == 8 || y == 9 {
29 // Red No Alpha -> Expected to be transparent
30 setColorFor(255, g: 0, b: 0, a: 0, x: x, y: y)
31 } else if y == 10 || y == 11 || y == 12 {
32 // Black No Alpha -> Is transparent
33 setColorFor(0, g: 0, b: 0, a: 0, x: x, y: y)
34 } else {
35 // Red
36 setColorFor(255, g: 0, b: 0, a: 255, x: x, y: y)
37 }
38
39 }
40 }
41 let cgImage = ctx.makeImage()!
42
43 // ImageView with Clear Background
44 let imageViewClearBackground = UIImageView()
45 imageViewClearBackground.backgroundColor = .clear
46 imageViewClearBackground.frame.origin = CGPoint(x: 10, y: 10)
47 imageViewClearBackground.layer.borderColor = UIColor.black.cgColor
48 imageViewClearBackground.layer.borderWidth = 1
49 imageViewClearBackground.frame.size = CGSize(width: viewSize, height: viewSize)
50 imageViewClearBackground.layer.magnificationFilter = CALayerContentsFilter.nearest
51
52 imageViewClearBackground.image = UIImage(cgImage: cgImage)
53 view.addSubview(imageViewClearBackground)
54
55 // ImageView with White Background
56 let imageViewWhiteBackground = UIImageView()
57 imageViewWhiteBackground.layer.borderColor = UIColor.black.cgColor
58 imageViewWhiteBackground.layer.borderWidth = 1
59 imageViewWhiteBackground.backgroundColor = .white
60 imageViewWhiteBackground.frame.size = CGSize(width: viewSize, height: viewSize)
61 imageViewWhiteBackground.frame.origin = CGPoint(x: viewSize + 20, y: 10)
62 imageViewWhiteBackground.layer.magnificationFilter = CALayerContentsFilter.nearest
63
64 imageViewWhiteBackground.image = UIImage(cgImage: cgImage)
65 view.addSubview(imageViewWhiteBackground)
66
67 self.view = view
68 }
69
70 func setColorFor(_ r: Int, g: Int, b: Int, a: Int, x: Int, y: Int) {
71 let offset = (y * Int(imageWidth) * 4) + x * 4
72 pixelPointer[offset+0] = UInt8(r)
73 pixelPointer[offset+1] = UInt8(g)
74 pixelPointer[offset+2] = UInt8(b)
75 pixelPointer[offset+3] = UInt8(a)
76 }
77}
78
79// Present the view controller in the Live View window
80PlaygroundPage.current.liveView = MyViewController()
81
ANSWER
Answered 2021-Oct-24 at 17:31The problem is that this:
1import UIKit
2import PlaygroundSupport
3import CoreGraphics
4
5class MyViewController : UIViewController {
6 var pixelPointer: UnsafeMutablePointer<UInt8>!
7
8 let imageWidth = 20
9 let imageHeight = 20
10
11 override func loadView() {
12 let view = UIView()
13 view.backgroundColor = .blue
14
15 let viewSize: CGFloat = 150
16
17 // Test Image
18 pixelPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: imageWidth * imageWidth * 4)
19 let ctx = CGContext(data: pixelPointer,
20 width: imageWidth,
21 height: imageHeight,
22 bitsPerComponent: 8,
23 bytesPerRow: 4 * imageWidth,
24 space: CGColorSpaceCreateDeviceRGB(),
25 bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
26 for x in 0 ..< imageWidth {
27 for y in 0 ..< imageHeight {
28 if y == 7 || y == 8 || y == 9 {
29 // Red No Alpha -> Expected to be transparent
30 setColorFor(255, g: 0, b: 0, a: 0, x: x, y: y)
31 } else if y == 10 || y == 11 || y == 12 {
32 // Black No Alpha -> Is transparent
33 setColorFor(0, g: 0, b: 0, a: 0, x: x, y: y)
34 } else {
35 // Red
36 setColorFor(255, g: 0, b: 0, a: 255, x: x, y: y)
37 }
38
39 }
40 }
41 let cgImage = ctx.makeImage()!
42
43 // ImageView with Clear Background
44 let imageViewClearBackground = UIImageView()
45 imageViewClearBackground.backgroundColor = .clear
46 imageViewClearBackground.frame.origin = CGPoint(x: 10, y: 10)
47 imageViewClearBackground.layer.borderColor = UIColor.black.cgColor
48 imageViewClearBackground.layer.borderWidth = 1
49 imageViewClearBackground.frame.size = CGSize(width: viewSize, height: viewSize)
50 imageViewClearBackground.layer.magnificationFilter = CALayerContentsFilter.nearest
51
52 imageViewClearBackground.image = UIImage(cgImage: cgImage)
53 view.addSubview(imageViewClearBackground)
54
55 // ImageView with White Background
56 let imageViewWhiteBackground = UIImageView()
57 imageViewWhiteBackground.layer.borderColor = UIColor.black.cgColor
58 imageViewWhiteBackground.layer.borderWidth = 1
59 imageViewWhiteBackground.backgroundColor = .white
60 imageViewWhiteBackground.frame.size = CGSize(width: viewSize, height: viewSize)
61 imageViewWhiteBackground.frame.origin = CGPoint(x: viewSize + 20, y: 10)
62 imageViewWhiteBackground.layer.magnificationFilter = CALayerContentsFilter.nearest
63
64 imageViewWhiteBackground.image = UIImage(cgImage: cgImage)
65 view.addSubview(imageViewWhiteBackground)
66
67 self.view = view
68 }
69
70 func setColorFor(_ r: Int, g: Int, b: Int, a: Int, x: Int, y: Int) {
71 let offset = (y * Int(imageWidth) * 4) + x * 4
72 pixelPointer[offset+0] = UInt8(r)
73 pixelPointer[offset+1] = UInt8(g)
74 pixelPointer[offset+2] = UInt8(b)
75 pixelPointer[offset+3] = UInt8(a)
76 }
77}
78
79// Present the view controller in the Live View window
80PlaygroundPage.current.liveView = MyViewController()
81setColorFor(255, g: 0, b: 0, a: 0, x: x, y: y)
82
is not a valid .premultipliedLast
color value. Premultiplied means that the colors have already been multiplied by the alpha. If you multiply 255 by 0 you get 0, so that is the correct red value here — as the result in rows 10-12 demonstrates.
You would probably confuse yourself a lot less if you would just construct the image using UIGraphicsImageRenderer in the normal way rather than a raw bitmap context. But of course if your use case precludes that, then by all means use the bitmap — but then there is a lot more room for you to use it incorrectly.
QUESTION
additionalSafeAreaInsets is not accounted for during view controller dismissal, using custom UIViewControllerTransitioningDelegate
Asked 2021-Dec-19 at 18:12So, straight to the problem:
I've created a custom UIViewControllerTransitioningDelegate that I use to animate a view from one view controller, to full-screen in another view controller. Im doing this by creating UIViewControllerAnimatedTransitioning-objects that animate the presented view's frame. And it works great! Except when I try to adjust the additionalSafeAreaInsets of the view controller owning the view during dismissal...
It looks like this property is not accounted for when I'm trying to animate the dismissal of the view controller and its view. It works fine during presentation.
The gif below shows how it looks. The red box is the safe area (plus some padding) of the presented view - which I'm trying to compensate for during animation, using the additionalSafeAreaInsets property of the view controller owning the view.
As the gif shows, the safe area is properly adjusted during presentation but not during dismissal.
So, what I want is: use additionalSafeAreaInsets to diminish the effect of the safe area during animation, by setting additionalSafeAreaInsets to the "inverted" values of the safe area. So that the effective safe area starts at 0 and "animates" to the expected value during presentation, and starts at expected value and "animates" to 0 during dismissal. (I'm quoting "animates", since its actually the view's frame that is animated. But UIKit/Auto Layout use these properties when calculating the frames)
Any thoughts on how to battle this issue is great welcome!
The code for the custom UIViewControllerTransitioningDelegate is provided below.
1//
2// FullScreenTransitionManager.swift
3//
4
5import Foundation
6import UIKit
7
8// MARK: FullScreenPresentationController
9
10final class FullScreenPresentationController: UIPresentationController {
11 private let backgroundView: UIView = {
12 let view = UIView()
13 view.backgroundColor = .systemBackground
14 view.alpha = 0
15 return view
16 }()
17
18 private lazy var tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(onTap))
19
20 @objc private func onTap(_ gesture: UITapGestureRecognizer) {
21 presentedViewController.dismiss(animated: true)
22 }
23}
24
25// MARK: UIPresentationController
26
27extension FullScreenPresentationController {
28 override func presentationTransitionWillBegin() {
29 guard let containerView = containerView else { return }
30
31 containerView.addGestureRecognizer(tapGestureRecognizer)
32
33 containerView.addSubview(backgroundView)
34 backgroundView.frame = containerView.frame
35
36 guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
37
38 transitionCoordinator.animate(alongsideTransition: { context in
39 self.backgroundView.alpha = 1
40 })
41 }
42
43 override func presentationTransitionDidEnd(_ completed: Bool) {
44 if !completed {
45 backgroundView.removeFromSuperview()
46 containerView?.removeGestureRecognizer(tapGestureRecognizer)
47 }
48 }
49
50 override func dismissalTransitionWillBegin() {
51 guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
52
53 transitionCoordinator.animate(alongsideTransition: { context in
54 self.backgroundView.alpha = 0
55 })
56 }
57
58 override func dismissalTransitionDidEnd(_ completed: Bool) {
59 if completed {
60 backgroundView.removeFromSuperview()
61 containerView?.removeGestureRecognizer(tapGestureRecognizer)
62 }
63 }
64
65 override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
66 guard
67 let containerView = containerView,
68 let presentedView = presentedView
69 else { return }
70 coordinator.animate(alongsideTransition: { context in
71 self.backgroundView.frame = containerView.frame
72 presentedView.frame = self.frameOfPresentedViewInContainerView
73 })
74 }
75}
76
77// MARK: FullScreenTransitionManager
78
79final class FullScreenTransitionManager: NSObject, UIViewControllerTransitioningDelegate {
80 private weak var anchorView: UIView?
81
82 init(anchorView: UIView) {
83 self.anchorView = anchorView
84 }
85
86 func presentationController(forPresented presented: UIViewController,
87 presenting: UIViewController?,
88 source: UIViewController) -> UIPresentationController? {
89 FullScreenPresentationController(presentedViewController: presented, presenting: presenting)
90 }
91
92 func animationController(forPresented presented: UIViewController,
93 presenting: UIViewController,
94 source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
95 let anchorFrame = anchorView?.safeAreaLayoutGuide.layoutFrame ?? CGRect(origin: presented.view.center, size: .zero)
96 return FullScreenAnimationController(animationType: .present,
97 anchorFrame: anchorFrame)
98 }
99
100 func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
101 let anchorFrame = anchorView?.safeAreaLayoutGuide.layoutFrame ?? CGRect(origin: dismissed.view.center, size: .zero)
102 return FullScreenAnimationController(animationType: .dismiss,
103 anchorFrame: anchorFrame)
104 }
105}
106
107// MARK: UIViewControllerAnimatedTransitioning
108
109final class FullScreenAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
110 enum AnimationType {
111 case present
112 case dismiss
113 }
114
115 private let animationType: AnimationType
116 private let anchorFrame: CGRect
117 private let animationDuration: TimeInterval
118 private var propertyAnimator: UIViewPropertyAnimator?
119
120 init(animationType: AnimationType, anchorFrame: CGRect, animationDuration: TimeInterval = 0.3) {
121 self.animationType = animationType
122 self.anchorFrame = anchorFrame
123 self.animationDuration = animationDuration
124 }
125
126 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
127 animationDuration
128 }
129
130 func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
131 switch animationType {
132 case .present:
133 guard
134 let toViewController = transitionContext.viewController(forKey: .to)
135 else {
136 return transitionContext.completeTransition(false)
137 }
138 transitionContext.containerView.addSubview(toViewController.view)
139 propertyAnimator = presentAnimator(with: transitionContext, animating: toViewController)
140 case .dismiss:
141 guard
142 let fromViewController = transitionContext.viewController(forKey: .from)
143 else {
144 return transitionContext.completeTransition(false)
145 }
146 propertyAnimator = dismissAnimator(with: transitionContext, animating: fromViewController)
147 }
148 }
149
150 private func presentAnimator(with transitionContext: UIViewControllerContextTransitioning,
151 animating viewController: UIViewController) -> UIViewPropertyAnimator {
152 let finalFrame = transitionContext.finalFrame(for: viewController)
153 let safeAreaInsets = transitionContext.containerView.safeAreaInsets
154 let safeAreaCompensation = UIEdgeInsets(top: -safeAreaInsets.top,
155 left: -safeAreaInsets.left,
156 bottom: -safeAreaInsets.bottom,
157 right: -safeAreaInsets.right)
158 viewController.additionalSafeAreaInsets = safeAreaCompensation
159 viewController.view.frame = anchorFrame
160 viewController.view.setNeedsLayout()
161 viewController.view.layoutIfNeeded()
162 return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.curveEaseInOut, .layoutSubviews], animations: {
163 viewController.additionalSafeAreaInsets = .zero
164 viewController.view.frame = finalFrame
165 viewController.view.setNeedsLayout()
166 viewController.view.layoutIfNeeded()
167 }, completion: { _ in
168 transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
169 })
170 }
171
172 private func dismissAnimator(with transitionContext: UIViewControllerContextTransitioning,
173 animating viewController: UIViewController) -> UIViewPropertyAnimator {
174 let finalFrame = anchorFrame
175 let safeAreaInsets = transitionContext.containerView.safeAreaInsets
176 let safeAreaCompensation = UIEdgeInsets(top: -safeAreaInsets.top,
177 left: -safeAreaInsets.left,
178 bottom: -safeAreaInsets.bottom,
179 right: -safeAreaInsets.right)
180 viewController.view.setNeedsLayout()
181 viewController.view.layoutIfNeeded()
182 return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.curveEaseInOut, .layoutSubviews], animations: {
183 viewController.additionalSafeAreaInsets = safeAreaCompensation
184 viewController.view.frame = finalFrame
185 viewController.view.setNeedsLayout()
186 viewController.view.layoutIfNeeded()
187 }, completion: { _ in
188 transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
189 })
190 }
191}
192
193
ANSWER
Answered 2021-Dec-19 at 18:12After some debugging I managed to find a workaround to this problem.
In short, it looks like the safe area is not updated after UIViewController.viewWillDisappear
is called, and hence any changes to .additionalSafeAreaInsets
is ignored (since these insets modifies the safe area of the view controller's view).
My current workaround is somewhat hacky, but it gets the job done. Since UIViewControllerTransitioningDelegate.animationController(forDismissed...)
is called right before UIViewController.viewWillDisappear
and UIViewControllerAnimatedTransitioning.animateTransition(using transitionContext...)
, I start the dismiss animation already in that method. That way the layout calculations for the animation get correct, and the correct safe area is set.
Below is the code for my custom UIViewControllerTransitioningDelegate
with the workaround. Note: I've removed the use of .additionalSafeAreaInsets
since its not necessary at all! And I've no idea why I thought I needed it in the first place...
1//
2// FullScreenTransitionManager.swift
3//
4
5import Foundation
6import UIKit
7
8// MARK: FullScreenPresentationController
9
10final class FullScreenPresentationController: UIPresentationController {
11 private let backgroundView: UIView = {
12 let view = UIView()
13 view.backgroundColor = .systemBackground
14 view.alpha = 0
15 return view
16 }()
17
18 private lazy var tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(onTap))
19
20 @objc private func onTap(_ gesture: UITapGestureRecognizer) {
21 presentedViewController.dismiss(animated: true)
22 }
23}
24
25// MARK: UIPresentationController
26
27extension FullScreenPresentationController {
28 override func presentationTransitionWillBegin() {
29 guard let containerView = containerView else { return }
30
31 containerView.addGestureRecognizer(tapGestureRecognizer)
32
33 containerView.addSubview(backgroundView)
34 backgroundView.frame = containerView.frame
35
36 guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
37
38 transitionCoordinator.animate(alongsideTransition: { context in
39 self.backgroundView.alpha = 1
40 })
41 }
42
43 override func presentationTransitionDidEnd(_ completed: Bool) {
44 if !completed {
45 backgroundView.removeFromSuperview()
46 containerView?.removeGestureRecognizer(tapGestureRecognizer)
47 }
48 }
49
50 override func dismissalTransitionWillBegin() {
51 guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
52
53 transitionCoordinator.animate(alongsideTransition: { context in
54 self.backgroundView.alpha = 0
55 })
56 }
57
58 override func dismissalTransitionDidEnd(_ completed: Bool) {
59 if completed {
60 backgroundView.removeFromSuperview()
61 containerView?.removeGestureRecognizer(tapGestureRecognizer)
62 }
63 }
64
65 override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
66 guard
67 let containerView = containerView,
68 let presentedView = presentedView
69 else { return }
70 coordinator.animate(alongsideTransition: { context in
71 self.backgroundView.frame = containerView.frame
72 presentedView.frame = self.frameOfPresentedViewInContainerView
73 })
74 }
75}
76
77// MARK: FullScreenTransitionManager
78
79final class FullScreenTransitionManager: NSObject, UIViewControllerTransitioningDelegate {
80 private weak var anchorView: UIView?
81
82 init(anchorView: UIView) {
83 self.anchorView = anchorView
84 }
85
86 func presentationController(forPresented presented: UIViewController,
87 presenting: UIViewController?,
88 source: UIViewController) -> UIPresentationController? {
89 FullScreenPresentationController(presentedViewController: presented, presenting: presenting)
90 }
91
92 func animationController(forPresented presented: UIViewController,
93 presenting: UIViewController,
94 source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
95 let anchorFrame = anchorView?.safeAreaLayoutGuide.layoutFrame ?? CGRect(origin: presented.view.center, size: .zero)
96 return FullScreenAnimationController(animationType: .present,
97 anchorFrame: anchorFrame)
98 }
99
100 func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
101 let anchorFrame = anchorView?.safeAreaLayoutGuide.layoutFrame ?? CGRect(origin: dismissed.view.center, size: .zero)
102 return FullScreenAnimationController(animationType: .dismiss,
103 anchorFrame: anchorFrame)
104 }
105}
106
107// MARK: UIViewControllerAnimatedTransitioning
108
109final class FullScreenAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
110 enum AnimationType {
111 case present
112 case dismiss
113 }
114
115 private let animationType: AnimationType
116 private let anchorFrame: CGRect
117 private let animationDuration: TimeInterval
118 private var propertyAnimator: UIViewPropertyAnimator?
119
120 init(animationType: AnimationType, anchorFrame: CGRect, animationDuration: TimeInterval = 0.3) {
121 self.animationType = animationType
122 self.anchorFrame = anchorFrame
123 self.animationDuration = animationDuration
124 }
125
126 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
127 animationDuration
128 }
129
130 func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
131 switch animationType {
132 case .present:
133 guard
134 let toViewController = transitionContext.viewController(forKey: .to)
135 else {
136 return transitionContext.completeTransition(false)
137 }
138 transitionContext.containerView.addSubview(toViewController.view)
139 propertyAnimator = presentAnimator(with: transitionContext, animating: toViewController)
140 case .dismiss:
141 guard
142 let fromViewController = transitionContext.viewController(forKey: .from)
143 else {
144 return transitionContext.completeTransition(false)
145 }
146 propertyAnimator = dismissAnimator(with: transitionContext, animating: fromViewController)
147 }
148 }
149
150 private func presentAnimator(with transitionContext: UIViewControllerContextTransitioning,
151 animating viewController: UIViewController) -> UIViewPropertyAnimator {
152 let finalFrame = transitionContext.finalFrame(for: viewController)
153 let safeAreaInsets = transitionContext.containerView.safeAreaInsets
154 let safeAreaCompensation = UIEdgeInsets(top: -safeAreaInsets.top,
155 left: -safeAreaInsets.left,
156 bottom: -safeAreaInsets.bottom,
157 right: -safeAreaInsets.right)
158 viewController.additionalSafeAreaInsets = safeAreaCompensation
159 viewController.view.frame = anchorFrame
160 viewController.view.setNeedsLayout()
161 viewController.view.layoutIfNeeded()
162 return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.curveEaseInOut, .layoutSubviews], animations: {
163 viewController.additionalSafeAreaInsets = .zero
164 viewController.view.frame = finalFrame
165 viewController.view.setNeedsLayout()
166 viewController.view.layoutIfNeeded()
167 }, completion: { _ in
168 transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
169 })
170 }
171
172 private func dismissAnimator(with transitionContext: UIViewControllerContextTransitioning,
173 animating viewController: UIViewController) -> UIViewPropertyAnimator {
174 let finalFrame = anchorFrame
175 let safeAreaInsets = transitionContext.containerView.safeAreaInsets
176 let safeAreaCompensation = UIEdgeInsets(top: -safeAreaInsets.top,
177 left: -safeAreaInsets.left,
178 bottom: -safeAreaInsets.bottom,
179 right: -safeAreaInsets.right)
180 viewController.view.setNeedsLayout()
181 viewController.view.layoutIfNeeded()
182 return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.curveEaseInOut, .layoutSubviews], animations: {
183 viewController.additionalSafeAreaInsets = safeAreaCompensation
184 viewController.view.frame = finalFrame
185 viewController.view.setNeedsLayout()
186 viewController.view.layoutIfNeeded()
187 }, completion: { _ in
188 transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
189 })
190 }
191}
192
193//
194// FullScreenTransitionManager.swift
195//
196
197import Foundation
198import UIKit
199
200// MARK: FullScreenPresentationController
201
202final class FullScreenPresentationController: UIPresentationController {
203 private let backgroundView: UIView = {
204 let view = UIView()
205 view.backgroundColor = .systemBackground
206 view.alpha = 0
207 return view
208 }()
209
210 private lazy var tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(onTap))
211
212 @objc private func onTap(_ gesture: UITapGestureRecognizer) {
213 presentedViewController.dismiss(animated: true)
214 }
215}
216
217// MARK: UIPresentationController
218
219extension FullScreenPresentationController {
220 override func presentationTransitionWillBegin() {
221 guard let containerView = containerView else { return }
222
223 containerView.addGestureRecognizer(tapGestureRecognizer)
224
225 containerView.addSubview(backgroundView)
226 backgroundView.frame = containerView.frame
227
228 guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
229
230 transitionCoordinator.animate(alongsideTransition: { context in
231 self.backgroundView.alpha = 1
232 })
233 }
234
235 override func presentationTransitionDidEnd(_ completed: Bool) {
236 if !completed {
237 backgroundView.removeFromSuperview()
238 containerView?.removeGestureRecognizer(tapGestureRecognizer)
239 }
240 }
241
242 override func dismissalTransitionWillBegin() {
243 guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
244
245 transitionCoordinator.animate(alongsideTransition: { context in
246 self.backgroundView.alpha = 0
247 })
248 }
249
250 override func dismissalTransitionDidEnd(_ completed: Bool) {
251 if completed {
252 backgroundView.removeFromSuperview()
253 containerView?.removeGestureRecognizer(tapGestureRecognizer)
254 }
255 }
256
257 override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
258 guard let containerView = containerView else { return }
259 coordinator.animate(alongsideTransition: { context in
260 self.backgroundView.frame = containerView.frame
261 })
262 }
263}
264
265// MARK: FullScreenTransitionManager
266
267final class FullScreenTransitionManager: NSObject, UIViewControllerTransitioningDelegate {
268 fileprivate enum AnimationState {
269 case present
270 case dismiss
271 }
272
273 private weak var anchorView: UIView?
274
275 private var animationState: AnimationState = .present
276 private var animationDuration: TimeInterval = Resources.animation.duration
277 private var anchorViewFrame: CGRect = .zero
278
279 private var propertyAnimator: UIViewPropertyAnimator?
280
281 init(anchorView: UIView) {
282 self.anchorView = anchorView
283 }
284
285 func presentationController(forPresented presented: UIViewController,
286 presenting: UIViewController?,
287 source: UIViewController) -> UIPresentationController? {
288 FullScreenPresentationController(presentedViewController: presented, presenting: presenting)
289 }
290
291 func animationController(forPresented presented: UIViewController,
292 presenting: UIViewController,
293 source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
294 prepare(animationState: .present)
295 }
296
297 func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
298 // Starting the animation here, since UIKit do not update safe area insets after UIViewController.viewWillDisappear() is called
299 defer {
300 propertyAnimator = dismissAnimator(animating: dismissed)
301 }
302 return prepare(animationState: .dismiss)
303 }
304}
305
306// MARK: UIViewControllerAnimatedTransitioning
307
308extension FullScreenTransitionManager: UIViewControllerAnimatedTransitioning {
309 private func prepare(animationState: AnimationState,
310 animationDuration: TimeInterval = Resources.animation.duration) -> UIViewControllerAnimatedTransitioning? {
311 guard let anchorView = anchorView else { return nil }
312
313 self.animationState = animationState
314 self.animationDuration = animationDuration
315 self.anchorViewFrame = anchorView.safeAreaLayoutGuide.layoutFrame
316
317 return self
318 }
319
320 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
321 animationDuration
322 }
323
324 func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
325 switch animationState {
326 case .present:
327 guard
328 let toViewController = transitionContext.viewController(forKey: .to)
329 else {
330 return transitionContext.completeTransition(false)
331 }
332 propertyAnimator = presentAnimator(with: transitionContext, animating: toViewController)
333 case .dismiss:
334 guard
335 let fromViewController = transitionContext.viewController(forKey: .from)
336 else {
337 return transitionContext.completeTransition(false)
338 }
339 propertyAnimator = updatedDismissAnimator(with: transitionContext, animating: fromViewController)
340 }
341 }
342
343 private func presentAnimator(with transitionContext: UIViewControllerContextTransitioning,
344 animating viewController: UIViewController) -> UIViewPropertyAnimator {
345 transitionContext.containerView.addSubview(viewController.view)
346 let finalFrame = transitionContext.finalFrame(for: viewController)
347 viewController.view.frame = anchorViewFrame
348 viewController.view.layoutIfNeeded()
349 return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: transitionDuration(using: transitionContext),
350 delay: 0,
351 options: [.curveEaseInOut],
352 animations: {
353 viewController.view.frame = finalFrame
354 viewController.view.layoutIfNeeded()
355 }, completion: { _ in
356 transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
357 })
358 }
359
360 private func dismissAnimator(animating viewController: UIViewController) -> UIViewPropertyAnimator {
361 let finalFrame = anchorViewFrame
362 return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: animationDuration,
363 delay: 0,
364 options: [.curveEaseInOut],
365 animations: {
366 viewController.view.frame = finalFrame
367 viewController.view.layoutIfNeeded()
368 })
369 }
370
371 private func updatedDismissAnimator(with transitionContext: UIViewControllerContextTransitioning,
372 animating viewController: UIViewController) -> UIViewPropertyAnimator {
373 let propertyAnimator = self.propertyAnimator ?? dismissAnimator(animating: viewController)
374 propertyAnimator.addCompletion({ _ in
375 transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
376 })
377 self.propertyAnimator = propertyAnimator
378 return propertyAnimator
379 }
380}
381
Also, here is a link to a Stack Overflow post regarding the safe area not updating after UIViewController.viewWillDisappear
. And a link to a similar post on the Apple forums.
QUESTION
Method 'UIKit.UIApplication.Main' is obsolete: Use the overload with 'Type' instead of 'String' parameters for type safety
Asked 2021-Nov-17 at 20:38After upgrade my Xamarin.Forms version to 5.0.0.2244, I'm getting the following warning in Main.cs
file inside my iOS project:
1Method 'UIKit.UIApplication.Main' is obsolete: Use the overload with 'Type' instead of 'String' parameters for type safety.
2
This my Main.cs
file:
1Method 'UIKit.UIApplication.Main' is obsolete: Use the overload with 'Type' instead of 'String' parameters for type safety.
2using UIKit;
3
4namespace LindeGctMobileApplication.iOS
5{
6 public class Application
7 {
8 private static void Main(string[] args)
9 {
10 UIApplication.Main(args, null, "AppDelegate"); // << warning
11 }
12 }
13}
14
What I need to change to remove this warning?
ANSWER
Answered 2021-Nov-17 at 20:38Class reference through a string is now deprecated. You need to change this line:
1Method 'UIKit.UIApplication.Main' is obsolete: Use the overload with 'Type' instead of 'String' parameters for type safety.
2using UIKit;
3
4namespace LindeGctMobileApplication.iOS
5{
6 public class Application
7 {
8 private static void Main(string[] args)
9 {
10 UIApplication.Main(args, null, "AppDelegate"); // << warning
11 }
12 }
13}
14UIApplication.Main(args, null, "AppDelegate");
15
to this:
1Method 'UIKit.UIApplication.Main' is obsolete: Use the overload with 'Type' instead of 'String' parameters for type safety.
2using UIKit;
3
4namespace LindeGctMobileApplication.iOS
5{
6 public class Application
7 {
8 private static void Main(string[] args)
9 {
10 UIApplication.Main(args, null, "AppDelegate"); // << warning
11 }
12 }
13}
14UIApplication.Main(args, null, "AppDelegate");
15UIApplication.Main(args, null, typeof(AppDelegate));
16
In that way, you explicitly inform the type of the class.
QUESTION
Document browser app in iOS 15 keeps creating iCloud drive folder
Asked 2021-Oct-28 at 00:42I have had a document browser app up since iOS 11 (using UIKit/UIDocumentBrowserViewController). The app (let's call it "MyApp") used to create a "MyApp" folder in iCloud Drive (and/or on the device, depending on the choice made in Settings), where documents would be saved automatically.
After updating my device to iOS 15 and recompiling, without any changes to the code, the app seems not to recognize its own folder in iCloud Drive anymore. Every time a file is opened/imported from outside the app (Files, Mail, etc.), the app creates a new "MyApp" folder in iCloud Drive and saves the document there. The existing folder is renamed "MyApp 2" (and then "MyApp 3", "MyApp 4" etc. if I try to import another document).
This seems to happen only in iCloud Drive. If I choose in Settings to save documents on my device, all documents are automatically imported into the "My App" folder, as it was the case in iOS 14 and earlier.
I also tried to create a test app from scratch using the Xcode 13 UIKit document-based app template, and the behavior seems to be exactly the same... so it looks like a bug in UIDocumentBrowserViewController was introduced with the iOS update.
Did anyone notice the same? Is there a solution/workaround? Should a bug report be filed?
Thank you for your help.
[EDIT] If anybody is interested, this is the simplest way to reproduce the issue using the Xcode template:
- Create a new project in Xcode 13.0
- Choose iOS -> Document App
- Choose Interface: Storyboard and create the project (for simplicity let’s use “MyApp” as Product Name)
- Build and install MyApp on a device running iOS 15.0 or 15.0.1 with iCloud Drive enabled
- Leave the default setting for Document Storage (i.e. iCloud Drive) in Settings -> MyApp
- Create a text file with any content and save it with extension “exampletext” (i.e. the imported type identifier used by the app template - let’s call the file “test.exampletext”)
- Send an email to an inbox accessible from the device with test.exampletext as attachment
- Open Mail on the device, long-press on test.exampletext and share it to MyApp
- If a folder “MyApp” exists in iCloud Drive, it will be renamed to “MyApp 2”; a new “MyApp” folder will be created and test.exampletext will be saved in the new folder
- Sharing again test.exampletext from the mail to MyApp will create another “MyApp” folder and rename the previous one; an arbitrary number of “MyApp n” folders can be created in this way, each including one copy of the document
ANSWER
Answered 2021-Oct-28 at 00:42This is fixed in the 15.2 beta.
Community Discussions contain sources that include Stack Exchange Network
Tutorials and Learning Resources in Uikit
Tutorials and Learning Resources are not available at this moment for Uikit