Merge branch 'mkirk/bordered-avatar'

pull/1/head
Michael Kirk 8 years ago
commit ced9125303

@ -1,5 +1,6 @@
// Created by Michael Kirk on 9/26/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@ -9,7 +10,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSAvatarBuilder : NSObject
+ (UIImage *)buildImageForThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager;
+ (UIImage *)buildImageForThread:(TSThread *)thread
contactsManager:(OWSContactsManager *)contactsManager
diameter:(NSUInteger)diameter;
- (nullable UIImage *)buildSavedImage;
- (UIImage *)buildDefaultImage;

@ -1,5 +1,6 @@
// Created by Michael Kirk on 9/26/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSAvatarBuilder.h"
#import "OWSContactAvatarBuilder.h"
@ -11,12 +12,13 @@ NS_ASSUME_NONNULL_BEGIN
@implementation OWSAvatarBuilder
+ (UIImage *)buildImageForThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager
+ (UIImage *)buildImageForThread:(TSThread *)thread
contactsManager:(OWSContactsManager *)contactsManager
diameter:(NSUInteger)diameter
{
OWSAvatarBuilder *avatarBuilder;
if ([thread isKindOfClass:[TSContactThread class]]) {
avatarBuilder =
[[OWSContactAvatarBuilder alloc] initWithThread:(TSContactThread *)thread contactsManager:contactsManager];
avatarBuilder = [[OWSContactAvatarBuilder alloc] initWithThread:(TSContactThread *)thread contactsManager:contactsManager diameter:diameter];
} else if ([thread isKindOfClass:[TSGroupThread class]]) {
avatarBuilder = [[OWSGroupAvatarBuilder alloc] initWithThread:(TSGroupThread *)thread];
} else {

@ -1,5 +1,6 @@
// Created by Michael Kirk on 9/22/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSAvatarBuilder.h"
@ -12,9 +13,12 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithContactId:(NSString *)contactId
name:(NSString *)name
contactsManager:(OWSContactsManager *)contactsManager;
contactsManager:(OWSContactsManager *)contactsManager
diameter:(NSUInteger)diameter;
- (instancetype)initWithThread:(TSContactThread *)thread contactsManager:(OWSContactsManager *)contactsManager;
- (instancetype)initWithThread:(TSContactThread *)thread
contactsManager:(OWSContactsManager *)contactsManager
diameter:(NSUInteger)diameter;
@end

@ -1,5 +1,6 @@
// Created by Michael Kirk on 9/22/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSContactAvatarBuilder.h"
#import "OWSContactsManager.h"
@ -17,6 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
@property (nonatomic, readonly) NSString *signalId;
@property (nonatomic, readonly) NSString *contactName;
@property (nonatomic, readonly) NSUInteger diameter;
@end
@ -25,23 +27,26 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithContactId:(NSString *)contactId
name:(NSString *)name
contactsManager:(OWSContactsManager *)contactsManager
diameter:(NSUInteger)diameter
{
self = [super init];
if (!self) {
return self;
}
_signalId = contactId;
_contactName = name;
_contactsManager = contactsManager;
_diameter = diameter;
return self;
}
- (instancetype)initWithThread:(TSContactThread *)thread contactsManager:(OWSContactsManager *)contactsManager
- (instancetype)initWithThread:(TSContactThread *)thread
contactsManager:(OWSContactsManager *)contactsManager
diameter:(NSUInteger)diameter
{
return [self initWithContactId:thread.contactIdentifier name:thread.name contactsManager:contactsManager];
return [self initWithContactId:thread.contactIdentifier name:thread.name contactsManager:contactsManager diameter:diameter];
}
- (nullable UIImage *)buildSavedImage
@ -51,7 +56,8 @@ NS_ASSUME_NONNULL_BEGIN
- (UIImage *)buildDefaultImage
{
UIImage *cachedAvatar = [self.contactsManager.avatarCache objectForKey:self.signalId];
NSString *cacheKey = [NSString stringWithFormat:@"signalId:%@:diamater:%lu", self.signalId, (unsigned long)self.diameter];
UIImage *cachedAvatar = [self.contactsManager.avatarCache objectForKey:cacheKey];
if (cachedAvatar) {
return cachedAvatar;
}
@ -82,12 +88,13 @@ NS_ASSUME_NONNULL_BEGIN
[initials appendString:@"#"];
}
CGFloat fontSize = (CGFloat)self.diameter / 2.8;
UIColor *backgroundColor = [UIColor backgroundColorForContact:self.signalId];
UIImage *image = [[JSQMessagesAvatarImageFactory avatarImageWithUserInitials:initials
backgroundColor:backgroundColor
textColor:[UIColor whiteColor]
font:[UIFont ows_boldFontWithSize:36.0]
diameter:100] avatarImage];
font:[UIFont ows_boldFontWithSize:fontSize]
diameter:self.diameter] avatarImage];
[self.contactsManager.avatarCache setObject:image forKey:self.signalId];
return image;
}

@ -147,7 +147,7 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R
createViews()
contactNameLabel.text = contactsManager.displayName(forPhoneIdentifier: thread.contactIdentifier())
contactAvatarView.image = OWSAvatarBuilder.buildImage(for: thread, contactsManager: contactsManager)
contactAvatarView.image = OWSAvatarBuilder.buildImage(for: thread, contactsManager: contactsManager, diameter:400)
assert(call != nil)
// Subscribe for future call updates

@ -2,13 +2,13 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TSThread.h"
NS_ASSUME_NONNULL_BEGIN
@class Contact;
@class OWSContactsManager;
@class AvatarImageView;
typedef enum : NSUInteger { kArchiveState = 0, kInboxState = 1 } CellState;
@ -16,7 +16,7 @@ typedef enum : NSUInteger { kArchiveState = 0, kInboxState = 1 } CellState;
@property (nonatomic) IBOutlet UILabel *nameLabel;
@property (nonatomic) IBOutlet UILabel *snippetLabel;
@property (nonatomic) IBOutlet UIImageView *contactPictureView;
@property (nonatomic) IBOutlet AvatarImageView *contactPictureView;
@property (nonatomic) IBOutlet UILabel *timeLabel;
@property (nonatomic) IBOutlet UIView *contentContainerView;
@property (nonatomic) IBOutlet UIView *messageCounter;

@ -24,6 +24,8 @@ NS_ASSUME_NONNULL_BEGIN
#define DATE_LABEL_SIZE 13
#define SWIPE_ARCHIVE_OFFSET -50
const NSUInteger kContactPictureViewDiameter = 52;
@interface InboxTableViewCell ()
@property (nonatomic) NSUInteger unreadMessages;
@ -120,7 +122,6 @@ NS_ASSUME_NONNULL_BEGIN
self.snippetLabel.attributedText = snippetText;
self.timeLabel.attributedText = attributedDate;
self.contactPictureView.image = nil;
[UIUtil applyRoundedBorderToImageView:_contactPictureView];
self.separatorInset = UIEdgeInsetsMake(0, _contactPictureView.frame.size.width * 1.5f, 0, 0);
@ -133,7 +134,9 @@ NS_ASSUME_NONNULL_BEGIN
NSString *threadIdCopy = thread.uniqueId;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *avatar = [OWSAvatarBuilder buildImageForThread:thread contactsManager:contactsManager];
UIImage *avatar = [OWSAvatarBuilder buildImageForThread:thread
contactsManager:contactsManager
diameter:kContactPictureViewDiameter];
dispatch_async(dispatch_get_main_queue(), ^{
if ([_threadId isEqualToString:threadIdCopy]) {
self.contactPictureView.image = avatar;

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="15G1217" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -16,15 +16,16 @@
<rect key="frame" x="0.0" y="0.0" width="400" height="72"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" multipleTouchEnabled="YES" contentMode="center" tableViewCell="axX-Rb-kiK" id="BRG-hJ-lRa">
<rect key="frame" x="0.0" y="0.0" width="400" height="71"/>
<rect key="frame" x="0.0" y="0.0" width="400" height="71.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kq8-RD-txC" userLabel="Container View">
<rect key="frame" x="0.0" y="0.0" width="400" height="72"/>
<rect key="frame" x="0.0" y="0.0" width="400" height="47"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="p9o-x6-nT5">
<rect key="frame" x="10" y="10" width="52" height="53"/>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="p9o-x6-nT5" customClass="AvatarImageView" customModule="Signal" customModuleProvider="target">
<rect key="frame" x="10" y="10" width="52" height="52"/>
<constraints>
<constraint firstAttribute="width" secondItem="p9o-x6-nT5" secondAttribute="height" multiplier="1:1" id="IjZ-Ac-GiV"/>
<constraint firstAttribute="width" constant="52" id="mb7-1o-58k"/>
</constraints>
</imageView>
@ -54,7 +55,7 @@
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mfN-2A-QxN">
<rect key="frame" x="75" y="6" width="236" height="25"/>
<rect key="frame" x="75" y="6" width="236" height="0.0"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>

@ -26,6 +26,8 @@
NS_ASSUME_NONNULL_BEGIN
const NSUInteger kNewGroupViewControllerAvatarWidth = 68;
@interface NewGroupViewController () <UIImagePickerControllerDelegate,
UITextFieldDelegate,
ContactsViewHelperDelegate,
@ -39,7 +41,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) GroupViewHelper *groupViewHelper;
@property (nonatomic, readonly) OWSTableViewController *tableViewController;
@property (nonatomic, readonly) UIImageView *avatarView;
@property (nonatomic, readonly) AvatarImageView *avatarView;
@property (nonatomic, readonly) UITextField *groupNameTextField;
@property (nonatomic, nullable) UIImage *groupAvatar;
@ -134,18 +136,14 @@ NS_ASSUME_NONNULL_BEGIN
[threadInfoView autoPinWidthToSuperviewWithMargin:16.f];
[threadInfoView autoPinHeightToSuperviewWithMargin:16.f];
const CGFloat kAvatarSize = 68.f;
UIImageView *avatarView = [UIImageView new];
AvatarImageView *avatarView = [AvatarImageView new];
_avatarView = avatarView;
avatarView.layer.borderColor = UIColor.clearColor.CGColor;
avatarView.layer.masksToBounds = YES;
avatarView.layer.cornerRadius = kAvatarSize / 2.0f;
avatarView.contentMode = UIViewContentModeScaleAspectFill;
[threadInfoView addSubview:avatarView];
[avatarView autoVCenterInSuperview];
[avatarView autoPinEdgeToSuperviewEdge:ALEdgeLeft];
[avatarView autoSetDimension:ALDimensionWidth toSize:kAvatarSize];
[avatarView autoSetDimension:ALDimensionHeight toSize:kAvatarSize];
[avatarView autoSetDimension:ALDimensionWidth toSize:kNewGroupViewControllerAvatarWidth];
[avatarView autoSetDimension:ALDimensionHeight toSize:kNewGroupViewControllerAvatarWidth];
[self updateAvatarView];
UITextField *groupNameTextField = [UITextField new];
@ -494,13 +492,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)updateAvatarView
{
UIImage *image = (self.groupAvatar ?: [UIImage imageNamed:@"empty-group-avatar"]);
OWSAssert(image);
self.avatarView.image = image;
self.avatarView.layer.borderColor = [[UIColor lightGrayColor] CGColor];
self.avatarView.layer.borderWidth = 0.5f;
self.avatarView.contentMode = UIViewContentModeScaleAspectFill;
self.avatarView.image = (self.groupAvatar ?: [UIImage imageNamed:@"empty-group-avatar"]);
}
#pragma mark - Event Handling

@ -536,15 +536,13 @@ NS_ASSUME_NONNULL_BEGIN
[threadInfoView autoPinWidthToSuperviewWithMargin:16.f];
[threadInfoView autoPinHeightToSuperviewWithMargin:16.f];
UIImage *avatar = [OWSAvatarBuilder buildImageForThread:self.thread contactsManager:self.contactsManager];
const NSUInteger kAvatarSize = 68;
UIImage *avatar =
[OWSAvatarBuilder buildImageForThread:self.thread contactsManager:self.contactsManager diameter:kAvatarSize];
OWSAssert(avatar);
const CGFloat kAvatarSize = 68.f;
UIImageView *avatarView = [[UIImageView alloc] initWithImage:avatar];
AvatarImageView *avatarView = [[AvatarImageView alloc] initWithImage:avatar];
_avatarView = avatarView;
avatarView.layer.borderColor = UIColor.clearColor.CGColor;
avatarView.layer.masksToBounds = YES;
avatarView.layer.cornerRadius = kAvatarSize / 2.0f;
avatarView.contentMode = UIViewContentModeScaleAspectFill;
[threadInfoView addSubview:avatarView];
[avatarView autoVCenterInSuperview];
[avatarView autoPinEdgeToSuperviewEdge:ALEdgeLeft];

@ -40,7 +40,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) GroupViewHelper *groupViewHelper;
@property (nonatomic, readonly) OWSTableViewController *tableViewController;
@property (nonatomic, readonly) UIImageView *avatarView;
@property (nonatomic, readonly) AvatarImageView *avatarView;
@property (nonatomic, readonly) UITextField *groupNameTextField;
@property (nonatomic, nullable) UIImage *groupAvatar;
@ -180,12 +180,9 @@ NS_ASSUME_NONNULL_BEGIN
[threadInfoView autoPinHeightToSuperviewWithMargin:16.f];
const CGFloat kAvatarSize = 68.f;
UIImageView *avatarView = [UIImageView new];
AvatarImageView *avatarView = [AvatarImageView new];
_avatarView = avatarView;
avatarView.layer.borderColor = UIColor.clearColor.CGColor;
avatarView.layer.masksToBounds = YES;
avatarView.layer.cornerRadius = kAvatarSize / 2.0f;
avatarView.contentMode = UIViewContentModeScaleAspectFill;
[threadInfoView addSubview:avatarView];
[avatarView autoVCenterInSuperview];
[avatarView autoPinEdgeToSuperviewEdge:ALEdgeLeft];
@ -403,13 +400,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)updateAvatarView
{
UIImage *image = (self.groupAvatar ?: [UIImage imageNamed:@"empty-group-avatar"]);
OWSAssert(image);
self.avatarView.image = image;
self.avatarView.layer.borderColor = [[UIColor lightGrayColor] CGColor];
self.avatarView.layer.borderWidth = 0.5f;
self.avatarView.contentMode = UIViewContentModeScaleAspectFill;
self.avatarView.image = (self.groupAvatar ?: [UIImage imageNamed:@"empty-group-avatar"]);
}
#pragma mark - Event Handling

@ -153,7 +153,7 @@
drawRect.origin.x = 0;
drawRect.size.width = dstSize.width;
drawRect.size.height = dstSize.width * srcSize.height / srcSize.width;
OWSAssert(drawRect.size.height > dstSize.height);
OWSAssert(drawRect.size.height >= dstSize.height);
drawRect.origin.y = (drawRect.size.height - dstSize.height) * -0.5f;
}

@ -1,14 +1,41 @@
// Created by Michael Kirk on 12/11/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
import UIKit
@IBDesignable
class AvatarImageView: UIImageView {
override func layoutSubviews() {
init() {
super.init(frame: CGRect.zero)
self.configureView()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.configureView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.configureView()
}
override init(image: UIImage?) {
super.init(image: image)
self.configureView()
}
func configureView() {
self.layer.minificationFilter = kCAFilterTrilinear
self.layer.magnificationFilter = kCAFilterTrilinear
self.layer.borderWidth = 0.5
self.layer.masksToBounds = true
self.layer.cornerRadius = self.frame.size.width / 2
self.contentMode = .scaleToFill
}
override func layoutSubviews() {
self.layer.borderColor = UIColor.black.cgColor.copy(alpha: 0.15)
self.layer.cornerRadius = self.frame.size.width / 2
}
}

@ -60,9 +60,11 @@ class ContactCell: UITableViewCell {
contactIdForDeterminingBackgroundColor = contact.fullName
}
let kAvatarWidth: UInt = 40
let avatarBuilder = OWSContactAvatarBuilder(contactId:contactIdForDeterminingBackgroundColor,
name:contact.fullName,
contactsManager:contactsManager)
contactsManager:contactsManager,
diameter: kAvatarWidth)
self.contactImageView?.image = avatarBuilder.buildDefaultImage()
} else {
self.contactImageView?.image = contact.image

@ -6,6 +6,7 @@
#import "Environment.h"
#import "OWSContactAvatarBuilder.h"
#import "OWSContactsManager.h"
#import "Signal-Swift.h"
#import "UIFont+OWS.h"
#import "UIUtil.h"
#import "UIView+OWS.h"
@ -16,6 +17,7 @@
NS_ASSUME_NONNULL_BEGIN
NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseIdentifier";
const NSUInteger kContactTableViewCellAvatarSize = 40;
@interface ContactTableViewCell ()
@ -51,15 +53,7 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI
- (void)configureProgrammatically
{
const CGFloat kAvatarSize = 40.f;
_avatarView = [UIImageView new];
_avatarView.image = [UIImage imageNamed:@"empty-group-avatar"];
_avatarView.contentMode = UIViewContentModeScaleToFill;
// applyRoundedBorderToImageView requires the avatar to have
// the correct size.
_avatarView.frame = CGRectMake(0, 0, kAvatarSize, kAvatarSize);
_avatarView.layer.minificationFilter = kCAFilterTrilinear;
_avatarView.layer.magnificationFilter = kCAFilterTrilinear;
_avatarView = [AvatarImageView new];
[self.contentView addSubview:_avatarView];
_nameLabel = [UILabel new];
@ -70,8 +64,8 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI
[_avatarView autoVCenterInSuperview];
[_avatarView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:ScaleFromIPhone5To7Plus(14.f, 20.f)];
[_avatarView autoSetDimension:ALDimensionWidth toSize:kAvatarSize];
[_avatarView autoSetDimension:ALDimensionHeight toSize:kAvatarSize];
[_avatarView autoSetDimension:ALDimensionWidth toSize:kContactTableViewCellAvatarSize];
[_avatarView autoSetDimension:ALDimensionHeight toSize:kContactTableViewCellAvatarSize];
[_nameLabel autoPinEdgeToSuperviewEdge:ALEdgeRight];
[_nameLabel autoPinEdgeToSuperviewEdge:ALEdgeTop];
@ -117,9 +111,10 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI
self.accessoryView = blockedLabel;
}
self.nameLabel.attributedText = attributedText;
self.avatarView.image =
[[[OWSContactAvatarBuilder alloc] initWithContactId:recipientId name:avatarName contactsManager:contactsManager]
build];
self.avatarView.image = [[[OWSContactAvatarBuilder alloc] initWithContactId:recipientId
name:avatarName
contactsManager:contactsManager
diameter:kContactTableViewCellAvatarSize] build];
// Force layout, since imageView isn't being initally rendered on App Store optimized build.
[self layoutSubviews];
@ -141,18 +136,14 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI
}];
self.nameLabel.attributedText = attributedText;
self.avatarView.image = [OWSAvatarBuilder buildImageForThread:thread contactsManager:contactsManager];
self.avatarView.image = [OWSAvatarBuilder buildImageForThread:thread
contactsManager:contactsManager
diameter:kContactTableViewCellAvatarSize];
// Force layout, since imageView isn't being initally rendered on App Store optimized build.
[self layoutSubviews];
}
- (void)layoutSubviews
{
[super layoutSubviews];
[UIUtil applyRoundedBorderToImageView:self.avatarView];
}
- (void)prepareForReuse
{
self.accessoryMessage = nil;

Loading…
Cancel
Save