2011-10-17 09:28:15 -07:00
|
|
|
//
|
|
|
|
// Utilities.m
|
|
|
|
// NewsBlur
|
|
|
|
//
|
|
|
|
// Created by Samuel Clay on 10/17/11.
|
|
|
|
// Copyright (c) 2011 NewsBlur. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "Utilities.h"
|
2013-06-22 19:34:12 -07:00
|
|
|
#import <CommonCrypto/CommonCrypto.h>
|
2011-10-17 09:28:15 -07:00
|
|
|
|
2012-07-23 10:57:11 -07:00
|
|
|
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor,
|
|
|
|
CGColorRef endColor) {
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
|
|
CGFloat locations[] = { 0.0, 1.0 };
|
|
|
|
|
|
|
|
NSArray *colors = [NSArray arrayWithObjects:(__bridge id)startColor, (__bridge id)endColor, nil];
|
|
|
|
|
|
|
|
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,
|
|
|
|
(__bridge CFArrayRef) colors, locations);
|
|
|
|
|
|
|
|
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
|
|
|
|
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
|
|
|
|
|
|
|
|
CGContextSaveGState(context);
|
|
|
|
CGContextAddRect(context, rect);
|
|
|
|
CGContextClip(context);
|
|
|
|
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
|
|
|
|
CGContextRestoreGState(context);
|
|
|
|
|
|
|
|
CGGradientRelease(gradient);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
}
|
|
|
|
|
2011-10-17 09:28:15 -07:00
|
|
|
@implementation Utilities
|
|
|
|
|
2012-07-22 09:12:02 -07:00
|
|
|
+ (void)drawLinearGradientWithRect:(CGRect)rect startColor:(CGColorRef)startColor endColor:(CGColorRef)endColor {
|
|
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
|
|
CGFloat locations[] = { 0.0, 1.0 };
|
|
|
|
|
|
|
|
NSArray *colors = [NSArray arrayWithObjects:(__bridge id)startColor, (__bridge id)endColor, nil];
|
|
|
|
|
|
|
|
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace,
|
|
|
|
(__bridge CFArrayRef) colors, locations);
|
|
|
|
|
|
|
|
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
|
|
|
|
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
|
|
|
|
|
|
|
|
CGContextSaveGState(context);
|
|
|
|
CGContextAddRect(context, rect);
|
|
|
|
CGContextClip(context);
|
|
|
|
|
|
|
|
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
|
|
|
|
CGContextRestoreGState(context);
|
|
|
|
|
|
|
|
CGGradientRelease(gradient);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
}
|
|
|
|
|
2018-09-26 11:20:51 -07:00
|
|
|
+ (UIImage *)roundCorneredImage:(UIImage*)orig radius:(CGFloat)r {
|
|
|
|
return [self roundCorneredImage:orig radius:r convertToSize:orig.size];
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (UIImage *)roundCorneredImage:(UIImage*)orig radius:(CGFloat)r convertToSize:(CGSize)size {
|
2013-12-11 18:06:22 -08:00
|
|
|
if (!orig) return nil;
|
2018-09-26 11:20:51 -07:00
|
|
|
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
|
|
|
|
[[UIBezierPath bezierPathWithRoundedRect:(CGRect){CGPointZero, size}
|
2012-07-11 18:08:07 -07:00
|
|
|
cornerRadius:r] addClip];
|
2018-09-26 11:20:51 -07:00
|
|
|
[orig drawInRect:(CGRect){CGPointZero, size}];
|
2012-07-11 18:08:07 -07:00
|
|
|
UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
|
|
|
|
UIGraphicsEndImageContext();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
#1693 (2022 Redesign)
- Laboriously updated the icons (finding the names in the web inspector, updating the code, inspecting the colors, setting those separately, dealing with scaling issues, etc etc).
- Let me know if you spot any icons that I missed, or unscaled icons (they have to be explicitly sized, otherwise appear huge).
- More space between story title and story content.
- More space when reading a folder.
- Added Compact/Comfortable to feed detail menu.
- Changing Compact/Comfortable also adjusts the feed detail list.
- Adjusted the color of the story content etc in the feed detail list.
- Removed top and bottom borders from feed title gradient in story detail.
- More space in story detail header.
- The feed detail is offset when selected.
- Updated the feeds social, search, and saved background colors.
- Unread counts no longer have a shadow, and have more space.
- Folders and feeds remain selected in the feeds list when returning from a story or refreshing.
- Folders in the feeds list no longer have a different background color.
- The folders and feeds highlight is now rounded and unbordered like on web.
2022-07-20 21:35:16 -07:00
|
|
|
+ (UIImage *)templateImageNamed:(NSString *)imageName sized:(CGFloat)size {
|
|
|
|
UIImage *image = [self imageWithImage:[UIImage imageNamed:imageName] convertToSize:CGSizeMake(size, size)];
|
|
|
|
|
|
|
|
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
|
|
|
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
2022-08-17 21:48:20 -07:00
|
|
|
+ (UIImage *)imageNamed:(NSString *)imageName sized:(CGFloat)size {
|
|
|
|
return [self imageWithImage:[UIImage imageNamed:imageName] convertToSize:CGSizeMake(size, size)];
|
#1693 (2022 Redesign)
- Laboriously updated the icons (finding the names in the web inspector, updating the code, inspecting the colors, setting those separately, dealing with scaling issues, etc etc).
- Let me know if you spot any icons that I missed, or unscaled icons (they have to be explicitly sized, otherwise appear huge).
- More space between story title and story content.
- More space when reading a folder.
- Added Compact/Comfortable to feed detail menu.
- Changing Compact/Comfortable also adjusts the feed detail list.
- Adjusted the color of the story content etc in the feed detail list.
- Removed top and bottom borders from feed title gradient in story detail.
- More space in story detail header.
- The feed detail is offset when selected.
- Updated the feeds social, search, and saved background colors.
- Unread counts no longer have a shadow, and have more space.
- Folders and feeds remain selected in the feeds list when returning from a story or refreshing.
- Folders in the feeds list no longer have a different background color.
- The folders and feeds highlight is now rounded and unbordered like on web.
2022-07-20 21:35:16 -07:00
|
|
|
}
|
|
|
|
|
2013-12-06 16:29:15 -08:00
|
|
|
+ (UIImage *)imageWithImage:(UIImage *)image convertToSize:(CGSize)size {
|
#1693 (2022 Redesign)
- Laboriously updated the icons (finding the names in the web inspector, updating the code, inspecting the colors, setting those separately, dealing with scaling issues, etc etc).
- Let me know if you spot any icons that I missed, or unscaled icons (they have to be explicitly sized, otherwise appear huge).
- More space between story title and story content.
- More space when reading a folder.
- Added Compact/Comfortable to feed detail menu.
- Changing Compact/Comfortable also adjusts the feed detail list.
- Adjusted the color of the story content etc in the feed detail list.
- Removed top and bottom borders from feed title gradient in story detail.
- More space in story detail header.
- The feed detail is offset when selected.
- Updated the feeds social, search, and saved background colors.
- Unread counts no longer have a shadow, and have more space.
- Folders and feeds remain selected in the feeds list when returning from a story or refreshing.
- Folders in the feeds list no longer have a different background color.
- The folders and feeds highlight is now rounded and unbordered like on web.
2022-07-20 21:35:16 -07:00
|
|
|
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
|
2013-12-06 16:29:15 -08:00
|
|
|
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
|
|
|
|
UIImage *destImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
|
|
UIGraphicsEndImageContext();
|
|
|
|
return destImage;
|
|
|
|
}
|
|
|
|
|
2013-06-22 19:34:12 -07:00
|
|
|
+ (NSString *)md5:(NSString *)string {
|
2022-11-09 21:38:40 -07:00
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
2013-06-22 19:34:12 -07:00
|
|
|
const char *cStr = [string UTF8String];
|
|
|
|
unsigned char result[16];
|
2013-09-25 17:43:00 -07:00
|
|
|
CC_MD5( cStr, (CC_LONG)strlen(cStr), result ); // This is the md5 call
|
2013-06-22 19:34:12 -07:00
|
|
|
return [NSString stringWithFormat:
|
|
|
|
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
|
|
|
result[0], result[1], result[2], result[3],
|
|
|
|
result[4], result[5], result[6], result[7],
|
|
|
|
result[8], result[9], result[10], result[11],
|
|
|
|
result[12], result[13], result[14], result[15]
|
|
|
|
];
|
2022-11-09 21:38:40 -07:00
|
|
|
#pragma GCC diagnostic pop
|
2013-06-22 19:34:12 -07:00
|
|
|
}
|
|
|
|
|
2013-10-10 12:58:40 -07:00
|
|
|
+ (NSString *)formatLongDateFromTimestamp:(NSInteger)timestamp {
|
2013-10-01 14:19:12 -07:00
|
|
|
if (!timestamp) timestamp = [[NSDate date] timeIntervalSince1970];
|
2013-10-10 12:58:40 -07:00
|
|
|
|
|
|
|
NSDate *date = [NSDate dateWithTimeIntervalSince1970:(double)timestamp];
|
2015-10-26 22:13:25 -07:00
|
|
|
|
|
|
|
static NSCalendar *calendar = nil;
|
|
|
|
static NSDateFormatter *todayFormatter = nil;
|
|
|
|
static NSDateFormatter *otherFormatter = nil;
|
|
|
|
|
|
|
|
if (!calendar || !todayFormatter || !otherFormatter) {
|
|
|
|
calendar = [NSCalendar currentCalendar];
|
|
|
|
|
|
|
|
todayFormatter = [NSDateFormatter new];
|
|
|
|
todayFormatter.dateStyle = NSDateFormatterNoStyle;
|
|
|
|
todayFormatter.timeStyle = NSDateFormatterShortStyle;
|
|
|
|
|
|
|
|
otherFormatter = [NSDateFormatter new];
|
|
|
|
otherFormatter.dateStyle = NSDateFormatterLongStyle;
|
|
|
|
otherFormatter.timeStyle = NSDateFormatterShortStyle;
|
|
|
|
otherFormatter.doesRelativeDateFormatting = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
return [otherFormatter stringFromDate:date];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2013-10-10 12:58:40 -07:00
|
|
|
static NSDateFormatter *dateFormatter = nil;
|
|
|
|
static NSDateFormatter *todayFormatter = nil;
|
|
|
|
static NSDateFormatter *yesterdayFormatter = nil;
|
|
|
|
static NSDateFormatter *formatterPeriod = nil;
|
|
|
|
|
|
|
|
NSDate *today = [NSDate date];
|
|
|
|
NSDateComponents *components = [[NSCalendar currentCalendar]
|
|
|
|
components:NSIntegerMax
|
|
|
|
fromDate:today];
|
|
|
|
[components setHour:0];
|
|
|
|
[components setMinute:0];
|
|
|
|
[components setSecond:0];
|
|
|
|
NSDate *midnight = [[NSCalendar currentCalendar] dateFromComponents:components];
|
|
|
|
NSDate *yesterday = [NSDate dateWithTimeInterval:-60*60*24 sinceDate:midnight];
|
|
|
|
|
|
|
|
if (!dateFormatter || !todayFormatter || !yesterdayFormatter || !formatterPeriod) {
|
|
|
|
dateFormatter = [[NSDateFormatter alloc] init];
|
|
|
|
[dateFormatter setDateFormat:@"EEEE, MMMM d'Sth', y h:mm"];
|
|
|
|
todayFormatter = [[NSDateFormatter alloc] init];
|
|
|
|
[todayFormatter setDateFormat:@"'Today', MMMM d'Sth' h:mm"];
|
|
|
|
yesterdayFormatter = [[NSDateFormatter alloc] init];
|
|
|
|
[yesterdayFormatter setDateFormat:@"'Yesterday', MMMM d'Sth' h:mm"];
|
|
|
|
formatterPeriod = [[NSDateFormatter alloc] init];
|
|
|
|
[formatterPeriod setDateFormat:@"a"];
|
|
|
|
}
|
|
|
|
|
|
|
|
NSString *dateString;
|
|
|
|
if ([date compare:midnight] == NSOrderedDescending) {
|
|
|
|
dateString = [NSString stringWithFormat:@"%@%@",
|
|
|
|
[todayFormatter stringFromDate:date],
|
|
|
|
[[formatterPeriod stringFromDate:date] lowercaseString]];
|
|
|
|
} else if ([date compare:yesterday] == NSOrderedDescending) {
|
|
|
|
dateString = [NSString stringWithFormat:@"%@%@",
|
|
|
|
[yesterdayFormatter stringFromDate:date],
|
|
|
|
[[formatterPeriod stringFromDate:date] lowercaseString]];
|
|
|
|
} else {
|
|
|
|
dateString = [NSString stringWithFormat:@"%@%@",
|
|
|
|
[dateFormatter stringFromDate:date],
|
|
|
|
[[formatterPeriod stringFromDate:date] lowercaseString]];
|
|
|
|
}
|
|
|
|
dateString = [dateString stringByReplacingOccurrencesOfString:@"Sth"
|
|
|
|
withString:[Utilities suffixForDayInDate:date]];
|
2013-10-01 14:19:12 -07:00
|
|
|
|
2013-10-10 12:58:40 -07:00
|
|
|
return dateString;
|
2015-10-26 22:13:25 -07:00
|
|
|
*/
|
2013-10-10 12:58:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSString *)formatShortDateFromTimestamp:(NSInteger)timestamp {
|
|
|
|
if (!timestamp) timestamp = [[NSDate date] timeIntervalSince1970];
|
|
|
|
|
2013-10-01 14:19:12 -07:00
|
|
|
NSDate *date = [NSDate dateWithTimeIntervalSince1970:(double)timestamp];
|
2015-10-26 22:13:25 -07:00
|
|
|
|
|
|
|
static NSCalendar *calendar = nil;
|
|
|
|
static NSDateFormatter *todayFormatter = nil;
|
|
|
|
static NSDateFormatter *otherFormatter = nil;
|
|
|
|
|
|
|
|
if (!calendar || !todayFormatter || !otherFormatter) {
|
|
|
|
calendar = [NSCalendar currentCalendar];
|
|
|
|
|
|
|
|
todayFormatter = [NSDateFormatter new];
|
|
|
|
todayFormatter.dateStyle = NSDateFormatterNoStyle;
|
|
|
|
todayFormatter.timeStyle = NSDateFormatterShortStyle;
|
|
|
|
|
|
|
|
otherFormatter = [NSDateFormatter new];
|
|
|
|
otherFormatter.dateStyle = NSDateFormatterMediumStyle;
|
|
|
|
otherFormatter.timeStyle = NSDateFormatterShortStyle;
|
|
|
|
otherFormatter.doesRelativeDateFormatting = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([calendar isDateInToday:date]) {
|
|
|
|
return [todayFormatter stringFromDate:date];
|
|
|
|
} else {
|
|
|
|
return [otherFormatter stringFromDate:date];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-10-10 12:58:40 -07:00
|
|
|
static NSDateFormatter *dateFormatter = nil;
|
|
|
|
static NSDateFormatter *todayFormatter = nil;
|
|
|
|
static NSDateFormatter *yesterdayFormatter = nil;
|
|
|
|
static NSDateFormatter *formatterPeriod = nil;
|
|
|
|
|
|
|
|
NSDate *today = [NSDate date];
|
|
|
|
NSDateComponents *components = [[NSCalendar currentCalendar]
|
|
|
|
components:NSIntegerMax
|
|
|
|
fromDate:today];
|
|
|
|
[components setHour:0];
|
|
|
|
[components setMinute:0];
|
|
|
|
[components setSecond:0];
|
|
|
|
NSDate *midnight = [[NSCalendar currentCalendar] dateFromComponents:components];
|
|
|
|
NSDate *yesterday = [NSDate dateWithTimeInterval:-60*60*24 sinceDate:midnight];
|
|
|
|
|
|
|
|
if (!dateFormatter || !todayFormatter || !yesterdayFormatter || !formatterPeriod) {
|
|
|
|
dateFormatter = [[NSDateFormatter alloc] init];
|
|
|
|
[dateFormatter setDateFormat:@"dd LLL y, h:mm"];
|
|
|
|
todayFormatter = [[NSDateFormatter alloc] init];
|
|
|
|
[todayFormatter setDateFormat:@"h:mm"];
|
|
|
|
yesterdayFormatter = [[NSDateFormatter alloc] init];
|
|
|
|
[yesterdayFormatter setDateFormat:@"'Yesterday', h:mm"];
|
|
|
|
formatterPeriod = [[NSDateFormatter alloc] init];
|
|
|
|
[formatterPeriod setDateFormat:@"a"];
|
|
|
|
}
|
2013-10-01 14:19:12 -07:00
|
|
|
|
2013-10-10 12:58:40 -07:00
|
|
|
NSString *dateString;
|
|
|
|
if ([date compare:midnight] == NSOrderedDescending) {
|
|
|
|
dateString = [NSString stringWithFormat:@"%@%@",
|
|
|
|
[todayFormatter stringFromDate:date],
|
|
|
|
[[formatterPeriod stringFromDate:date] lowercaseString]];
|
|
|
|
} else if ([date compare:yesterday] == NSOrderedDescending) {
|
|
|
|
dateString = [NSString stringWithFormat:@"%@%@",
|
|
|
|
[yesterdayFormatter stringFromDate:date],
|
|
|
|
[[formatterPeriod stringFromDate:date] lowercaseString]];
|
|
|
|
} else {
|
|
|
|
dateString = [NSString stringWithFormat:@"%@%@",
|
|
|
|
[dateFormatter stringFromDate:date],
|
|
|
|
[[formatterPeriod stringFromDate:date] lowercaseString]];
|
|
|
|
}
|
|
|
|
|
|
|
|
return dateString;
|
2015-10-26 22:13:25 -07:00
|
|
|
*/
|
2013-10-10 12:58:40 -07:00
|
|
|
}
|
|
|
|
|
2015-10-26 22:13:25 -07:00
|
|
|
/*
|
2013-10-10 12:58:40 -07:00
|
|
|
+ (NSString *)suffixForDayInDate:(NSDate *)date {
|
2015-09-18 15:29:57 -07:00
|
|
|
NSInteger day = [[[[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]
|
|
|
|
components:NSCalendarUnitDay fromDate:date] day];
|
2013-12-02 16:52:17 -08:00
|
|
|
if (day == 11 || day == 12 || day == 13) {
|
2013-10-10 12:58:40 -07:00
|
|
|
return @"th";
|
|
|
|
} else if (day % 10 == 1) {
|
|
|
|
return @"st";
|
|
|
|
} else if (day % 10 == 2) {
|
|
|
|
return @"nd";
|
|
|
|
} else if (day % 10 == 3) {
|
|
|
|
return @"rd";
|
|
|
|
} else {
|
|
|
|
return @"th";
|
|
|
|
}
|
2013-10-01 14:19:12 -07:00
|
|
|
}
|
2015-10-26 22:13:25 -07:00
|
|
|
*/
|
2013-10-01 14:19:12 -07:00
|
|
|
|
2015-12-07 16:09:49 -08:00
|
|
|
@end
|
2022-03-29 21:20:41 -07:00
|
|
|
|
|
|
|
|
|
|
|
static __weak id currentFirstResponder;
|
|
|
|
|
|
|
|
@implementation UIResponder (FirstResponder)
|
|
|
|
|
|
|
|
/**
|
|
|
|
This is primarily as a debugging aid.
|
|
|
|
*/
|
|
|
|
|
|
|
|
+(id)currentFirstResponder {
|
|
|
|
currentFirstResponder = nil;
|
|
|
|
[[UIApplication sharedApplication] sendAction:@selector(findFirstResponder:) to:nil from:nil forEvent:nil];
|
|
|
|
return currentFirstResponder;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)findFirstResponder:(id)sender {
|
|
|
|
currentFirstResponder = self;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|