Merge branch 'charlesmchen/homeViewDesignChanges2'

pull/1/head
Matthew Chen 7 years ago
commit d0aad5360a

@ -161,7 +161,7 @@ const CGFloat kExpirationTimerViewSize = 16.f;
} }
CGFloat ratioRemaining = (CGFloat)timeUntilFlashing / (CGFloat)self.initialDurationSeconds; CGFloat ratioRemaining = (CGFloat)timeUntilFlashing / (CGFloat)self.initialDurationSeconds;
CGFloat ratioComplete = Clamp((CGFloat)1.0 - ratioRemaining, 0, 1.0); CGFloat ratioComplete = CGFloatClamp((CGFloat)1.0 - ratioRemaining, 0, 1.0);
CGPoint startPosition = CGPointMake(0, self.fullHourglassImageView.height * ratioComplete); CGPoint startPosition = CGPointMake(0, self.fullHourglassImageView.height * ratioComplete);
// We offset the bottom slightly to make sure the duration of the perceived animation is correct. // We offset the bottom slightly to make sure the duration of the perceived animation is correct.

@ -316,7 +316,7 @@ static const CGFloat ConversationInputToolbarBorderViewHeight = 0.5;
const CGFloat kMaxTextViewHeight = 98.f; const CGFloat kMaxTextViewHeight = 98.f;
const CGFloat textViewDesiredHeight = (self.inputTextView.contentSize.height + self.inputTextView.contentInset.top const CGFloat textViewDesiredHeight = (self.inputTextView.contentSize.height + self.inputTextView.contentInset.top
+ self.inputTextView.contentInset.bottom); + self.inputTextView.contentInset.bottom);
const CGFloat textViewHeight = ceil(Clamp(textViewDesiredHeight, kMinTextViewHeight, kMaxTextViewHeight)); const CGFloat textViewHeight = ceil(CGFloatClamp(textViewDesiredHeight, kMinTextViewHeight, kMaxTextViewHeight));
const CGFloat kMinContentHeight = kMinTextViewHeight + textViewVInset * 2; const CGFloat kMinContentHeight = kMinTextViewHeight + textViewVInset * 2;
self.textViewHeight = textViewHeight; self.textViewHeight = textViewHeight;

@ -3960,7 +3960,7 @@ typedef enum : NSUInteger {
// keyboard, up to the limits of the content bounds. // keyboard, up to the limits of the content bounds.
CGFloat insetChange = newInsets.bottom - oldInsets.bottom; CGFloat insetChange = newInsets.bottom - oldInsets.bottom;
CGFloat oldYOffset = self.collectionView.contentOffset.y; CGFloat oldYOffset = self.collectionView.contentOffset.y;
CGFloat newYOffset = Clamp(oldYOffset + insetChange, 0, self.safeContentHeight); CGFloat newYOffset = CGFloatClamp(oldYOffset + insetChange, 0, self.safeContentHeight);
CGPoint newOffset = CGPointMake(0, newYOffset); CGPoint newOffset = CGPointMake(0, newYOffset);
// If the user is dismissing the keyboard via interactive scrolling, any additional conset offset feels // If the user is dismissing the keyboard via interactive scrolling, any additional conset offset feels
@ -4739,7 +4739,7 @@ typedef enum : NSUInteger {
const CGFloat swipeTranslation const CGFloat swipeTranslation
= ([gestureRecognizer translationInView:self.view].x * (self.view.isRTL ? +1.f : -1.f)); = ([gestureRecognizer translationInView:self.view].x * (self.view.isRTL ? +1.f : -1.f));
const CGFloat ratioComplete = Clamp(swipeTranslation / self.view.frame.size.width, 0, 1); const CGFloat ratioComplete = CGFloatClamp(swipeTranslation / self.view.frame.size.width, 0, 1);
switch (gestureRecognizer.state) { switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan: { case UIGestureRecognizerStateBegan: {

@ -15,8 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)configureWithThread:(TSThread *)thread - (void)configureWithThread:(TSThread *)thread
contactsManager:(OWSContactsManager *)contactsManager contactsManager:(OWSContactsManager *)contactsManager
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet;
shouldHaveBottomSeparator:(BOOL)shouldHaveBottomSeparator;
@end @end

@ -6,6 +6,7 @@
#import "OWSAvatarBuilder.h" #import "OWSAvatarBuilder.h"
#import "Signal-Swift.h" #import "Signal-Swift.h"
#import <SignalMessaging/OWSFormat.h> #import <SignalMessaging/OWSFormat.h>
#import <SignalMessaging/OWSMath.h>
#import <SignalMessaging/OWSUserProfile.h> #import <SignalMessaging/OWSUserProfile.h>
#import <SignalMessaging/SignalMessaging-Swift.h> #import <SignalMessaging/SignalMessaging-Swift.h>
#import <SignalServiceKit/OWSMessageManager.h> #import <SignalServiceKit/OWSMessageManager.h>
@ -15,16 +16,11 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
const NSUInteger kHomeViewCellHeight = 72;
const NSUInteger kHomeViewCellHMargin = 16;
const NSUInteger kHomeViewCellVMargin = 12;
const NSUInteger kHomeViewAvatarSize = kHomeViewCellHeight - kHomeViewCellVMargin * 2;
const NSUInteger kHomeViewAvatarHSpacing = 12;
@interface HomeViewCell () @interface HomeViewCell ()
@property (nonatomic) AvatarImageView *avatarView; @property (nonatomic) AvatarImageView *avatarView;
@property (nonatomic) UIView *payloadView; @property (nonatomic) UIStackView *payloadView;
@property (nonatomic) UIStackView *topRowView;
@property (nonatomic) UILabel *nameLabel; @property (nonatomic) UILabel *nameLabel;
@property (nonatomic) UILabel *snippetLabel; @property (nonatomic) UILabel *snippetLabel;
@property (nonatomic) UILabel *dateTimeLabel; @property (nonatomic) UILabel *dateTimeLabel;
@ -76,17 +72,22 @@ const NSUInteger kHomeViewAvatarHSpacing = 12;
self.avatarView = [[AvatarImageView alloc] init]; self.avatarView = [[AvatarImageView alloc] init];
[self.contentView addSubview:self.avatarView]; [self.contentView addSubview:self.avatarView];
[self.avatarView autoSetDimension:ALDimensionWidth toSize:kHomeViewAvatarSize]; [self.avatarView autoSetDimension:ALDimensionWidth toSize:self.avatarSize];
[self.avatarView autoSetDimension:ALDimensionHeight toSize:kHomeViewAvatarSize]; [self.avatarView autoSetDimension:ALDimensionHeight toSize:self.avatarSize];
[self.avatarView autoPinLeadingToSuperviewMarginWithInset:kHomeViewCellHMargin]; [self.avatarView autoPinLeadingToSuperviewMarginWithInset:self.cellHMargin];
[self.avatarView autoVCenterInSuperview]; [self.avatarView autoVCenterInSuperview];
[self.avatarView setContentHuggingHigh]; [self.avatarView setContentHuggingHigh];
[self.avatarView setCompressionResistanceHigh]; [self.avatarView setCompressionResistanceHigh];
self.payloadView = [UIView containerView]; self.payloadView = [UIStackView new];
self.payloadView.axis = UILayoutConstraintAxisVertical;
[self.contentView addSubview:self.payloadView]; [self.contentView addSubview:self.payloadView];
[self.payloadView autoPinLeadingToTrailingEdgeOfView:self.avatarView offset:kHomeViewAvatarHSpacing]; [self.payloadView autoPinLeadingToTrailingEdgeOfView:self.avatarView offset:self.avatarHSpacing];
[self.payloadView autoPinTrailingToSuperviewMarginWithInset:self.cellHMargin];
[self.payloadView autoVCenterInSuperview]; [self.payloadView autoVCenterInSuperview];
// Ensure that the cell's contents never overflow the cell bounds.
[self.payloadView autoPinEdgeToSuperviewMargin:ALEdgeTop relation:NSLayoutRelationGreaterThanOrEqual];
[self.payloadView autoPinEdgeToSuperviewMargin:ALEdgeBottom relation:NSLayoutRelationGreaterThanOrEqual];
self.nameLabel = [UILabel new]; self.nameLabel = [UILabel new];
self.nameLabel.lineBreakMode = NSLineBreakByTruncatingTail; self.nameLabel.lineBreakMode = NSLineBreakByTruncatingTail;
@ -98,26 +99,19 @@ const NSUInteger kHomeViewAvatarHSpacing = 12;
[self.dateTimeLabel setContentHuggingHorizontalHigh]; [self.dateTimeLabel setContentHuggingHorizontalHigh];
[self.dateTimeLabel setCompressionResistanceHorizontalHigh]; [self.dateTimeLabel setCompressionResistanceHorizontalHigh];
UIStackView *topRowView = [[UIStackView alloc] initWithArrangedSubviews:@[ self.topRowView = [[UIStackView alloc] initWithArrangedSubviews:@[
self.nameLabel, self.nameLabel,
self.dateTimeLabel, self.dateTimeLabel,
]]; ]];
topRowView.axis = UILayoutConstraintAxisHorizontal; self.topRowView.axis = UILayoutConstraintAxisHorizontal;
topRowView.spacing = 4; self.topRowView.alignment = UIStackViewAlignmentCenter;
[self.payloadView addSubview:topRowView]; [self.payloadView addArrangedSubview:self.topRowView];
[topRowView autoPinLeadingToSuperviewMargin];
[topRowView autoPinTrailingToSuperviewMargin];
[topRowView autoPinTopToSuperviewMargin];
self.snippetLabel = [UILabel new]; self.snippetLabel = [UILabel new];
self.snippetLabel.font = [self snippetFont]; self.snippetLabel.font = [self snippetFont];
self.snippetLabel.numberOfLines = 1; self.snippetLabel.numberOfLines = 1;
self.snippetLabel.lineBreakMode = NSLineBreakByTruncatingTail; self.snippetLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[self.payloadView addSubview:self.snippetLabel]; [self.payloadView addArrangedSubview:self.snippetLabel];
[self.snippetLabel autoPinLeadingToSuperviewMargin];
[self.snippetLabel autoPinTrailingToSuperviewMargin];
[self.snippetLabel autoPinBottomToSuperviewMargin];
[self.snippetLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:topRowView withOffset:5.f];
[self.snippetLabel setContentHuggingHorizontalLow]; [self.snippetLabel setContentHuggingHorizontalLow];
[self.snippetLabel setCompressionResistanceHorizontalLow]; [self.snippetLabel setCompressionResistanceHorizontalLow];
@ -129,9 +123,6 @@ const NSUInteger kHomeViewAvatarHSpacing = 12;
self.unreadBadge = [NeverClearView new]; self.unreadBadge = [NeverClearView new];
self.unreadBadge.backgroundColor = [UIColor ows_materialBlueColor]; self.unreadBadge.backgroundColor = [UIColor ows_materialBlueColor];
[self.contentView addSubview:self.unreadBadge];
[self.unreadBadge autoPinTrailingToSuperviewMarginWithInset:kHomeViewCellHMargin];
[self.unreadBadge autoAlignAxis:ALAxisHorizontal toSameAxisOfView:self.dateTimeLabel];
[self.unreadBadge setContentHuggingHigh]; [self.unreadBadge setContentHuggingHigh];
[self.unreadBadge setCompressionResistanceHigh]; [self.unreadBadge setCompressionResistanceHigh];
@ -145,11 +136,6 @@ const NSUInteger kHomeViewAvatarHSpacing = 12;
return NSStringFromClass([self class]); return NSStringFromClass([self class]);
} }
+ (CGFloat)rowHeight
{
return kHomeViewCellHeight;
}
- (void)initializeLayout - (void)initializeLayout
{ {
self.selectionStyle = UITableViewCellSelectionStyleDefault; self.selectionStyle = UITableViewCellSelectionStyleDefault;
@ -163,15 +149,12 @@ const NSUInteger kHomeViewAvatarHSpacing = 12;
- (void)configureWithThread:(TSThread *)thread - (void)configureWithThread:(TSThread *)thread
contactsManager:(OWSContactsManager *)contactsManager contactsManager:(OWSContactsManager *)contactsManager
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
shouldHaveBottomSeparator:(BOOL)shouldHaveBottomSeparator
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
OWSAssert(thread); OWSAssert(thread);
OWSAssert(contactsManager); OWSAssert(contactsManager);
OWSAssert(blockedPhoneNumberSet); OWSAssert(blockedPhoneNumberSet);
// TODO: Honor shouldHaveBottomSeparator.
self.thread = thread; self.thread = thread;
self.contactsManager = contactsManager; self.contactsManager = contactsManager;
@ -184,22 +167,22 @@ const NSUInteger kHomeViewAvatarHSpacing = 12;
[self updateNameLabel]; [self updateNameLabel];
[self updateAvatarView]; [self updateAvatarView];
self.payloadView.spacing = 0.f;
self.topRowView.spacing = ceil([HomeViewCell scaleValueWithDynamicType:5]);
// We update the fonts every time this cell is configured to ensure that // We update the fonts every time this cell is configured to ensure that
// changes to the dynamic type settings are reflected. // changes to the dynamic type settings are reflected.
self.snippetLabel.font = [self snippetFont]; self.snippetLabel.font = [self snippetFont];
self.snippetLabel.attributedText = self.snippetLabel.attributedText =
[self attributedSnippetForThread:thread blockedPhoneNumberSet:blockedPhoneNumberSet]; [self attributedSnippetForThread:thread blockedPhoneNumberSet:blockedPhoneNumberSet];
self.dateTimeLabel.attributedText = [self attributedStringForDate:thread.lastMessageDate];
self.separatorInset
= UIEdgeInsetsMake(0, kHomeViewAvatarSize + kHomeViewCellHMargin + kHomeViewAvatarHSpacing, 0, 0);
self.dateTimeLabel.attributedText = [self attributedStringForDate:thread.lastMessageDate];
self.dateTimeLabel.textColor = hasUnreadMessages ? [UIColor ows_materialBlueColor] : [UIColor ows_darkGrayColor]; self.dateTimeLabel.textColor = hasUnreadMessages ? [UIColor ows_materialBlueColor] : [UIColor ows_darkGrayColor];
NSUInteger unreadCount = [[OWSMessageUtils sharedManager] unreadMessagesInThread:thread]; NSUInteger unreadCount = [[OWSMessageUtils sharedManager] unreadMessagesInThread:thread];
if (unreadCount > 0) { if (unreadCount > 0) {
[self.topRowView addArrangedSubview:self.unreadBadge];
self.unreadBadge.hidden = NO;
self.unreadLabel.font = [UIFont ows_dynamicTypeCaption1Font]; self.unreadLabel.font = [UIFont ows_dynamicTypeCaption1Font];
self.unreadLabel.text = [OWSFormat formatInt:MIN(99, (int)unreadCount)]; self.unreadLabel.text = [OWSFormat formatInt:MIN(99, (int)unreadCount)];
@ -212,13 +195,6 @@ const NSUInteger kHomeViewAvatarHSpacing = 12;
[self.viewConstraints addObjectsFromArray:@[ [self.viewConstraints addObjectsFromArray:@[
[self.unreadBadge autoSetDimension:ALDimensionWidth toSize:unreadBadgeSize], [self.unreadBadge autoSetDimension:ALDimensionWidth toSize:unreadBadgeSize],
[self.unreadBadge autoSetDimension:ALDimensionHeight toSize:unreadBadgeSize], [self.unreadBadge autoSetDimension:ALDimensionHeight toSize:unreadBadgeSize],
[self.unreadBadge autoPinLeadingToTrailingEdgeOfView:self.payloadView offset:4.f],
]];
} else {
self.unreadBadge.hidden = YES;
[self.viewConstraints addObjectsFromArray:@[
[self.payloadView autoPinTrailingToSuperviewMarginWithInset:kHomeViewCellHMargin],
]]; ]];
} }
} }
@ -240,7 +216,7 @@ const NSUInteger kHomeViewAvatarHSpacing = 12;
} }
self.avatarView.image = self.avatarView.image =
[OWSAvatarBuilder buildImageForThread:thread diameter:kHomeViewAvatarSize contactsManager:contactsManager]; [OWSAvatarBuilder buildImageForThread:thread diameter:self.avatarSize contactsManager:contactsManager];
} }
- (NSAttributedString *)attributedSnippetForThread:(TSThread *)thread - (NSAttributedString *)attributedSnippetForThread:(TSThread *)thread
@ -331,7 +307,7 @@ const NSUInteger kHomeViewAvatarHSpacing = 12;
- (UIFont *)snippetFont - (UIFont *)snippetFont
{ {
return [UIFont ows_dynamicTypeBodyFont]; return [UIFont ows_dynamicTypeFootnoteFont];
} }
- (UIFont *)nameFont - (UIFont *)nameFont
@ -345,6 +321,42 @@ const NSUInteger kHomeViewAvatarHSpacing = 12;
return [UIFont ows_dynamicTypeFootnoteFont]; return [UIFont ows_dynamicTypeFootnoteFont];
} }
// A simple function to scale dimensions to reflect dynamic type. Given a value
// we lerp it larger linearly to reflect size of dynamic type relative to a
// reference value for default dynamic type sizes.
//
// * We _NEVER_ scale values down.
// * We cap scaling.
+ (CGFloat)scaleValueWithDynamicType:(CGFloat)minValue
{
// The default size of dynamic "body" type.
const NSUInteger kReferenceFontSizeMin = 17.f;
CGFloat referenceFontSize = UIFont.ows_dynamicTypeBodyFont.pointSize;
CGFloat alpha = CGFloatClamp(referenceFontSize / kReferenceFontSizeMin, 1.f, 1.3f);
return minValue * alpha;
}
+ (CGFloat)rowHeight
{
return 72;
}
- (NSUInteger)cellHMargin
{
return 16;
}
- (NSUInteger)avatarSize
{
return 48.f;
}
- (NSUInteger)avatarHSpacing
{
return 12.f;
}
#pragma mark - Reuse #pragma mark - Reuse
- (void)prepareForReuse - (void)prepareForReuse
@ -357,6 +369,8 @@ const NSUInteger kHomeViewAvatarHSpacing = 12;
self.thread = nil; self.thread = nil;
self.contactsManager = nil; self.contactsManager = nil;
[self.unreadBadge removeFromSuperview];
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
} }

@ -210,6 +210,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
self.tableView.delegate = self; self.tableView.delegate = self;
self.tableView.dataSource = self; self.tableView.dataSource = self;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.tableView registerClass:[HomeViewCell class] forCellReuseIdentifier:HomeViewCell.cellReuseIdentifier]; [self.tableView registerClass:[HomeViewCell class] forCellReuseIdentifier:HomeViewCell.cellReuseIdentifier];
[self.view addSubview:self.tableView]; [self.view addSubview:self.tableView];
[self.tableView autoPinWidthToSuperview]; [self.tableView autoPinWidthToSuperview];
@ -583,13 +584,9 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
TSThread *thread = [self threadForIndexPath:indexPath]; TSThread *thread = [self threadForIndexPath:indexPath];
BOOL isLastCell = (indexPath.row == [self tableView:tableView numberOfRowsInSection:indexPath.section] - 1);
BOOL shouldHaveBottomSeparator = !isLastCell;
[cell configureWithThread:thread [cell configureWithThread:thread
contactsManager:self.contactsManager contactsManager:self.contactsManager
blockedPhoneNumberSet:self.blockedPhoneNumberSet blockedPhoneNumberSet:self.blockedPhoneNumberSet];
shouldHaveBottomSeparator:shouldHaveBottomSeparator];
if ((unsigned long)indexPath.row == [self.threadMappings numberOfItemsInSection:0] - 1) { if ((unsigned long)indexPath.row == [self.threadMappings numberOfItemsInSection:0] - 1) {
cell.separatorInset = UIEdgeInsetsMake(0.f, cell.bounds.size.width, 0.f, 0.f); cell.separatorInset = UIEdgeInsetsMake(0.f, cell.bounds.size.width, 0.f, 0.f);

@ -276,7 +276,7 @@ NS_ASSUME_NONNULL_BEGIN
cell.accessoryMessage = NSLocalizedString( cell.accessoryMessage = NSLocalizedString(
@"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked."); @"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked.");
} else { } else {
cell.selectionStyle = UITableViewCellSeparatorStyleNone; cell.selectionStyle = UITableViewCellSelectionStyleNone;
} }
} else { } else {
// In the "members" section, we label "new" members as such when editing an existing group. // In the "members" section, we label "new" members as such when editing an existing group.

@ -686,6 +686,6 @@ class CaptioningToolbar: UIView, UITextViewDelegate {
private func clampedTextViewHeight(fixedWidth: CGFloat) -> CGFloat { private func clampedTextViewHeight(fixedWidth: CGFloat) -> CGFloat {
let contentSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude)) let contentSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
return Clamp(contentSize.height, kMinTextViewHeight, maxTextViewHeight) return CGFloatClamp(contentSize.height, kMinTextViewHeight, maxTextViewHeight)
} }
} }

@ -124,7 +124,7 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
- (NSLayoutConstraint *)autoPinToAspectRatio:(CGFloat)ratio - (NSLayoutConstraint *)autoPinToAspectRatio:(CGFloat)ratio
{ {
// Clamp to ensure view has reasonable aspect ratio. // Clamp to ensure view has reasonable aspect ratio.
CGFloat clampedRatio = Clamp(ratio, 0.05, 95.0); CGFloat clampedRatio = CGFloatClamp(ratio, 0.05, 95.0);
if (clampedRatio != ratio) { if (clampedRatio != ratio) {
OWSFail(@"Invalid aspect ratio: %f for view: %@", ratio, self); OWSFail(@"Invalid aspect ratio: %f for view: %@", ratio, self);
} }

@ -1,23 +1,23 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
// TODO: We'll eventually want to promote these into an OWSMath.h header. // TODO: We'll eventually want to promote these into an OWSMath.h header.
static inline CGFloat Clamp(CGFloat value, CGFloat minValue, CGFloat maxValue) static inline CGFloat CGFloatClamp(CGFloat value, CGFloat minValue, CGFloat maxValue)
{ {
return MAX(minValue, MIN(maxValue, value)); return MAX(minValue, MIN(maxValue, value));
} }
static inline CGFloat Clamp01(CGFloat value) static inline CGFloat CGFloatClamp01(CGFloat value)
{ {
return Clamp(value, 0.f, 1.f); return CGFloatClamp(value, 0.f, 1.f);
} }
static inline CGFloat CGFloatLerp(CGFloat left, CGFloat right, CGFloat alpha) static inline CGFloat CGFloatLerp(CGFloat left, CGFloat right, CGFloat alpha)
{ {
alpha = Clamp01(alpha); alpha = CGFloatClamp01(alpha);
return (left * (1.f - alpha)) + (right * alpha); return (left * (1.f - alpha)) + (right * alpha);
} }
@ -27,6 +27,12 @@ static inline CGFloat CGFloatInverseLerp(CGFloat value, CGFloat minValue, CGFloa
return (value - minValue) / (maxValue - minValue); return (value - minValue) / (maxValue - minValue);
} }
// Ceil to an even number
static inline CGFloat CeilEven(CGFloat value)
{
return 2.f * ceil(value * 0.5f);
}
void SetRandFunctionSeed(void); void SetRandFunctionSeed(void);
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

Loading…
Cancel
Save