Skip to content

Offer every of your objects a night version (or other theme)

License

Notifications You must be signed in to change notification settings

coppercash/KnightWatson

Repository files navigation

Knight Watson

Build Status CocoaPods Platform License MIT Language

Knight Watson is a theme managing library, with which an iOS app can easily add functions like Night Version. Benefiting from the runtime of ObjC, Knight Watson can add theme to an instance with simply a suffix following it. Thus, it would save you from many hours of learning unfamiliar new methods.

Demo

Demo

Installation with CocoaPods

Add following line to your Podfile.

pod 'KnightWatson'

The import the headers with one line to where you need it.

#import <KnightWatson/KnightWatson.h>

How to Use

The Most Basic

All magic starts from the suffix knw_themable, and then what left are only familiar Cocoa methods.

    // Configure the instance with values by theme
    //
    UIView
    *view = [[UIView alloc] init];
    view.knw_themable.backgroundColor = (id)@{@"daylight": UIColor.whiteColor,
                                                 @"night": UIColor.blackColor,};

    // Change the theme sometime later
    //
    KNWThemeContext.defaultThemeContext.theme = @"night";

Themed Arguments

An argument will be regarded as themed if it conforms to protocol KNWObjectArgument. Class KNWThemedArgument is provided with the library as an example. Take a look at how method knw_valueWithThemeContext: is implemented, so you can implement your own THEMED ARGUMENTS. For example, to make NSDictionary themed argument:

@interface NSDictionary (KNWObjectArgument) <KNWObjectArgument>
@end

@implementation NSDictionary (KNWObjectArgument)

- (id)knw_valueWithThemeContext:(KNWThemeContext *)context
{
    return self[context.theme];
}

@end

Arguments Consume a lot of Memory

Instances of UIImage are widely used, and may be replaced while switching between themes. Because they consume a lot of memory, we shouldn't keep images for all the themes in memory. Instead, we just keep their names. Take a look at class KNWAUIImage to understand better.

@implementation KNWAUIImage

- (instancetype)initWithImageNamesByTheme:(NSDictionary *)names
{
    if (self = [super init]) {
        _imageNamesByTheme = names;
    }
    return self;
}

- (id)knw_valueWithThemeContext:(KNWThemeContext *)context
{
    return [UIImage imageNamed:_imageNamesByTheme[context.theme]];
}

@end

Non-object Arguments

Non-object arguments are supported as well:

    UIView
    *view = [[UIView alloc] init];
    NSDictionary
    *framesByTheme =
    @{@"theme_a": [NSValue valueWithCGRect:CGRectMake(1., 1., 1., 1.)],
      @"theme_b": [NSValue valueWithCGRect:CGRectMake(2., 2., 2., 2.)],};
      
    [view
     .knw_themable
     .argAtIndex(0, framesByTheme)
     setFrame:CGRectZero];

Primitive variables (integer, bool...) and C structs boxed in NSNumber or NSValue can be automatically de-boxed. Moreover, you can also implement a class to store non-object arguments in your own way. To archieve this, make the class conform protocol KNWNonObjectArgument. Take class KNWACGColorRef as an example:

@implementation KNWACGColorRef

- (instancetype)initWithColorsByTheme:(NSDictionary *)colors {
    if (self = [super init]) {
        _colorsByTheme = colors;
    }
    return self;
}

- (void)knw_invocation:(NSInvocation *)invocation
    setArgumentAtIndex:(NSUInteger)index
      withThemeContext:(KNWThemeContext *)context
{
    CGColorRef
    color = _colorsByTheme[context.theme].CGColor;
    [invocation setArgument:&color
                    atIndex:index];
}

@end

Instance with Short Lifetime

Sometimes, we just want the value for the current theme instead of its changing with the theme. In that case, we need one more line:

    UIView
    *view = [[UIView alloc] init];
    NSDictionary
    *colorsByTheme = @{@"daylight": UIColor.whiteColor,
                       @"night": UIColor.blackColor,};
    [view
     .knw_themable
     .keepThemable(NO)
     setBackgroundColor:(id)colorsByTheme];

Or simply use the convenient suffix (notice that the suffix is replaced with knw_themed):

    view.knw_themed.backgroundColor = (id)@{@"daylight": UIColor.whiteColor,
                                            @"night": UIColor.blackColor,};

What can be a Theme?

KNWThemeContext#theme is of type id. So it can be of any class as long as your implementation for KNWObjectArgument (or KNWNonObjectArgument) can handle it. For example, if instances of NSNumber are used as themes, NSArray will be able to be passed as themed argument:

    KNWThemeContext.defaultThemeContext.theme = @1;
    
    UIButton
    *button = [[UIButton alloc] init];
    NSArray
    *colorsByTheme = @[UIColor.whiteColor, UIColor.blackColor,];
    [button.knw_themable setTitleColor:(id)colorsByTheme
                              forState:UIControlStateNormal];

Todo

  • [UIButton, UILabel, UIImageView...] support
  • [UIColor, UIImage, NSAttributedString...] support
  • Mutiple themes support
  • Custom theme type support
  • Cocoapods support
  • Non-object (primitive types, C struct) argument support
  • Custom argument implementation support
  • Dot expression support
  • Multiple threads (multiple theme contexts) support
  • Class methods support
  • Duplicate registered invocations removal
  • Notifications for theme-switching
  • Support OS X
  • Support Cathage
  • Documentation

About

Offer every of your objects a night version (or other theme)

Resources

License

Stars

Watchers

Forks

Packages

No packages published