Explore all Uikit open source software, libraries, packages, source code, cloud functions and APIs.

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

awesome-ios

by vsouza doticonswiftdoticon

star image 37808 doticonMIT

A curated list of awesome iOS ecosystem, including Objective-C and Swift Projects

mdb-ui-kit

by mdbootstrap doticonjavascriptdoticon

star image 21896 doticonNOASSERTION

Bootstrap 5 & Material Design 2.0 UI KIT

Hero

by HeroTransitions doticonswiftdoticon

star image 20722 doticonMIT

Elegant transition library for iOS & tvOS

vant

by youzan doticontypescriptdoticon

star image 19479 doticonMIT

Lightweight Mobile UI Components built on Vue

Flat-UI

by designmodo doticonjavascriptdoticon

star image 14862 doticonMIT

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.

Material

by CosmicMind doticonswiftdoticon

star image 11774 doticonMIT

A UI/UX framework for creating beautiful applications.

Eureka

by xmartlabs doticonswiftdoticon

star image 11392 doticonMIT

Elegant iOS form builder in Swift

animated-tab-bar

by Ramotion doticonswiftdoticon

star image 10851 doticonMIT

:octocat: RAMAnimatedTabBarController is a Swift UI module library for adding animation to iOS tabbar items and icons. iOS library made by @Ramotion

IBAnimatable

by IBAnimatable doticonswiftdoticon

star image 8600 doticonMIT

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

SwiftUI-Kit

by jordansinger doticonswiftdoticon

star image 1687 doticonMIT

A SwiftUI system components and interactions demo app

chakra-templates

by hauptrolle doticontypescriptdoticon

star image 781 doticonMIT

A growing collection of responsive Chakra UI Templates ready to drop into your React project.

neumorphism-ui-bootstrap

by themesberg doticonhtmldoticon

star image 671 doticonMIT

Neumorphism inspired UI Kit: web components, sections and pages in neumorphic style built with Bootstrap CSS Framework

Parma

by dasautoooo doticonswiftdoticon

star image 655 doticonMIT

A SwiftUI view for displaying Markdown with customizable appearances.

fluentui-apple

by microsoft doticonswiftdoticon

star image 641 doticonMIT

UIKit and AppKit controls for building native Microsoft experiences

awesome-ios-developer

by jphong1111 doticonswiftdoticon

star image 545 doticonMIT

List of awesome iOS & Swift stuff!!

Gedatsu

by bannzai doticonswiftdoticon

star image 473 doticonMIT

Gedatsu provide readable format about AutoLayout error console log

SwiftUIKitView

by AvdLee doticonswiftdoticon

star image 451 doticonMIT

Easily use UIKit views in your SwiftUI applications. Create Xcode Previews for UIView elements

KeyboardGuide

by niw doticonswiftdoticon

star image 429 doticonMIT

A modern, real iOS keyboard system notifications handler framework that Just Works.

Top Authors in Uikit

1

Ramotion

11 Libraries

star icon33659

2

jVirus

8 Libraries

star icon1186

3

ivanvorobei

8 Libraries

star icon10688

4

mdbootstrap

8 Libraries

star icon25419

5

maximbilan

8 Libraries

star icon199

6

master3-blank-template

6 Libraries

star icon42

7

alfianlosari

6 Libraries

star icon149

8

omaralbeik

6 Libraries

star icon210

9

ajaymarathe

5 Libraries

star icon16

10

fumiyasac

5 Libraries

star icon125

1

11 Libraries

star icon33659

2

8 Libraries

star icon1186

3

8 Libraries

star icon10688

4

8 Libraries

star icon25419

5

8 Libraries

star icon199

7

6 Libraries

star icon149

8

6 Libraries

star icon210

9

5 Libraries

star icon16

10

5 Libraries

star icon125

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:04

I 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:34

You 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.

Source https://stackoverflow.com/questions/71560311

QUESTION

Flutter on iOS: redefinition of module 'Firebase'

Asked 2022-Mar-11 at 10:40

I'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 and pod 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:43

If you use M1 try this

arch -x86_64 pod install

Source https://stackoverflow.com/questions/70760326

QUESTION

PHPickerViewController tapping on Search gets error... "Unable to load photos"

Asked 2022-Feb-10 at 17:27

I'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).

Sample project

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 code

My 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
All this works in the simple case

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 case

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 UIKit

I 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:32

Well.. 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 workaround

After 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.

Source https://stackoverflow.com/questions/69306179

QUESTION

NPM CI error bindings not accessible from watchpack-chokidar2:fsevents

Asked 2022-Feb-02 at 12:53

When 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:57

Solved removing packages-lock.json and running again using NodeJS 14 (was 10)

Source https://stackoverflow.com/questions/69225852

QUESTION

ScrollViewReader scrollTo with .center anchor bug?

Asked 2022-Jan-31 at 10:38

So 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:31

I 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.

Source https://stackoverflow.com/questions/68515053

QUESTION

Subclassing UIView from Kotlin Native

Asked 2022-Jan-28 at 00:10

UIKit 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:10

So 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

Source https://stackoverflow.com/questions/69266205

QUESTION

UIImageView Is Blending Colors With No Alpha

Asked 2021-Dec-21 at 20:08

I 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.

enter image description here

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:31

The 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.

Source https://stackoverflow.com/questions/69698304

QUESTION

additionalSafeAreaInsets is not accounted for during view controller dismissal, using custom UIViewControllerTransitioningDelegate

Asked 2021-Dec-19 at 18:12

So, 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.

additionalSafeAreaInsets only accounted for during presentation

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:12

After 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.

Source https://stackoverflow.com/questions/70366593

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:38

After 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:38

Class 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.

Source https://stackoverflow.com/questions/70011243

QUESTION

Document browser app in iOS 15 keeps creating iCloud drive folder

Asked 2021-Oct-28 at 00:42

I 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:42

This is fixed in the 15.2 beta.

Source https://stackoverflow.com/questions/69279614

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

Share this Page

share link

Get latest updates on Uikit