'adjustsImageWhenDisabled' was deprecated in iOS 15.0 - ios

Hey i get this Warning in my code and want to resolve it but i find no solution:
'adjustsImageWhenDisabled' was deprecated in iOS 15.0: This property is ignored when using UIButtonConfiguration, you may customize to replicate this behavior via a configurationUpdateHandler
Does anyone have an idea how to resolve the warning?
I have no idea how to solve it.

You should set up a ConfigurationUpdateHandler where you should switch between your button states to set up your button's imageView's tintAdjustmentMode.
btn.configurationUpdateHandler = { button in
switch button.state {
case .disabled:
button.imageView?.tintAdjustmentMode = .dimmed
break
default:
button.imageView?.tintAdjustmentMode = .normal
break
}
}

Related

Why does .title(for: .normal) return nil for Plain styles in UIKit

I'm following the Apple Pie project from Apple's Develop in Swift Fundamentals book (pp. 333 - 362). There was a weird error where getting a button's title seemed to cause an exception.
"Fatal error: Unexpectedly found nil while unwrapping an Optional value"
#IBAction func letterButtonPressed(_ sender: UIButton) {
sender.isEnabled = false
let letterString = sender.title(for: .normal)! <-- offending code
let letter = Character(letterString.lowercased())
}
I figured out the fix which involved setting the button's Style to default instead of Plain. However, I don't understand why the Plain style would return nil in the first place. Any ideas?
I figured out the fix which involved setting the button's Style to default instead of Plain.
I think this is because when you set the style to "Plain", you are effectively using the UIButton.Configuration API introduced in iOS 15. You are basically doing something like this:
button.configuration = .plain()
And setting the title in the storyboard would set the title of the configuration, rather than calling setTitle(_:for:). You can think of this as:
button.configuration?.title = "..."
This is why you can't get the title using title(for:), but you should be able to get the title you set in the storyboard using button.configuration?.title.

setting bounds.height in ios 13 not working anymore

Since the iOS update to 13 my app is unfortunately behaving bad.
The containers height is controlled by the UISegmentControl element.
The hook works fine and the correct index is set.
There are 3 subcontainers:
profileDataContainerView
addValueContainerView
checkInHistoryContainerView
Unfortunately the containers within the main scrollview dont change their height.
Does anyone know the solution for this issue?
#IBAction func showComponent(_ sender: UISegmentedControl) {
showComponent(index: sender.selectedSegmentIndex)
}
private func showComponent(index: Int) {
currentContainer?.bounds.size.height = 0
currentSegmentedControllerIndex = index
switch index {
case 0:
profileDataContainerView.bounds.size.height = 308
currentContainer = profileDataContainerView
case 1:
addValueContainerView.bounds.size.height = 294
currentContainer = addValueContainerView
case 2:
if let tableView = checkInHistoryContainerView.subviews.last as? UITableView {
checkInHistoryContainerView.bounds.size.height = tableView.contentSize.height
}
currentContainer = checkInHistoryContainerView
default:
break
}
updateContentHeight()
}
You can use yourview.layer.bounds instead of yourview.bounds.
It's because it's a struct.
this will work
myView.frame = Frame(....)
this will not
myView.bounds.height = someNumber
It is hard to say for sure why your app is not working with just the code provided, but you can keep in mind a few things.
A scroll view size is adjusted automatically by its content. Try adjusting the scrollview itself, see this link: how to set scrollview content size in swift 3.0
Adjust the content size and then reload the view afterwards. Try to make sure that your app is loading all of your views after you set the new size. This link may help you with changing frame sizes: Change frame programmatically with auto layout
In general storyboards are a disaster for things like this if you aren't careful. Storyboards tend to give the illusion of success when in fact the constraints are wrong.
One final thing to check (a bug I found within my code) is that segues have changed substantially, so make sure that your segues are the correct type and you are presenting in the way you want to. Here is a link on some of the presenting changes in iOS 13:
https://medium.com/#hacknicity/view-controller-presentation-changes-in-ios-13-ac8c901ebc4e
Edit: Chris replied mentioning that you are using a struct, if thats the case then they are correct in that you are not properly changing the value.

iOS 13 disable Dark Mode changes [duplicate]

A large part of my app consists of web views to provide functionality not yet available through native implementations. The web team has no plans to implement a dark theme for the website. As such, my app will look a bit half/half with Dark Mode support on iOS 13.
Is it possible to opt out of Dark Mode support such that our app always shows light mode to match the website theme?
First, here is Apple's entry related to opting out of dark mode.
The content at this link is written for Xcode 11 & iOS 13:
Entire app via info.plist file (Xcode 12)
Use the following key in your info.plist file:
UIUserInterfaceStyle
And assign it a value of Light.
The XML for the UIUserInterfaceStyle assignment:
<key>UIUserInterfaceStyle</key>
<string>Light</string>
Apple documentation for UIUserInterfaceStyle
Entire app via info.plist in build settings (Xcode 13)
Entire app window via window property
You can set overrideUserInterfaceStyle against the app's window variable. This will apply to all views that appear within the window. This became available with iOS 13, so for apps that support previous versions, you must include an availability check.
Depending on how your project was created, this may be in the AppDelegate or SceneDelegate file.
if #available(iOS 13.0, *) {
window?.overrideUserInterfaceStyle = .light
}
Individual UIViewController or UIView
You can set overrideUserInterfaceStyle against the UIViewControllers or UIView's overrideUserInterfaceStyle variable. This became available with iOS 13, so for apps that support previous versions, you must include an availability check.
Swift
override func viewDidLoad() {
super.viewDidLoad()
// overrideUserInterfaceStyle is available with iOS 13
if #available(iOS 13.0, *) {
// Always adopt a light interface style.
overrideUserInterfaceStyle = .light
}
}
For those poor souls in Objective-C
if (#available(iOS 13.0, *)) {
self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
}
When set against the UIViewController, the view controller and its children adopt the defined mode.
When set against the UIView, the view and its children adopt the defined mode.
Apple documentation for overrideUserInterfaceStyle
Individual views via SwiftUI View
You can set preferredColorScheme to be either light or dark. The provided value will set the color scheme for the presentation.
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Light Only")
.preferredColorScheme(.light)
}
}
Apple documentation for preferredColorScheme
Credit to #Aron Nelson, #Raimundas Sakalauskas, #NSLeader and #rmaddy for improving this answer with their feedback.
According to Apple's session on "Implementing Dark Mode on iOS" (https://developer.apple.com/videos/play/wwdc2019/214/ starting at 31:13) it is possible to set overrideUserInterfaceStyle to UIUserInterfaceStyleLight or UIUserInterfaceStyleDark on any view controller or view, which will the be used in the traitCollection for any subview or view controller.
As already mentioned by SeanR, you can set UIUserInterfaceStyle to Light or Dark in your app's plist file to change this for your whole app.
If you are not using Xcode 11 or later (i,e iOS 13 or later SDK), your app has not automatically opted to support dark mode. So, there's no need to opt out of dark mode.
If you are using Xcode 11 or later, the system has automatically enabled dark mode for your app. There are two approaches to disable dark mode depending on your preference. You can disable it entirely or disable it for any specific window, view, or view controller.
Disable Dark Mode Entirely for your App
You can disable dark mode by including the UIUserInterfaceStyle key with a value as Light in your app’s Info.plist file.
This ignores the user's preference and always applies a light appearance to your app.
Disable dark mode for Window, View, or View Controller
You can force your interface to always appear in a light or dark style by setting the overrideUserInterfaceStyle property of the appropriate window, view, or view controller.
View controllers:
override func viewDidLoad() {
super.viewDidLoad()
/* view controller’s views and child view controllers
always adopt a light interface style. */
overrideUserInterfaceStyle = .light
}
Views:
// The view and all of its subviews always adopt light style.
youView.overrideUserInterfaceStyle = .light
Window:
/* Everything in the window adopts the style,
including the root view controller and all presentation controllers that
display content in that window.*/
window.overrideUserInterfaceStyle = .light
Note: Apple strongly encourages to support dark mode in your app. So,
you can only disable dark mode temporarily.
Read more here: Choosing a Specific Interface Style for Your iOS App
Xcode 12 and iOS 14 update. I have try the previous options to opt-out dark mode and this sentence in the info.plist file is not working for me:
<key>UIUserInterfaceStyle</key>
<string>Light</string>
Now it is renamed to:
<key>Appearance</key>
<string>Light</string>
This setting will block all dark mode in the full app.
EDITED:
Fixed typo thank you to #sarah
********** Easiest way for Xcode 11 and above ***********
Add this to info.plist before </dict></plist>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
I think I've found the solution. I initially pieced it together from UIUserInterfaceStyle - Information Property List and UIUserInterfaceStyle - UIKit, but have now found it actually documented at Choosing a specific interface style for your iOS app.
In your info.plist, set UIUserInterfaceStyle (User Interface Style) to 1 (UIUserInterfaceStyle.light).
EDIT: As per dorbeetle's answer, a more appropriate setting for UIUserInterfaceStyle may be Light.
The answer above works if you want to opt out the whole app. If you are working on the lib that has UI, and you don't have luxury of editing .plist, you can do it via code too.
If you are compiling against iOS 13 SDK, you can simply use following code:
Swift:
if #available(iOS 13.0, *) {
self.overrideUserInterfaceStyle = .light
}
Obj-C:
if (#available(iOS 13.0, *)) {
self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
}
HOWEVER, if you want your code to compile against iOS 12 SDK too (which right now is still the latest stable SDK), you should resort to using selectors. Code with selectors:
Swift (XCode will show warnings for this code, but that's the only way to do it for now as property does not exist in SDK 12 therefore won't compile):
if #available(iOS 13.0, *) {
if self.responds(to: Selector("overrideUserInterfaceStyle")) {
self.setValue(UIUserInterfaceStyle.light.rawValue, forKey: "overrideUserInterfaceStyle")
}
}
Obj-C:
if (#available(iOS 13.0, *)) {
if ([self respondsToSelector:NSSelectorFromString(#"overrideUserInterfaceStyle")]) {
[self setValue:#(UIUserInterfaceStyleLight) forKey:#"overrideUserInterfaceStyle"];
}
}
For the entire App: (in the info.plist file):
<key>UIUserInterfaceStyle</key>
<string>Light</string>
Window (Usually the whole app):
window!.overrideUserInterfaceStyle = .light
You can get the window from SceneDelegate
UIViewController:
viewController.overrideUserInterfaceStyle = .light
You can set any viewController, even inside the viewController itself
UIView:
view.overrideUserInterfaceStyle = .light
You can set any view, even inside the view itself
You may need to use if #available(iOS 13.0, *) { ,,, } if you are supporting earlier iOS versions.
SwiftUI View:
.preferredColorScheme(.light) <- This Modifier
or
.environment(\.colorScheme, .light) <- This Modifier
You can turn Dark Mode off in entire application in Xcode 11:
Go Info.plist
Add bellow like
<key>UIUserInterfaceStyle</key>
<string>Light</string>
Info.plist will be look like below...
Swift 5
You can download a demo project with this link:- https://github.com/rashidlatif55/DarkLightMode
Two ways to switch dark to light mode:
1- info.plist
<key>UIUserInterfaceStyle</key>
<string>Light</string>
2- Programmatically or Runtime
#IBAction private func switchToDark(_ sender: UIButton){
UIApplication.shared.windows.first?.overrideUserInterfaceStyle = .dark
}
Latest Update-
If you're using Xcode 10.x, then the default UIUserInterfaceStyle is light for iOS 13.x. When run on an iOS 13 device, it will work in Light Mode only.
No need to explicitly add the UIUserInterfaceStyle key in Info.plist file, adding it will give an error when you Validate your app, saying:
Invalid Info.plist Key. The key 'UIUserInterfaceStyle' in the Payload/AppName.appInfo.plist file is not valid.
Only add the UIUserInterfaceStyle key in Info.plist file when using Xcode 11.x.
iOS 14.3 and Xcode 12.3 Update
In info.plist file add Appearance as Light.
<key>Appearance</key>
<string>Light</string>
If you will add UIUserInterfaceStyle key to the plist file, possibly Apple will reject release build as mentioned here: https://stackoverflow.com/a/56546554/7524146
Anyway it's annoying to explicitly tell each ViewController self.overrideUserInterfaceStyle = .light. But you can use this peace of code once for your root window object:
if #available(iOS 13.0, *) {
if window.responds(to: Selector(("overrideUserInterfaceStyle"))) {
window.setValue(UIUserInterfaceStyle.light.rawValue, forKey: "overrideUserInterfaceStyle")
}
}
Just notice you can't do this inside application(application: didFinishLaunchingWithOptions:) because for this selector will not respond true at that early stage. But you can do it later on. It's super easy if you are using custom AppPresenter or AppRouter class in your app instead of starting UI in the AppDelegate automatically.
Apart from other responses, from my understanding of the following, you only need to prepare for Dark mode when compiling against iOS 13 SDK (using XCode 11).
The system assumes that apps linked against the iOS 13 or later SDK
support both light and dark appearances. In iOS, you specify the
specific appearance you want by assigning a specific interface style
to your window, view, or view controller. You can also disable support
for Dark Mode entirely using an Info.plist key.
Link
In Xcode 12, you can change add as "appearances". This will work!!
My app does not support dark mode as of now and uses a light app bar color. I was able to force the status bar content to dark text and icons by adding the following key to my Info.plist:
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDarkContent</string>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
Find the other possible values here: https://developer.apple.com/documentation/uikit/uistatusbarstyle
Flutter Users
Don't forget to set the app bar brightness attribute on your Flutter app bar like this:
AppBar(
backgroundColor: Colors.grey[100],
brightness: Brightness.light, // <---------
title: const Text('Hi there'),
),
Yes you can skip by adding the following code in viewDidLoad:
if #available(iOS 13.0, *) {
// Always adopt a light interface style.
overrideUserInterfaceStyle = .light
}
Objective-c version
if (#available(iOS 13.0, *)) {
_window.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
}
Add this to info.plist
<key>UIUserInterfaceStyle</key>
<string>Light</string>
Yes.. you can add below setting in iOS project.
In info.plist file add UIUserInterfaceStyle to Light.
If your project is in IONIC.. You can add below setting in config file
<platform name="ios">
<edit-config file="*-Info.plist" mode="merge" target="UIUserInterfaceStyle">
<string>Light</string>
</edit-config>
</platform>
Using these settings, device dark mode will not affect your app.
if #available(iOS 13.0, *) {
overrideUserInterfaceStyle = .light
} else {
// Fallback on earlier versions
}
I would use this solution since window property may be changed during the app life cycle. So assigning "overrideUserInterfaceStyle = .light" needs to be repeated. UIWindow.appearance() enables us to set default value that will be used for newly created UIWindow objects.
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 13.0, *) {
UIWindow.appearance().overrideUserInterfaceStyle = .light
}
return true
}
}
Here are a few tips and tricks which you can use in your app to support or bypass the dark mode.
First tip: To override the ViewController style
you can override the interface style of UIViewController by
1: overrideUserInterfaceStyle = .dark //For dark mode
2: overrideUserInterfaceStyle = .light //For light mode
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
overrideUserInterfaceStyle = .light
}
}
Second tip: Adding a key in info.plist
Simply you can add a new key
UIUserInterfaceStyle
in your app info.plist and set its value to Light or Dark. this will override the app default style to the value you provide.
You don't have to add overrideUserInterfaceStyle = .light this line in every viewController, just one line in info.plist that’s it.
Just simply add following key in your info.plist file :
<key>UIUserInterfaceStyle</key>
<string>Light</string>
Just add these line in info.plist file:
<key>UIUserInterfaceStyle</key>
<string>light</string>
This will force app to run in light mode only.
add overrideUserInterfaceStyle = .light in ViewController.swift file or change Appearance to "light" in info.plist file
import UIKit
extension UIViewController {
override open func awakeFromNib() {
super.awakeFromNib()
if #available(iOS 13.0, *) {
overrideUserInterfaceStyle = .light
}
}
}
You can do: add this new key UIUserInterfaceStyle to Info.plist and set its value to Light. and check alert controller appears with light mode.
UIUserInterfaceStyle
Light
If you are force light/dark mode in your whole application regardless of the user's settings by adding the key UIUserInterfaceStyle to your Info.plist file and setting its value to either Light or Dark.
This question has so many answers, rather using it in info.plist you can set it in AppDelegate like this:
#if compiler(>=5.1)
if #available(iOS 13.0, *) {
self.window?.overrideUserInterfaceStyle = .light
}
#endif
Test on Xcode 11.3, iOS 13.3
Actually I just wrote some code that will allow you to globally opt out of dark mode in code without having to putz with every single viw controller in your application. This can probably be refined to opt out on a class by class basis by managing a list of classes. For me, what I want is for my users to see if they like the dark mode interface for my app, and if they don't like it, they can turn it off. This will allow them to continue using dark mode for the rest of their applications.
User choice is good (Ahem, looking at you Apple, this is how you should have implemented it).
So how this works is that it's just a category of UIViewController. When it loads it replaces the native viewDidLoad method with one that will check a global flag to see if dark mode is disabled for everything or not.
Because it is triggered on UIViewController loading it should automatically start up and disable dark mode by default. If this is not what you want, then you need to get in there somewhere early and set the flag, or else just set the default flag.
I haven't yet written anything to respond to the user turning the flag on or off. So this is basically example code. If we want the user to interact with this, all the view controllers will need to reload. I don't know how to do that offhand but probably sending some notification is going to do the trick. So right now, this global on/off for dark mode is only going to work at startup or restart of the app.
Now, it's not just enough to try to turn off dark mode in every single MFING viewController in your huge app. If you're using color assets you are completely boned. We for 10+ years have understood immutable objects to be immutable. Colors you get from the color asset catalog say they are UIColor but they are dynamic (mutable) colors and will change underneath you as the system changes from dark to light mode. That is supposed to be a feature. But of course there is no master toggle to ask these things to stop making this change (as far as I know right now, maybe someone can improve this).
So the solution is in two parts:
a public category on UIViewController that gives some utility and convenience methods... for instance I don't think apple has thought about the fact that some of us mix in web code into our apps. As such we have stylesheets that need to be toggled based on dark or light mode. Thus, you either need to build some kind of a dynamic stylesheet object (which would be good) or just ask what the current state is (bad but easy).
this category when it loads will replace the viewDidLoad method of the UIViewController class and intercept calls. I don't know if that breaks app store rules. If it does, there are other ways around that probably but you can consider it a proof of concept. You can for instance make one subclass of all the main view controller types and make all of your own view controllers inherit from those, and then you can use the DarkMode category idea and call into it to force opt out all of your view controllers. It is uglier but it is not going to break any rules. I prefer using the runtime because that's what the runtime was made to do. So in my version you just add the category, you set a global variable on the category for whether or not you want it to block dark mode, and it will do it.
You are not out of the woods yet, as mentioned, the other problem is UIColor basically doing whatever the hell it wants. So even if your view controllers are blocking dark mode UIColor doesn't know where or how you're using it so can't adapt. As a result you can fetch it correctly but then it's going to revert on you at some point in the future. Maybe soon maybe later. So the way around that is by allocating it twice using a CGColor and turning it into a static color. This means if your user goes back and re-enables dark mode on your settings page (the idea here is to make this work so that the user has control over your app over and above the rest of the system), all of those static colors need replacing. So far this is left for someone else to solve. The easy ass way to do it is to make a default that you're opting out of dark mode, divide by zero to crash the app since you can't exit it and tell the user to just restart it. That probably violates app store guidelines as well but it's an idea.
The UIColor category doesn't need to be exposed, it just works calling colorNamed: ... if you didn't tell the DarkMode ViewController class to block dark mode, it will work perfectly nicely as expected. Trying to make something elegant instead of the standard apple sphaghetti code which is going to mean you're going to have to modify most of your app if you want to programatically opt out of dark mode or toggle it. Now I don't know if there is a better way of programatically altering the Info.plist to turn off dark mode as needed. As far as my understanding goes that's a compile time feature and after that you're boned.
So here is the code you need. Should be drop in and just use the one method to set the UI Style or set the default in the code. You are free to use, modify, do whatever you want with this for any purpose and no warranty is given and I don't know if it will pass the app store. Improvements very welcome.
Fair warning I don't use ARC or any other handholding methods.
////// H file
#import <UIKit/UIKit.h>
#interface UIViewController(DarkMode)
// if you want to globally opt out of dark mode you call these before any view controllers load
// at the moment they will only take effect for future loaded view controllers, rather than currently
// loaded view controllers
// we are doing it like this so you don't have to fill your code with #availables() when you include this
typedef enum {
QOverrideUserInterfaceStyleUnspecified,
QOverrideUserInterfaceStyleLight,
QOverrideUserInterfaceStyleDark,
} QOverrideUserInterfaceStyle;
// the opposite condition is light interface mode
+ (void)setOverrideUserInterfaceMode:(QOverrideUserInterfaceStyle)override;
+ (QOverrideUserInterfaceStyle)overrideUserInterfaceMode;
// utility methods
// this will tell you if any particular view controller is operating in dark mode
- (BOOL)isUsingDarkInterfaceStyle;
// this will tell you if any particular view controller is operating in light mode mode
- (BOOL)isUsingLightInterfaceStyle;
// this is called automatically during all view controller loads to enforce a single style
- (void)tryToOverrideUserInterfaceStyle;
#end
////// M file
//
// QDarkMode.m
#import "UIViewController+DarkMode.h"
#import "q-runtime.h"
#implementation UIViewController(DarkMode)
typedef void (*void_method_imp_t) (id self, SEL cmd);
static void_method_imp_t _nativeViewDidLoad = NULL;
// we can't #available here because we're not in a method context
static long _override = -1;
+ (void)load;
{
#define DEFAULT_UI_STYLE UIUserInterfaceStyleLight
// we won't mess around with anything that is not iOS 13 dark mode capable
if (#available(iOS 13,*)) {
// default setting is to override into light style
_override = DEFAULT_UI_STYLE;
/*
This doesn't work...
NSUserDefaults *d = NSUserDefaults.standardUserDefaults;
[d setObject:#"Light" forKey:#"UIUserInterfaceStyle"];
id uiStyle = [d objectForKey:#"UIUserInterfaceStyle"];
NSLog(#"%#",uiStyle);
*/
if (!_nativeViewDidLoad) {
Class targetClass = UIViewController.class;
SEL targetSelector = #selector(viewDidLoad);
SEL replacementSelector = #selector(_overrideModeViewDidLoad);
_nativeViewDidLoad = (void_method_imp_t)QMethodImplementationForSEL(targetClass,targetSelector);
QInstanceMethodOverrideFromClass(targetClass, targetSelector, targetClass, replacementSelector);
}
}
}
// we do it like this because it's not going to be set often, and it will be tested often
// so we can cache the value that we want to hand to the OS
+ (void)setOverrideUserInterfaceMode:(QOverrideUserInterfaceStyle)style;
{
if (#available(iOS 13,*)){
switch(style) {
case QOverrideUserInterfaceStyleLight: {
_override = UIUserInterfaceStyleLight;
} break;
case QOverrideUserInterfaceStyleDark: {
_override = UIUserInterfaceStyleDark;
} break;
default:
/* FALLTHROUGH - more modes can go here*/
case QOverrideUserInterfaceStyleUnspecified: {
_override = UIUserInterfaceStyleUnspecified;
} break;
}
}
}
+ (QOverrideUserInterfaceStyle)overrideUserInterfaceMode;
{
if (#available(iOS 13,*)){
switch(_override) {
case UIUserInterfaceStyleLight: {
return QOverrideUserInterfaceStyleLight;
} break;
case UIUserInterfaceStyleDark: {
return QOverrideUserInterfaceStyleDark;
} break;
default:
/* FALLTHROUGH */
case UIUserInterfaceStyleUnspecified: {
return QOverrideUserInterfaceStyleUnspecified;
} break;
}
} else {
// we can't override anything below iOS 12
return QOverrideUserInterfaceStyleUnspecified;
}
}
- (BOOL)isUsingDarkInterfaceStyle;
{
if (#available(iOS 13,*)) {
if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark){
return YES;
}
}
return NO;
}
- (BOOL)isUsingLightInterfaceStyle;
{
if (#available(iOS 13,*)) {
if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleLight){
return YES;
}
// if it's unspecified we should probably assume light mode, esp. iOS 12
}
return YES;
}
- (void)tryToOverrideUserInterfaceStyle;
{
// we have to check again or the compile will bitch
if (#available(iOS 13,*)) {
[self setOverrideUserInterfaceStyle:(UIUserInterfaceStyle)_override];
}
}
// this method will be called via the viewDidLoad chain as we will patch it into the
// UIViewController class
- (void)_overrideModeViewDidLoad;
{
if (_nativeViewDidLoad) {
_nativeViewDidLoad(self,#selector(viewDidLoad));
}
[self tryToOverrideUserInterfaceStyle];
}
#end
// keep this in the same file, hidden away as it needs to switch on the global ... yeah global variables, I know, but viewDidLoad and colorNamed: are going to get called a ton and already it's adding some inefficiency to an already inefficient system ... you can change if you want to make it a class variable.
// this is necessary because UIColor will also check the current trait collection when using asset catalogs
// so we need to repair colorNamed: and possibly other methods
#interface UIColor(DarkMode)
#end
#implementation UIColor (DarkMode)
typedef UIColor *(*color_method_imp_t) (id self, SEL cmd, NSString *name);
static color_method_imp_t _nativeColorNamed = NULL;
+ (void)load;
{
// we won't mess around with anything that is not iOS 13 dark mode capable
if (#available(iOS 13,*)) {
// default setting is to override into light style
if (!_nativeColorNamed) {
// we need to call it once to force the color assets to load
Class targetClass = UIColor.class;
SEL targetSelector = #selector(colorNamed:);
SEL replacementSelector = #selector(_overrideColorNamed:);
_nativeColorNamed = (color_method_imp_t)QClassMethodImplementationForSEL(targetClass,targetSelector);
QClassMethodOverrideFromClass(targetClass, targetSelector, targetClass, replacementSelector);
}
}
}
// basically the colors you get
// out of colorNamed: are dynamic colors... as the system traits change underneath you, the UIColor object you
// have will also change since we can't force override the system traits all we can do is force the UIColor
// that's requested to be allocated out of the trait collection, and then stripped of the dynamic info
// unfortunately that means that all colors throughout the app will be static and that is either a bug or
// a good thing since they won't respond to the system going in and out of dark mode
+ (UIColor *)_overrideColorNamed:(NSString *)string;
{
UIColor *value = nil;
if (#available(iOS 13,*)) {
value = _nativeColorNamed(self,#selector(colorNamed:),string);
if (_override != UIUserInterfaceStyleUnspecified) {
// the value we have is a dynamic color... we need to resolve against a chosen trait collection
UITraitCollection *tc = [UITraitCollection traitCollectionWithUserInterfaceStyle:_override];
value = [value resolvedColorWithTraitCollection:tc];
}
} else {
// this is unreachable code since the method won't get patched in below iOS 13, so this
// is left blank on purpose
}
return value;
}
#end
There is a set of utility functions that this uses for doing method swapping. Separate file. This is standard stuff though and you can find similar code anywhere.
// q-runtime.h
#import <Foundation/Foundation.h>
#import <objc/message.h>
#import <stdatomic.h>
// returns the method implementation for the selector
extern IMP
QMethodImplementationForSEL(Class aClass, SEL aSelector);
// as above but gets class method
extern IMP
QClassMethodImplementationForSEL(Class aClass, SEL aSelector);
extern BOOL
QClassMethodOverrideFromClass(Class targetClass, SEL targetSelector,
Class replacementClass, SEL replacementSelector);
extern BOOL
QInstanceMethodOverrideFromClass(Class targetClass, SEL targetSelector,
Class replacementClass, SEL replacementSelector);
// q-runtime.m
static BOOL
_QMethodOverride(Class targetClass, SEL targetSelector, Method original, Method replacement)
{
BOOL flag = NO;
IMP imp = method_getImplementation(replacement);
// we need something to work with
if (replacement) {
// if something was sitting on the SEL already
if (original) {
flag = method_setImplementation(original, imp) ? YES : NO;
// if we're swapping, use this
//method_exchangeImplementations(om, rm);
} else {
// not sure this works with class methods...
// if it's not there we want to add it
flag = YES;
const char *types = method_getTypeEncoding(replacement);
class_addMethod(targetClass,targetSelector,imp,types);
XLog_FB(red,black,#"Not sure this works...");
}
}
return flag;
}
BOOL
QInstanceMethodOverrideFromClass(Class targetClass, SEL targetSelector,
Class replacementClass, SEL replacementSelector)
{
BOOL flag = NO;
if (targetClass && replacementClass) {
Method om = class_getInstanceMethod(targetClass,targetSelector);
Method rm = class_getInstanceMethod(replacementClass,replacementSelector);
flag = _QMethodOverride(targetClass,targetSelector,om,rm);
}
return flag;
}
BOOL
QClassMethodOverrideFromClass(Class targetClass, SEL targetSelector,
Class replacementClass, SEL replacementSelector)
{
BOOL flag = NO;
if (targetClass && replacementClass) {
Method om = class_getClassMethod(targetClass,targetSelector);
Method rm = class_getClassMethod(replacementClass,replacementSelector);
flag = _QMethodOverride(targetClass,targetSelector,om,rm);
}
return flag;
}
IMP
QMethodImplementationForSEL(Class aClass, SEL aSelector)
{
Method method = class_getInstanceMethod(aClass,aSelector);
if (method) {
return method_getImplementation(method);
} else {
return NULL;
}
}
IMP
QClassMethodImplementationForSEL(Class aClass, SEL aSelector)
{
Method method = class_getClassMethod(aClass,aSelector);
if (method) {
return method_getImplementation(method);
} else {
return NULL;
}
}
I'm copying and pasting this out of a couple of files since the q-runtime.h is my reusable library and this is just a part of it. If something doesn't compile let me know.

UICollection view Reordering cells crash ios swift

I want to reorder my cells of uicollection view. When i try, it works sometimes(with lags) but sometimes, my app got crashed.
I searched everywhere on internet but unable to find the answer till now.
func handleLongGesture(panRecognizer: UIPanGestureRecognizer){
let locationPoint: CGPoint! = panRecognizer.locationInView(collectionView)
guard let selectedIndexPath : NSIndexPath = collectionView.indexPathForItemAtPoint(locationPoint) else {
return
}
if panRecognizer.state == .Began{
collectionView.beginInteractiveMovementForItemAtIndexPath(selectedIndexPath)
indexPathSelectedItem = selectedIndexPath
}
else if panRecognizer.state == .Changed{
collectionView.updateInteractiveMovementTargetPosition(locationPoint)
}
else if panRecognizer.state == .Ended{
collectionView.endInteractiveMovement()
}
}
This is the above code which i am trying. i am not able to find the bugs in the entire code.
I want to let you know that i also tried to use breakpoints to find out where my app crashing and i found that sometimes control not able to go under the state "panRecognizer.state == .Ended" and i think that is the reason my app get crashing.
Without the crash log it's difficult to say exactly what's happening but here are a few suggestions in the meanwhile:
First:
You have a beautiful guard statement at the top of your method, I suggest you add the let locationPoint: CGPoint! = panRecognizer.locationInView(collectionView) to it. This way you don't have to force unwrap and the code will be protected against this particular crash.
Second:
When you call the endInteractiveMovement() method of your collection view, it will in turn call your delegate method collectionView:moveItemAtIndexPath:toIndexPath: to let you now that you need to update your datasource and move the item there as well.
Make sure you implemented it and moved the object in question to the right place! If not, your app will crash because the datasource is not in sync with the collectionview anymore.
Third
I suggest you use a switch statement instead of a if-else to catch all other possible states, this will give you the possibility to cancel the move action (which you're not doing right now):
switch(panRecognizer.state) {
case UIGestureRecognizerState.Began:
// Begin movement
collectionView.beginInteractiveMovementForItemAtIndexPath(selectedIndexPath)
indexPathSelectedItem = selectedIndexPath
case UIGestureRecognizerState.Changed:
// Update movement
collectionView.updateInteractiveMovementTargetPosition(locationPoint)
case UIGestureRecognizerState.Ended:
// End movement
collectionView.endInteractiveMovement()
default:
collectionView.cancelInteractiveMovement()
}
}

Can't switch state of UISwitch

I have to UISwitches in my application. At launch, they're both set to off and the second switch is disabled and should only be enabled when the first switch is aswell, meaning:
#IBAction func switchOneToggled(sender: UISwitch) {
if switchOne.on {
switchTwo.enabled = true
}
else {
switchTWo.enabled = false
}
}
My problem is, that when I enable switchOne, switchTwo gets enabled, but I can't toggle switchTwo's on/off state by touching it.
Try resetting the switches. I had a similar problem and just deleted the switch and replaced it, and giving it the same connections that is had before. It must be a bug inside Xcode, since it should be able to turn on and off without any code.
#IBAction func switchOneToggled(sender: UISwitch) {
switchTwo.enabled = switchOne.on
}
do you maybe have userinteractionenabled set to false for switchTwo?
The problem is that your switchOneToggled is called when the Switch is tapped and the state of the switch is changed. You should check the state of Switch 1 and enable your Switch 2 in your viewDidLoad.

Resources