uikit | UIKit - modern ui components for the modern web | User Interface library
kandi X-RAY | uikit Summary
Support
Quality
Security
License
Reuse
- build ui
- Initialize a new ColorPicker .
- Initialize a overlay .
- Highlight the given string .
- Initialize a new Dialog .
- Initialize a new Menu .
- Initialize a new notification .
- Update h color position
- Initialize a new Card .
- Creates a new SplitButton .
uikit Key Features
uikit Examples and Code Snippets
Trending Discussions on uikit
Trending Discussions on uikit
QUESTION
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:
import UIKit
class LabelAndSwitchTableViewCell: UITableViewCell {
private let label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let _switch: UISwitch = {
let _switch = UISwitch()
_switch.translatesAutoresizingMaskIntoConstraints = false
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
return _switch
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(label)
contentView.addSubview(_switch)
// layout constraints removed for brevity
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func didToggleSwitch() {
print("Switch was toggled...")
}
}
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:
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
After updating to Xcode 13.3, I'm now seeing a new warning on this line:
'self' refers to the method 'LabelAndSwitchTableViewCell.self', which may be unexpected
Xcode's suggestion to silence this warning is to replace:
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
...with...
_switch.addTarget(LabelAndSwitchTableViewCell.self, action: #selector(didToggleSwitch), for: .valueChanged)
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:
[app_mockup.LabelAndSwitchTableViewCell didToggleSwitch]: unrecognized selector sent to class 0x1043d86e8
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[app_mockup.LabelAndSwitchTableViewCell didToggleSwitch]: unrecognized selector sent to class 0x1043d86e8'
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
private lazy var _switch2: UISwitch = {
let _switch = UISwitch()
_switch.translatesAutoresizingMaskIntoConstraints = false
_switch.addTarget(self, action: #selector(didToggleSwitch), for: .valueChanged)
return _switch
}()
The Xcode fix-it suggestion is just wrong.
QUESTION
I've been trying to build my flutter app on iOS but flutter run
is throwing the following error:
/Users//Desktop/projects/app/ios/Pods/Firebase/CoreOnly/Source
s/module.modulemap:1:8: error: redefinition of module 'Firebase'
module Firebase {
^
/Users//Library/Developer/Xcode/DerivedData/Runner-dbkgurnsasbvieahfnk
dontejqss/SourcePackages/checkouts/firebase-ios-sdk/CoreOnly/Sources/module.
modulemap:1:8: note: previously defined here
module Firebase {
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:
import UIKit
import Flutter
import Firebase
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
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
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).
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...
public struct ImagePicker: UIViewControllerRepresentable {
// Vars and setup stuff...
@Environment(\.presentationMode) var presentationMode
let viewStore: ViewStore
public init(store: Store) {
self.viewStore = ViewStore(store)
}
// UIViewControllerRepresentable required functions
public func makeUIViewController(context: UIViewControllerRepresentableContext) -> some UIViewController {
// Configuring the PHPickerViewController
var config = PHPickerConfiguration()
config.filter = PHPickerFilter.images
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}
public func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
public func makeCoordinator() -> Coordinator {
Coordinator(self)
}
// This is the coordinator that acts as the delegate
public class Coordinator: PHPickerViewControllerDelegate {
let parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
guard let itemProvider = results.first?.itemProvider,
itemProvider.canLoadObject(ofClass: UIImage.self) else {
return
}
itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
if let image = image as? UIImage {
DispatchQueue.main.async {
self?.parent.viewStore.send(.imagePicked(image: image))
}
}
}
}
}
}
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).
// These happen on immediately presenting the ImagePicker
AppName[587:30596] [Picker] Showing picker unavailable UI (reason: still loading) with error: (null)
AppName[587:30596] Writing analzed variants.
// These happen when tapping the search bar
AppName[587:30867] [lifecycle] [u A95D90FC-C77B-43CC-8FC6-C8E7C81DD22A:m (null)] [com.apple.mobileslideshow.photospicker(1.0)] Connection to plugin interrupted while in use.
AppName[587:31002] [lifecycle] [u A95D90FC-C77B-43CC-8FC6-C8E7C81DD22A:m (null)] [com.apple.mobileslideshow.photospicker(1.0)] Connection to plugin invalidated while in use.
AppName[587:30596] [Picker] Showing picker unavailable UI (reason: crashed) with error: (null)
AppName[587:30596] viewServiceDidTerminateWithError:: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted}
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.
class WrappedPhotoPicker: UIViewController {
var picker: PHPickerViewController?
override func viewDidLoad() {
super.viewDidLoad()
if let picker = picker {
present(picker, animated: false)
}
}
}
Then in the SwiftUI View I create this wrapper and set the picker in it.
struct WrappedPickerView: UIViewControllerRepresentable {
@Environment(\.presentationMode) var presentationMode
@Binding var photoPickerResult: PHPickerResult?
let wrappedPicker = WrappedPhotoPicker()
func makeUIViewController(context: Context) -> WrappedPhotoPicker {
var config = PHPickerConfiguration()
config.filter = .images
config.selectionLimit = 1
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
wrappedPicker.picker = picker
return wrappedPicker
}
func updateUIViewController(_ uiViewController: WrappedPhotoPicker, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: PHPickerViewControllerDelegate {
let parent: WrappedPickerView
init(_ parent: WrappedPickerView) {
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
parent.presentationMode.wrappedValue.dismiss()
parent.wrappedPicker.dismiss(animated: false)
parent.photoPickerResult = results.first
}
}
}
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
When I run npm ci
on Github Actions I got the error:
Run npm ci
npm ERR! bindings not accessible from watchpack-chokidar2:fsevents
npm ERR! A complete log of this run can be found in:
npm ERR! /home/runner/.npm/_logs/2021-09-17T15_18_42_465Z-debug.log
Error: Process completed with exit code 1.
What can be?
My .github/workflows/eslint.yaml
name: ESLint
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: '14.x'
- run: npm ci
- run: npm run lint
My package.json
{
"name": "@blinktrade/uikit",
"version": "1.0.0",
"main": "dist/index.js",
"license": "MIT",
"devDependencies": {
"@babel/plugin-transform-react-jsx": "^7.14.9",
"@babel/preset-env": "^7.15.6",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@storybook/addon-essentials": "^6.3.8",
"@storybook/react": "^6.3.8",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.1.0",
"@testing-library/user-event": "^13.2.1",
"@types/jest": "^27.0.1",
"@types/react": "^17.0.21",
"@typescript-eslint/eslint-plugin": "^4.31.1",
"@typescript-eslint/parser": "^4.31.1",
"eslint": "^7.32.0",
"eslint-plugin-react": "^7.25.2",
"husky": "^7.0.2",
"jest": "^27.2.0",
"prettier": "^2.4.1",
"pretty-quick": "^3.1.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rimraf": "^3.0.2",
"typescript": "^4.4.3"
},
"husky": {
"hooks": {
"pre-push": "npm run lint",
"pre-commit": "pretty-quick --staged"
}
},
"scripts": {
"build": "tsc -p .",
"clear": "rimraf dist/",
"format": "prettier '**/*' --write --ignore-unknown",
"lint": "eslint --max-warnings=0 .",
"storybook": "start-storybook -p 4000",
"test": "jest"
}
}
ANSWER
Answered 2021-Sep-20 at 20:57Solved removing packages-lock.json and running again using NodeJS 14 (was 10)
QUESTION
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: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.
ScrollView(.horizontal) { scrollProxy in
ForEach(sections) { section in
Text(section.text)
.scrollId(section.id)
}
.onChange(of: index) {
scrollProxy.scrollTo(
sections[index].id,
alignment: .center
)
}
}
The package adds a convenience init to ScrollView to give access to the scrollProxy.
QUESTION
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:
import UIKit
import Foundation
class SmileView: UIView {
override func draw(_ rect: CGRect) {
super.draw(rect)
let smile = ":)" as NSString
smile.draw(in: rect, withAttributes: nil)
}
}
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
kotlin {
android()
ios {
binaries {
framework {
baseName = "shared"
}
}
compilations.getByName("main") {
val uikit by cinterops.creating {
}
}
}
2. Add a `src/nativeinterop/cinterop/uikit.def` file.
package = demo.cinterop
language = Objective-C
---
#import
#import
@protocol UIViewWithOverrides
- (void) drawRect:(CGRect)aRect;
- (void) layoutSubviews;
@end
3. Create a custom UIView class
The class extends the UIView from UIKit and implements the previously created UIViewWithOverridesProtocol (the suffix is automatically added)
package demo
import demo.cinterop.UIViewWithOverridesProtocol
import kotlinx.cinterop.*
import platform.CoreGraphics.*
import platform.UIKit.*
@ExportObjCClass
class MyView() : UIView(frame = CGRectMake(.0, .0, .0, .0)), UIViewWithOverridesProtocol {
override fun layoutSubviews() {
println("layoutSubviews")
setNeedsDisplay()
}
override fun drawRect(aRect: CValue) {
val rectAsString = aRect.useContents {
"" + this.origin.x + ", " + this.origin.y + ", " + (this.origin.x +this.size.width) + ", " + (this.origin.y +this.size.height)
}
println("drawRect:: Rect[$rectAsString]")
val context: CPointer? = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(context, 2.0)
val components = cValuesOf(0.0, 0.0, 1.0, 1.0)
CGContextSetFillColor(context, components)
val square = CGRectMake(100.0, 100.0, 200.0, 200.0)
CGContextFillRect(context, square)
}
}
fun createMyView(): UIView = MyView()
4. Use it from Swift
struct ChartView: View {
var body: some View {
VStack {
Text("Chart View")
MyView()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}
}
}
struct ChartView_Previews: PreviewProvider {
static var previews: some View {
ChartView()
}
}
struct MyView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
UIChartViewKt.createMyView()
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
QUESTION
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.
Here is some sample code to replicate the issue.
import UIKit
import PlaygroundSupport
import CoreGraphics
class MyViewController : UIViewController {
var pixelPointer: UnsafeMutablePointer!
let imageWidth = 20
let imageHeight = 20
override func loadView() {
let view = UIView()
view.backgroundColor = .blue
let viewSize: CGFloat = 150
// Test Image
pixelPointer = UnsafeMutablePointer.allocate(capacity: imageWidth * imageWidth * 4)
let ctx = CGContext(data: pixelPointer,
width: imageWidth,
height: imageHeight,
bitsPerComponent: 8,
bytesPerRow: 4 * imageWidth,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
for x in 0 ..< imageWidth {
for y in 0 ..< imageHeight {
if y == 7 || y == 8 || y == 9 {
// Red No Alpha -> Expected to be transparent
setColorFor(255, g: 0, b: 0, a: 0, x: x, y: y)
} else if y == 10 || y == 11 || y == 12 {
// Black No Alpha -> Is transparent
setColorFor(0, g: 0, b: 0, a: 0, x: x, y: y)
} else {
// Red
setColorFor(255, g: 0, b: 0, a: 255, x: x, y: y)
}
}
}
let cgImage = ctx.makeImage()!
// ImageView with Clear Background
let imageViewClearBackground = UIImageView()
imageViewClearBackground.backgroundColor = .clear
imageViewClearBackground.frame.origin = CGPoint(x: 10, y: 10)
imageViewClearBackground.layer.borderColor = UIColor.black.cgColor
imageViewClearBackground.layer.borderWidth = 1
imageViewClearBackground.frame.size = CGSize(width: viewSize, height: viewSize)
imageViewClearBackground.layer.magnificationFilter = CALayerContentsFilter.nearest
imageViewClearBackground.image = UIImage(cgImage: cgImage)
view.addSubview(imageViewClearBackground)
// ImageView with White Background
let imageViewWhiteBackground = UIImageView()
imageViewWhiteBackground.layer.borderColor = UIColor.black.cgColor
imageViewWhiteBackground.layer.borderWidth = 1
imageViewWhiteBackground.backgroundColor = .white
imageViewWhiteBackground.frame.size = CGSize(width: viewSize, height: viewSize)
imageViewWhiteBackground.frame.origin = CGPoint(x: viewSize + 20, y: 10)
imageViewWhiteBackground.layer.magnificationFilter = CALayerContentsFilter.nearest
imageViewWhiteBackground.image = UIImage(cgImage: cgImage)
view.addSubview(imageViewWhiteBackground)
self.view = view
}
func setColorFor(_ r: Int, g: Int, b: Int, a: Int, x: Int, y: Int) {
let offset = (y * Int(imageWidth) * 4) + x * 4
pixelPointer[offset+0] = UInt8(r)
pixelPointer[offset+1] = UInt8(g)
pixelPointer[offset+2] = UInt8(b)
pixelPointer[offset+3] = UInt8(a)
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
ANSWER
Answered 2021-Oct-24 at 17:31The problem is that this:
setColorFor(255, g: 0, b: 0, a: 0, x: x, y: y)
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
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.
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.
//
// FullScreenTransitionManager.swift
//
import Foundation
import UIKit
// MARK: FullScreenPresentationController
final class FullScreenPresentationController: UIPresentationController {
private let backgroundView: UIView = {
let view = UIView()
view.backgroundColor = .systemBackground
view.alpha = 0
return view
}()
private lazy var tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(onTap))
@objc private func onTap(_ gesture: UITapGestureRecognizer) {
presentedViewController.dismiss(animated: true)
}
}
// MARK: UIPresentationController
extension FullScreenPresentationController {
override func presentationTransitionWillBegin() {
guard let containerView = containerView else { return }
containerView.addGestureRecognizer(tapGestureRecognizer)
containerView.addSubview(backgroundView)
backgroundView.frame = containerView.frame
guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
transitionCoordinator.animate(alongsideTransition: { context in
self.backgroundView.alpha = 1
})
}
override func presentationTransitionDidEnd(_ completed: Bool) {
if !completed {
backgroundView.removeFromSuperview()
containerView?.removeGestureRecognizer(tapGestureRecognizer)
}
}
override func dismissalTransitionWillBegin() {
guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
transitionCoordinator.animate(alongsideTransition: { context in
self.backgroundView.alpha = 0
})
}
override func dismissalTransitionDidEnd(_ completed: Bool) {
if completed {
backgroundView.removeFromSuperview()
containerView?.removeGestureRecognizer(tapGestureRecognizer)
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
guard
let containerView = containerView,
let presentedView = presentedView
else { return }
coordinator.animate(alongsideTransition: { context in
self.backgroundView.frame = containerView.frame
presentedView.frame = self.frameOfPresentedViewInContainerView
})
}
}
// MARK: FullScreenTransitionManager
final class FullScreenTransitionManager: NSObject, UIViewControllerTransitioningDelegate {
private weak var anchorView: UIView?
init(anchorView: UIView) {
self.anchorView = anchorView
}
func presentationController(forPresented presented: UIViewController,
presenting: UIViewController?,
source: UIViewController) -> UIPresentationController? {
FullScreenPresentationController(presentedViewController: presented, presenting: presenting)
}
func animationController(forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let anchorFrame = anchorView?.safeAreaLayoutGuide.layoutFrame ?? CGRect(origin: presented.view.center, size: .zero)
return FullScreenAnimationController(animationType: .present,
anchorFrame: anchorFrame)
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let anchorFrame = anchorView?.safeAreaLayoutGuide.layoutFrame ?? CGRect(origin: dismissed.view.center, size: .zero)
return FullScreenAnimationController(animationType: .dismiss,
anchorFrame: anchorFrame)
}
}
// MARK: UIViewControllerAnimatedTransitioning
final class FullScreenAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
enum AnimationType {
case present
case dismiss
}
private let animationType: AnimationType
private let anchorFrame: CGRect
private let animationDuration: TimeInterval
private var propertyAnimator: UIViewPropertyAnimator?
init(animationType: AnimationType, anchorFrame: CGRect, animationDuration: TimeInterval = 0.3) {
self.animationType = animationType
self.anchorFrame = anchorFrame
self.animationDuration = animationDuration
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
animationDuration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
switch animationType {
case .present:
guard
let toViewController = transitionContext.viewController(forKey: .to)
else {
return transitionContext.completeTransition(false)
}
transitionContext.containerView.addSubview(toViewController.view)
propertyAnimator = presentAnimator(with: transitionContext, animating: toViewController)
case .dismiss:
guard
let fromViewController = transitionContext.viewController(forKey: .from)
else {
return transitionContext.completeTransition(false)
}
propertyAnimator = dismissAnimator(with: transitionContext, animating: fromViewController)
}
}
private func presentAnimator(with transitionContext: UIViewControllerContextTransitioning,
animating viewController: UIViewController) -> UIViewPropertyAnimator {
let finalFrame = transitionContext.finalFrame(for: viewController)
let safeAreaInsets = transitionContext.containerView.safeAreaInsets
let safeAreaCompensation = UIEdgeInsets(top: -safeAreaInsets.top,
left: -safeAreaInsets.left,
bottom: -safeAreaInsets.bottom,
right: -safeAreaInsets.right)
viewController.additionalSafeAreaInsets = safeAreaCompensation
viewController.view.frame = anchorFrame
viewController.view.setNeedsLayout()
viewController.view.layoutIfNeeded()
return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.curveEaseInOut, .layoutSubviews], animations: {
viewController.additionalSafeAreaInsets = .zero
viewController.view.frame = finalFrame
viewController.view.setNeedsLayout()
viewController.view.layoutIfNeeded()
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
private func dismissAnimator(with transitionContext: UIViewControllerContextTransitioning,
animating viewController: UIViewController) -> UIViewPropertyAnimator {
let finalFrame = anchorFrame
let safeAreaInsets = transitionContext.containerView.safeAreaInsets
let safeAreaCompensation = UIEdgeInsets(top: -safeAreaInsets.top,
left: -safeAreaInsets.left,
bottom: -safeAreaInsets.bottom,
right: -safeAreaInsets.right)
viewController.view.setNeedsLayout()
viewController.view.layoutIfNeeded()
return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.curveEaseInOut, .layoutSubviews], animations: {
viewController.additionalSafeAreaInsets = safeAreaCompensation
viewController.view.frame = finalFrame
viewController.view.setNeedsLayout()
viewController.view.layoutIfNeeded()
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
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...
//
// FullScreenTransitionManager.swift
//
import Foundation
import UIKit
// MARK: FullScreenPresentationController
final class FullScreenPresentationController: UIPresentationController {
private let backgroundView: UIView = {
let view = UIView()
view.backgroundColor = .systemBackground
view.alpha = 0
return view
}()
private lazy var tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(onTap))
@objc private func onTap(_ gesture: UITapGestureRecognizer) {
presentedViewController.dismiss(animated: true)
}
}
// MARK: UIPresentationController
extension FullScreenPresentationController {
override func presentationTransitionWillBegin() {
guard let containerView = containerView else { return }
containerView.addGestureRecognizer(tapGestureRecognizer)
containerView.addSubview(backgroundView)
backgroundView.frame = containerView.frame
guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
transitionCoordinator.animate(alongsideTransition: { context in
self.backgroundView.alpha = 1
})
}
override func presentationTransitionDidEnd(_ completed: Bool) {
if !completed {
backgroundView.removeFromSuperview()
containerView?.removeGestureRecognizer(tapGestureRecognizer)
}
}
override func dismissalTransitionWillBegin() {
guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
transitionCoordinator.animate(alongsideTransition: { context in
self.backgroundView.alpha = 0
})
}
override func dismissalTransitionDidEnd(_ completed: Bool) {
if completed {
backgroundView.removeFromSuperview()
containerView?.removeGestureRecognizer(tapGestureRecognizer)
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
guard let containerView = containerView else { return }
coordinator.animate(alongsideTransition: { context in
self.backgroundView.frame = containerView.frame
})
}
}
// MARK: FullScreenTransitionManager
final class FullScreenTransitionManager: NSObject, UIViewControllerTransitioningDelegate {
fileprivate enum AnimationState {
case present
case dismiss
}
private weak var anchorView: UIView?
private var animationState: AnimationState = .present
private var animationDuration: TimeInterval = Resources.animation.duration
private var anchorViewFrame: CGRect = .zero
private var propertyAnimator: UIViewPropertyAnimator?
init(anchorView: UIView) {
self.anchorView = anchorView
}
func presentationController(forPresented presented: UIViewController,
presenting: UIViewController?,
source: UIViewController) -> UIPresentationController? {
FullScreenPresentationController(presentedViewController: presented, presenting: presenting)
}
func animationController(forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
prepare(animationState: .present)
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
// Starting the animation here, since UIKit do not update safe area insets after UIViewController.viewWillDisappear() is called
defer {
propertyAnimator = dismissAnimator(animating: dismissed)
}
return prepare(animationState: .dismiss)
}
}
// MARK: UIViewControllerAnimatedTransitioning
extension FullScreenTransitionManager: UIViewControllerAnimatedTransitioning {
private func prepare(animationState: AnimationState,
animationDuration: TimeInterval = Resources.animation.duration) -> UIViewControllerAnimatedTransitioning? {
guard let anchorView = anchorView else { return nil }
self.animationState = animationState
self.animationDuration = animationDuration
self.anchorViewFrame = anchorView.safeAreaLayoutGuide.layoutFrame
return self
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
animationDuration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
switch animationState {
case .present:
guard
let toViewController = transitionContext.viewController(forKey: .to)
else {
return transitionContext.completeTransition(false)
}
propertyAnimator = presentAnimator(with: transitionContext, animating: toViewController)
case .dismiss:
guard
let fromViewController = transitionContext.viewController(forKey: .from)
else {
return transitionContext.completeTransition(false)
}
propertyAnimator = updatedDismissAnimator(with: transitionContext, animating: fromViewController)
}
}
private func presentAnimator(with transitionContext: UIViewControllerContextTransitioning,
animating viewController: UIViewController) -> UIViewPropertyAnimator {
transitionContext.containerView.addSubview(viewController.view)
let finalFrame = transitionContext.finalFrame(for: viewController)
viewController.view.frame = anchorViewFrame
viewController.view.layoutIfNeeded()
return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: [.curveEaseInOut],
animations: {
viewController.view.frame = finalFrame
viewController.view.layoutIfNeeded()
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
private func dismissAnimator(animating viewController: UIViewController) -> UIViewPropertyAnimator {
let finalFrame = anchorViewFrame
return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: animationDuration,
delay: 0,
options: [.curveEaseInOut],
animations: {
viewController.view.frame = finalFrame
viewController.view.layoutIfNeeded()
})
}
private func updatedDismissAnimator(with transitionContext: UIViewControllerContextTransitioning,
animating viewController: UIViewController) -> UIViewPropertyAnimator {
let propertyAnimator = self.propertyAnimator ?? dismissAnimator(animating: viewController)
propertyAnimator.addCompletion({ _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
self.propertyAnimator = propertyAnimator
return propertyAnimator
}
}
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
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:
Method 'UIKit.UIApplication.Main' is obsolete: Use the overload with 'Type' instead of 'String' parameters for type safety.
This my Main.cs
file:
using UIKit;
namespace LindeGctMobileApplication.iOS
{
public class Application
{
private static void Main(string[] args)
{
UIApplication.Main(args, null, "AppDelegate"); // << warning
}
}
}
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:
UIApplication.Main(args, null, "AppDelegate");
to this:
UIApplication.Main(args, null, typeof(AppDelegate));
In that way, you explicitly inform the type of the class.
QUESTION
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:42This is fixed in the 15.2 beta.
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
Vulnerabilities
No vulnerabilities reported
Install uikit
Support
Find, review, and download reusable Libraries, Code Snippets, Cloud APIs from over 650 million Knowledge Items
Find more librariesExplore Kits - Develop, implement, customize Projects, Custom Functions and Applications with kandi kits
Save this library and start creating your kit
Share this Page