Coder Social home page Coder Social logo

fluentdarkmodekit's Introduction

Fluent Dark Mode Kit

Carthage compatible

FluentDarkModeKit was designed and developed before Apple‘s official dark mode release. It provides a mechanism to support dark mode for apps on iOS 11+ (including iOS 13).

Features

  • Dark Mode support for iOS 11+
  • Dynamic theme change within the app without restart
  • Simple API design with minimal changes to your existing code

Installation

Requirements

  • iOS 11.0+
  • Xcode 11.0+
  • Swift 5.0+
  • Git LFS

You can use Homebrew to install Git LFS:

brew install git-lfs

Carthage

To integrate FluentDarkModeKit into your Xcode project using Carthage, specify it in your Cartfile:

github "microsoft/FluentDarkModeKit"

Swift Package Manager

Click "Files -> Swift Package Manager -> Add Package Dependency..." in Xcode's menu and search "https://github.com/microsoft/FluentDarkModeKit"

Since Xcode uses its own tools to handle swifft packages, git-lfs might not be picked up if it is installed via Homebrew. Run the following command to create a symbolic link if Xcode fails to fetch the package:

ln -s /usr/local/bin/git-lfs $(xcode-select -p)/usr/bin/git-lfs

CocoaPods

To integrate FluentDarkModeKit into your Xcode project using CocoaPods, specify it in your Podfile:

pod "FluentDarkModeKit"

Get Started

How to Set Up FluentDarkModeKit

To set up FluentDarkModeKit, you need to call the following methods first. A good place to call it is inside your AppDelegate's application:didFinishLaunchingWithOptions:.

import FluentDarkModeKit

let configuration = DMEnvironmentConfiguration()
configuration.themeChangeHandler = {
  print("theme changed")
}

if #available(iOS 13.0, *) {
  configuration.windowThemeChangeHandler = { window in
    print("\(window) theme changed")
  }
  configuration.useImageAsset = false
}

DarkModeManager.setup(with: configuration)
DarkModeManager.register(with: UIApplication.shared)

There are 3 configurable properties in FluentDarkModeKit as you can see in the code sample above.

themeChangeHandler

This is a callback for app wide theme change. Defaults to nil.

It is invoked when a new theme is set via setOverrideTraitCollection(_:) or system wide theme changes (DMTraitCollection.overrideTraitCollection.userInterfaceStyle is set to .unspecific).

windowThemeChangeHandler

This is a callback for window theme change. iOS 13+. Defaults to nil.

It is invoked per window when the window's traitCollection.userInterfaceStyle changes. UIWindow's traitCollection.userInterfaceStyle can change for multiple reasons:

  1. System wide theme changes (DMTraitCollection.overrideTraitCollection.userInterfaceStyle and UIWindow's overrideUserInterfaceStyle are both .unspecific).
  2. setOverrideTraitCollection(_:) is called (UIWindow's overrideUserInterfaceStyle is .unspecific).
  3. UIWindow's overrideUserInterfaceStyle is manually modified to a different value than current mode.
  4. UIKit modifies your UIWindow's traitCollection (An example would be when an app enters background, UIKit will prepare snapshots with dark mode on/off by modifying it).

useImageAsset

It determines whether UIImageAsset is used for dynamic image. iOS 13+. Defaults to false.

Images marked with "Preserve Vector Data" must not to be used when it is set to true.

Objective-C

The equivalent Objective-C code for setting up FluentDarkModeKit is as below.

#import <FluentDarkModeKit/FluentDarkModeKit.h>
#import <FluentDarkModeKit/FluentDarkModeKit-Swift.h>

DMEnvironmentConfiguration *configuration = [[DMEnvironmentConfiguration alloc] init];
configuration.themeChangeHandler = ^{
  NSLog(@"theme changed");
};

if (@available(iOS 13.0, *)) {
  configuration.windowThemeChangeHandler = ^(UIWindow *window) {
    NSLog(@"%@ theme changed", window);
  };
  configuration.useImageAsset = NO;
}

[DarkModeManager setupWithConfiguration:configuration];
[DarkModeManager registerWithApplication:[UIApplication sharedApplication] syncImmediately:NO animated:NO];

How to Use FluentDarkModeKit

To use FluentDarkModeKit, provide a pair of colors or images instead of a single value. Simply replace existing colors/images with a pair of light and dark colors/images.

Colors

extension UIColor {
  init(_ namespace: DMNamespace, light: UIColor, dark: UIColor)
  init(_ namespace: DMNamespace, dynamicProvider: @escaping (DMTraitCollection) -> UIColor)
}

let color1 = UIColor(.dm, light: .white, dark: .black)
let color2 = UIColor(.dm) { traitCollection in
  return traitCollection.userInterfaceStyle == .dark ? UIColor.black : UIColor.white
}

Objective-C

UIColor *color1 = [UIColor dm_colorWithLightColor:[UIColor whiteColor] darkColor:[UIColor blackColor]];
UIColor *color2 = [UIColor dm_colorWithDynamicProvider:^UIColor * (DMTraitCollection *traitCollection) {
  if (traitCollection.userInterfaceStyle == DMUserInterfaceStyleDark) {
    return [UIColor blackColor];
  }
  return [UIColor whiteColor];
}];

Images

extension UIImage {
  init(_ namespace: DMNamespace, light: UIImage, dark: UIImage)
}

let lightImage = UIImage(named: "Light")!
let darkImage = UIImage(named: "Dark")!
let image = UIImage(.dm, light: lightImage, dark: darkImage)

Objective-C

UIImage *image = [UIImage dm_imageWithLightImage:lightImage darkImage:darkImage];

Change Theme

DMTraitCollection.setOverride(DMTraitCollection(userInterfaceStyle: .light), animated: true) // Change to light theme with animation
DMTraitCollection.setOverride(DMTraitCollection(userInterfaceStyle: .unspecified), animated: true) // Change to "follow system" theme

Objective-C

[DMTraitCollection setOverrideTraitCollection:[DMTraitCollection traitCollectionWithUserInterfaceStyle:DMUserInterfaceStyleLight] animated:YES]; // Change to light theme with animation
[DMTraitCollection setOverrideTraitCollection:[DMTraitCollection traitCollectionWithUserInterfaceStyle:DMUserInterfaceStyleUnspecified] animated:YES]; // Change to "follow system" theme

Others

For more complex scenarios, FluentDarkModeKit is similar to what Apple does in iOS 13 (some slight differences). It maintains a global DMTraitCollection which can be accessed through DMTraitCollection.current during custom layout.

For more information on cases you should handle during layout, please refer to Apple's documentation.

FluentDarkModeKit will notify views or view controllers in the current window when the theme changes by calling the following delegate method.

Note that on iOS 13 and later, certain UIKit classes do not call UIView's implementation of traitCollectionDidChange(_:), thus dmTraitCollectionDidChange(_:) will not be invoked.

The (incomprehensive) list includes UIImageView, UILabel, UIDatePicker, UIPickerView, UISplitViewController, and their subclasses. See issue 104 .

protocol DMTraitEnvironment: NSObjectProtocol {
  func dmTraitCollectionDidChange(_ previousTraitCollection: DMTraitCollection?)
}

Migration

If you are using a pre 0.5.2 version and wants to migrate to the new version, here are the changes that you should pay attention to:

  1. On iOS 13, the latest version of FluentDarkModeKit uses iOS 13's API for dynamic color and theme change.
  2. There are some API changes, see How to Set Up FluentDarkModeKit and Change Theme.

Carefully test your app after updating FluentDarkModeKit and fix compile errors/warnings.

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.

When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

License

Copyright (c) Microsoft Corporation. All rights reserved.

Licensed under the MIT license.

fluentdarkmodekit's People

Contributors

abdullahselek avatar alex7vecher avatar corbinreynolds avatar icodesign avatar imwildcat avatar levinli303 avatar microsoft-github-operations[bot] avatar microsoftopensource avatar petrguan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fluentdarkmodekit's Issues

How to use ?

There is no change

FluentDarkModeKit~> 0.5.2 | iOS14 | Xcode 12.0

1.Xcode  ->  New project

2.application didFinishLaunchingWithOptions  ->  DarkModeManager.setup()

3.ViewController  ->  view.backgroundColor = UIColor(.dm, light: .red, dark: .blue)

but your demo is good

Remove git-lfs?

I don't think enable git-lfs for an image which will almost never change and let users do some extra setups is worth.

How about just remove git-lfs?

cc @icodesign

Migrate to system API on iOS 13

  • Use system dynamic color on iOS 13
  • Bridge UIView traitCollectionDidChange(_:)
  • Remove swizzling codes on iOS 13, swizzling only for traitCollectionDidChange(_:)
  • Remove dm_updateDynamicColors & dm_updateDynamicImages, existing code should be moved to dmTraitCollectionDidChange:
  • Bridge UIViewController traitCollectionDidChange(_:)
  • Fix failing tests
  • Adopt theme in newly created windows #81
  • Mark DMTraitCollection.current as deprecated #82
  • dmTraitCollection property requirement for DMTraitEnvironment #84
  • Make swizzling methods private #88
  • UIImage related methods #82
  • Update README.md #92

Maybe should not implemented

  • A new place to modify current theme
  • Bridge classes that does not call super.traitCollectionDidChange(_:)

The first 6 is fixed in #78

Dark mode in HTML

Hello,
Does this library contains the methods to parse a html/css string to support Dark Mode (used in Outlook to switch emails themes even if they are not fitted for dark mode) ?
Thank you !

Refine Color's API

What's UIKit Have Now

@interface UIColor (DynamicColors)

/* Create a dynamic color with a provider.
 * When methods are called on this color that need color component values,
 * the provider is called with UITraitCollection.currentTraitCollection.
 * The provider should use that trait collection to decide a more fundamental UIColor to return.
 * As much as possible, use the given trait collection to make that decision, not other state.
 */

+ (UIColor *)colorWithDynamicProvider:(UIColor * (^)(UITraitCollection *traitCollection))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
- (UIColor *)initWithDynamicProvider:(UIColor * (^)(UITraitCollection *traitCollection))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

/* Resolve any color to its most fundamental form (a non-dynamic color) for a specific trait collection.
 */
- (UIColor *)resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

@end

Drafts for Our API

@interface UIColor (DarkModeKit)

+ (UIColor *)dm_colorWithDynamicProvider:(UIColor * (^)(DMTraitCollection *traitCollection))dynamicProvider;

- (UIColor *)dm_resolvedColorWithTraitCollection:(DMTraitCollection *)traitCollection;

@end
extension UIColor {
    init(_: DMNamespace, dynamicProvider: (DMTraitCollection) -> UIColor)
}

What is DMNamespace

Inspired by PromiseKit, you can use almost same name to cover a already exists method:

// Call UIKit color initializer
let color = UIColor { traitCollection in
    // blah blah
}

// Call DarkModeKit initializer
let color = UIColor(.dm) { traitCollection in
    // blah blah
}

Bug:objc_retain + 16

切换模式的时候遇到crash:
0 libobjc.A.dylib 0x0000000194dfc020 objc_retain + 16
1 UIKitCore 0x00000001995bfaf4 -[UIView+ 15215348 (Hierarchy) _setBackgroundColor:] + 316
2 FDBaseUI 0x000000010661b724 0x1065fc000 + 128804
3 FDADSDK 0x0000000109f75210 0x109f38000 + 250384
4 FDADSDK 0x0000000109f742b0 0x109f38000 + 246448
5 FDADSDK 0x0000000109f72c90 0x109f38000 + 240784
6 FDADSDK 0x0000000109f47a24 0x109f38000 + 64036
7 FDADSDK 0x0000000109fd6f38 0x109f38000 + 651064
8 FDADSDK 0x0000000109fd69e8 0x109f38000 + 649704
9 FDADSDK 0x0000000109fd99a8 0x109f38000 + 661928
10 FDADSDK 0x0000000109f441c0 0x109f38000 + 49600
11 FDADSDK 0x0000000109f43d68 0x109f38000 + 48488
12 FDADSDK 0x0000000109f43b1c 0x109f38000 + 47900
13 FDADSDK 0x0000000109f48a4c 0x109f38000 + 68172
14 FDADSDK 0x0000000109f6a0bc 0x109f38000 + 204988
15 libdispatch.dylib 0x0000000194d86610 _dispatch_call_block_and_release + 24
16 libdispatch.dylib 0x0000000194d87184 _dispatch_client_callout + 16
17 libdispatch.dylib 0x0000000194d39190 _dispatch_main_queue_callback_4CF$VARIANT$mp + 1044
18 CoreFoundation 0x00000001950385e4 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 12
19 CoreFoundation 0x00000001950335d8 __CFRunLoopRun + 2004
20 CoreFoundation 0x0000000195032adc CFRunLoopRunSpecific + 464
21 GraphicsServices 0x000000019efd3328 GSEventRunModal + 104
22 UIKitCore 0x000000019914063c UIApplicationMain + 1936
23 readerFree 0x0000000105378a88 main + 3771016 (main.m:14)
24 libdyld.dylib 0x0000000194ebc360 start + 4

都是挂在了:static void (*dm_original_setBackgroundColor)(UIView *, SEL, UIColor *);
看这样子是说当前要修改颜色的view已经销毁了,请问又遇到这种情况吗?

Project does not build once added.

I get the following error when building my app. In the file DarkModeCore/UIColor+DarkModeKit.h
#import <FluentDarkModeKit/DMNamespace.h> is not found.

Screen Shot 2020-06-06 at 2 59 00 PM

DMDynamicImageProxy may crash when proxying resizable Image

(UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets resizingMode:(UIImageResizingMode)resizingMode returns _UIResizableImage, and when using DMDynamicImageProxy as _UIResizableImage in a UIButton, it tries to access to image's ivar which is not aligned with DMDynamicImageProxy class

Tabbar can not change

Click on the second item and refresh the theme. The bottom tabbar will not change.

Namespace style

Sorry, this may not be a wrong.

r.swift uses .rx to distinguish namespaces

Why didn’t you use the same method, for compatibility with Objective-C?

Usability in Objective-C only Projects

The DarkModeManager related APIs are Swift only and cannot be called from Objective-C. Thus an Objective-C user cannot properly set up the environment and is only allowed to create UIColor/UIImage which are part of DarkModeCore.

Although you should be able to achieve it by adding the modifier @objc, the issue/dilemma about hiding swizzling method makes me feel like maybe it is more sane to move the Swift code to Objective-C instead.

Supporting Multiple Color Schemes

G'day folks!

Congratulations on an absolutely brilliant library! I've been curious for a while about how easy building a third party theming system that integrates with UIKit would be, and it looks like this is the most elegant way out there.

One extra thing I was curious about was the concept of being able to have multiple color themes on top of light mode and dark. For example, Twitter for iOS offers both a dark blue, and a pure black theme, offering the user more choices for customisability, and apps like Tweetbot and Apollo for Reddit support upwards of 5 or 6 completely different color schemes.

I noticed there was an issue for making the color API more flexible at one point. Is that sort of functionality something you're interested in adding?

If it isn't, is it something you'd be interested in receiving a PR for?

Memory

When I use like this,everytime i went into secondController, the app memory has grow and will not release :

`#import <FluentDarkModeKit/UIImage+DarkModeKit.h>
@interface SecondViewController ()
@Property (nonatomic, strong) UIImageView *imageV;
@EnD

@implementation SecondViewController

  • (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    UIImage *lightImage = [UIImage imageNamed:@"ad_owner_header"];

    UIImage *darkImage = [[self class] image:lightImage byApplyingAlpha:0.5];

    UIImage *image = [UIImage dm_imageWithLightImage:lightImage darkImage:darkImage];

    self.imageV.image = image;
    [self.view addSubview:self.imageV];
    }

  • (UIImage *)image:(UIImage *)image byApplyingAlpha:(CGFloat) alpha {

    UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0f);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
    CGContextScaleCTM(ctx, 1, -1);
    CGContextTranslateCTM(ctx, 0, -rect.size.height);
    CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
    CGContextSetAlpha(ctx, alpha);
    CGContextDrawImage(ctx, rect, image.CGImage);
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
    }

  • (UIImageView *)imageV {
    if (!_imageV) {
    _imageV = [[UIImageView alloc] initWithFrame:CGRectMake(100, 200, 40, 20)];
    }
    return _imageV;
    }`

Rename themeDidChange()

Since we don't have a concept called theme now, we should rename themeDidChange.

@interface DMTraitEnvironment <NSObject>

- (void)dm_traitCollectionDidChange:(DMTraitCollection *)previousTraitCollection;

- (void)dmTraitCollectionDidChange:(DMTraitCollection *)previousTraitCollection;

@end

The minimum supported version.

I adjusted the version and found that UIViewPropertyAnimator requires iOS10 or higher. Why not support iOS10? Is there something I didn't find?

Bundle SwiftLint inside the project?

Currently the project replies on SwiftLint within our own development environment. There're some issues around it:

  1. You need install SwiftLint before writing any code. Some people may not know how it works and it will cost some time.
  2. Not all developers are using same version of SwiftLint which could potentially cause conflicts.

I'm wondering if we could bundle SwiftLint inside our project via CocoaPods, Carthage or just a binary?

Does it support layer?

I use this

  view.layer.borderColor = UIColor (.dm, light color: .red, dark color: .green).cgColor

But there is no response.

Use image extension can be crash!

when I try use UImage-dark in the demo, I receive a crash err. next the code

let lightImage = UIImage(named: "light")!
let darkImage = UIImage(named: "dark")!
let image = UIImage(.dm, light: lightImage, dark: darkImage)

can't find image ,the crash err is:

'+[UIImage dm_namespace:dm_imageWithLightImage:darkImage:]: unrecognized selector sent to class 0x7fff898bd368'

WidgetExtension #error unsupported Swift architecture

I have a widget extension. And my podfile like this:

platform :ios,'11.0'

use_frameworks!

target 'A' do

    pod 'FluentDarkModeKit'

end

target 'AWidgetExtension' do

end

When I use my Canvas for my widget, there is such a mistake.

FluentDarkModeKit.framework/Headers/FluentDarkModeKit-Swift.h:351:2: error: unsupported Swift architecture
#error unsupported Swift architecture

Does anyone have any idea?

Can not install

platform :ios, '10.0'
source 'https://github.com/CocoaPods/Specs.git'
#use_frameworks!

# ignore all warnings from all pods
inhibit_all_warnings!

target 'darkTest' do
    pod "FluentDarkModeKit"
end

Podfile like this. But when I input pod install, result like this

Analyzing dependencies
[!] Unable to find a specification for `FluentDarkModeKit`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.