From f8db90014d7a87e9a6eb754a296154f74aca6250 Mon Sep 17 00:00:00 2001 From: dtsbourg Date: Fri, 19 Dec 2014 10:37:33 +0100 Subject: [PATCH] Attachements: Fixing UI issues --- Signal.xcodeproj/project.pbxproj | 6 + Signal/src/Storyboard/Storyboard.storyboard | 173 -------- Signal/src/util/UIDevice+TSHardwareVersion.h | 48 +++ Signal/src/util/UIDevice+TSHardwareVersion.m | 125 ++++++ .../FullImageViewController.h | 8 +- .../FullImageViewController.m | 389 ++++++++++++++---- .../view controllers/MessagesViewController.m | 13 +- .../view controllers/TSAttachementAdapter.m | 52 ++- 8 files changed, 556 insertions(+), 258 deletions(-) create mode 100644 Signal/src/util/UIDevice+TSHardwareVersion.h create mode 100644 Signal/src/util/UIDevice+TSHardwareVersion.m diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 1790ea513..779475e78 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -522,6 +522,7 @@ FCB626B51A3B067900FDB504 /* ArrowBottom@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FCB626B11A3B067900FDB504 /* ArrowBottom@2x.png */; }; FCB626B61A3B067900FDB504 /* ArrowBottom@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = FCB626B21A3B067900FDB504 /* ArrowBottom@3x.png */; }; FCB626B71A3B067900FDB504 /* ArrowTop@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = FCB626B31A3B067900FDB504 /* ArrowTop@3x.png */; }; + FCC81A981A44558300DFEC7D /* UIDevice+TSHardwareVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = FCC81A971A44558300DFEC7D /* UIDevice+TSHardwareVersion.m */; }; FCF72A081A01A765006BC849 /* ContactsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FCF72A071A01A765006BC849 /* ContactsTableViewController.m */; }; FCF72A131A02D27F006BC849 /* ContactDetailTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FCF72A111A02D27F006BC849 /* ContactDetailTableViewController.m */; }; FCFA64B41A24F3880007FB87 /* UIColor+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = FCFA64B31A24F3880007FB87 /* UIColor+OWS.m */; }; @@ -1220,6 +1221,8 @@ FCB626B11A3B067900FDB504 /* ArrowBottom@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ArrowBottom@2x.png"; sourceTree = ""; }; FCB626B21A3B067900FDB504 /* ArrowBottom@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ArrowBottom@3x.png"; sourceTree = ""; }; FCB626B31A3B067900FDB504 /* ArrowTop@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ArrowTop@3x.png"; sourceTree = ""; }; + FCC81A961A44558300DFEC7D /* UIDevice+TSHardwareVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDevice+TSHardwareVersion.h"; sourceTree = ""; }; + FCC81A971A44558300DFEC7D /* UIDevice+TSHardwareVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+TSHardwareVersion.m"; sourceTree = ""; }; FCF72A061A01A765006BC849 /* ContactsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactsTableViewController.h; sourceTree = ""; }; FCF72A071A01A765006BC849 /* ContactsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactsTableViewController.m; sourceTree = ""; }; FCF72A111A02D27F006BC849 /* ContactDetailTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactDetailTableViewController.m; sourceTree = ""; }; @@ -1806,6 +1809,8 @@ 76EB04FB18170B33006006FC /* Util.h */, 76EB04FC18170B33006006FC /* Zid.h */, 76EB04FD18170B33006006FC /* Zid.m */, + FCC81A961A44558300DFEC7D /* UIDevice+TSHardwareVersion.h */, + FCC81A971A44558300DFEC7D /* UIDevice+TSHardwareVersion.m */, ); path = util; sourceTree = ""; @@ -3259,6 +3264,7 @@ FCFD256F1A151BCB00F4C644 /* NewGroupViewController.m in Sources */, 76EB059E18170B33006006FC /* HttpSocket.m in Sources */, E197B60E18BBEC1A00F073E5 /* CallAudioManager.m in Sources */, + FCC81A981A44558300DFEC7D /* UIDevice+TSHardwareVersion.m in Sources */, 76EB054018170B33006006FC /* AppDelegate.m in Sources */, 76EB05D018170B33006006FC /* ZrtpHandshakeSocket.m in Sources */, B63761EF19E1FBE8005735D1 /* HttpResponse.m in Sources */, diff --git a/Signal/src/Storyboard/Storyboard.storyboard b/Signal/src/Storyboard/Storyboard.storyboard index 621040ad4..f4587b8e0 100755 --- a/Signal/src/Storyboard/Storyboard.storyboard +++ b/Signal/src/Storyboard/Storyboard.storyboard @@ -119,7 +119,6 @@ - @@ -2532,177 +2531,6 @@ A0 09 9A FF A8 8A 09 99 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3824,7 +3652,6 @@ Licensed under the GPLv3 - diff --git a/Signal/src/util/UIDevice+TSHardwareVersion.h b/Signal/src/util/UIDevice+TSHardwareVersion.h new file mode 100644 index 000000000..52b8a1917 --- /dev/null +++ b/Signal/src/util/UIDevice+TSHardwareVersion.h @@ -0,0 +1,48 @@ +// +// UIDevice+TSHardwareVersion.h +// Signal +// +// Created by Dylan Bourgeois on 19/12/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// +// Original Source : +// Erica Sadun, http://ericasadun.com +// iPhone Developer's Cookbook, 6.x Edition +// BSD License, Use at your own risk +// +// + +#import + +typedef NS_ENUM(NSUInteger, UIDeviceFamily) { + UIDeviceFamilyiPhone, + UIDeviceFamilyiPod, + UIDeviceFamilyiPad, + UIDeviceFamilyAppleTV, + UIDeviceFamilyUnknown, +}; + + +@interface UIDevice (TSHardwareVersion) + +/** + Returns a machine-readable model name in the format of "iPhone4,1" + */ +- (NSString *)modelIdentifier; + +/** + Returns a human-readable model name in the format of "iPhone 4S". Fallback of the the `modelIdentifier` value. + */ +- (NSString *)modelName; + +/** + Returns the device family as a `UIDeviceFamily` + */ +- (UIDeviceFamily)deviceFamily; + +/* + * Returns true if device is iPhone 6 or 6+ + */ +- (BOOL)isiPhoneVersionSixOrMore; + +@end diff --git a/Signal/src/util/UIDevice+TSHardwareVersion.m b/Signal/src/util/UIDevice+TSHardwareVersion.m new file mode 100644 index 000000000..e043c9fa0 --- /dev/null +++ b/Signal/src/util/UIDevice+TSHardwareVersion.m @@ -0,0 +1,125 @@ +// +// UIDevice+TSHardwareVersion.m +// Signal +// +// Created by Dylan Bourgeois on 19/12/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// +// Original Source : +// Erica Sadun, http://ericasadun.com +// iPhone Developer's Cookbook, 6.x Edition +// BSD License, Use at your own risk +// +// + +#import "UIDevice+TSHardwareVersion.h" +#include + +@implementation UIDevice (TSHardwareVersion) + +- (NSString *)getSysInfoByName:(char *)typeSpecifier +{ + size_t size; + sysctlbyname(typeSpecifier, NULL, &size, NULL, 0); + + char *answer = malloc(size); + sysctlbyname(typeSpecifier, answer, &size, NULL, 0); + + NSString *results = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding]; + + free(answer); + return results; +} + +- (NSString *)modelIdentifier +{ + return [self getSysInfoByName:"hw.machine"]; +} + +- (NSString *)modelName +{ + return [self modelNameForModelIdentifier:[self modelIdentifier]]; +} + +- (NSString *)modelNameForModelIdentifier:(NSString *)modelIdentifier +{ + // iPhone http://theiphonewiki.com/wiki/IPhone + + if ([modelIdentifier isEqualToString:@"iPhone1,1"]) return @"iPhone 1G"; + if ([modelIdentifier isEqualToString:@"iPhone1,2"]) return @"iPhone 3G"; + if ([modelIdentifier isEqualToString:@"iPhone2,1"]) return @"iPhone 3GS"; + if ([modelIdentifier isEqualToString:@"iPhone3,1"]) return @"iPhone 4 (GSM)"; + if ([modelIdentifier isEqualToString:@"iPhone3,2"]) return @"iPhone 4 (GSM Rev A)"; + if ([modelIdentifier isEqualToString:@"iPhone3,3"]) return @"iPhone 4 (CDMA)"; + if ([modelIdentifier isEqualToString:@"iPhone4,1"]) return @"iPhone 4S"; + if ([modelIdentifier isEqualToString:@"iPhone5,1"]) return @"iPhone 5 (GSM)"; + if ([modelIdentifier isEqualToString:@"iPhone5,2"]) return @"iPhone 5 (Global)"; + if ([modelIdentifier isEqualToString:@"iPhone5,3"]) return @"iPhone 5c (GSM)"; + if ([modelIdentifier isEqualToString:@"iPhone5,4"]) return @"iPhone 5c (Global)"; + if ([modelIdentifier isEqualToString:@"iPhone6,1"]) return @"iPhone 5s (GSM)"; + if ([modelIdentifier isEqualToString:@"iPhone6,2"]) return @"iPhone 5s (Global)"; + if ([modelIdentifier isEqualToString:@"iPhone7,1"]) return @"iPhone 6 Plus"; + if ([modelIdentifier isEqualToString:@"iPhone7,2"]) return @"iPhone 6"; + + // iPad http://theiphonewiki.com/wiki/IPad + + if ([modelIdentifier isEqualToString:@"iPad1,1"]) return @"iPad 1G"; + if ([modelIdentifier isEqualToString:@"iPad2,1"]) return @"iPad 2 (Wi-Fi)"; + if ([modelIdentifier isEqualToString:@"iPad2,2"]) return @"iPad 2 (GSM)"; + if ([modelIdentifier isEqualToString:@"iPad2,3"]) return @"iPad 2 (CDMA)"; + if ([modelIdentifier isEqualToString:@"iPad2,4"]) return @"iPad 2 (Rev A)"; + if ([modelIdentifier isEqualToString:@"iPad3,1"]) return @"iPad 3 (Wi-Fi)"; + if ([modelIdentifier isEqualToString:@"iPad3,2"]) return @"iPad 3 (GSM)"; + if ([modelIdentifier isEqualToString:@"iPad3,3"]) return @"iPad 3 (Global)"; + if ([modelIdentifier isEqualToString:@"iPad3,4"]) return @"iPad 4 (Wi-Fi)"; + if ([modelIdentifier isEqualToString:@"iPad3,5"]) return @"iPad 4 (GSM)"; + if ([modelIdentifier isEqualToString:@"iPad3,6"]) return @"iPad 4 (Global)"; + + if ([modelIdentifier isEqualToString:@"iPad4,1"]) return @"iPad Air (Wi-Fi)"; + if ([modelIdentifier isEqualToString:@"iPad4,2"]) return @"iPad Air (Cellular)"; + if ([modelIdentifier isEqualToString:@"iPad5,3"]) return @"iPad Air 2 (Wi-Fi)"; + if ([modelIdentifier isEqualToString:@"iPad5,4"]) return @"iPad Air 2 (Cellular)"; + + // iPad Mini http://theiphonewiki.com/wiki/IPad_mini + + if ([modelIdentifier isEqualToString:@"iPad2,5"]) return @"iPad mini 1G (Wi-Fi)"; + if ([modelIdentifier isEqualToString:@"iPad2,6"]) return @"iPad mini 1G (GSM)"; + if ([modelIdentifier isEqualToString:@"iPad2,7"]) return @"iPad mini 1G (Global)"; + if ([modelIdentifier isEqualToString:@"iPad4,4"]) return @"iPad mini 2G (Wi-Fi)"; + if ([modelIdentifier isEqualToString:@"iPad4,5"]) return @"iPad mini 2G (Cellular)"; + if ([modelIdentifier isEqualToString:@"iPad4,7"]) return @"iPad mini 3G (Wi-Fi)"; + if ([modelIdentifier isEqualToString:@"iPad4,8"]) return @"iPad mini 3G (Cellular)"; + if ([modelIdentifier isEqualToString:@"iPad4,9"]) return @"iPad mini 3G (Cellular)"; + + // iPod http://theiphonewiki.com/wiki/IPod + + if ([modelIdentifier isEqualToString:@"iPod1,1"]) return @"iPod touch 1G"; + if ([modelIdentifier isEqualToString:@"iPod2,1"]) return @"iPod touch 2G"; + if ([modelIdentifier isEqualToString:@"iPod3,1"]) return @"iPod touch 3G"; + if ([modelIdentifier isEqualToString:@"iPod4,1"]) return @"iPod touch 4G"; + if ([modelIdentifier isEqualToString:@"iPod5,1"]) return @"iPod touch 5G"; + + // Simulator + if ([modelIdentifier hasSuffix:@"86"] || [modelIdentifier isEqual:@"x86_64"]) + { + BOOL smallerScreen = ([[UIScreen mainScreen] bounds].size.width < 768.0); + return (smallerScreen ? @"iPhone Simulator" : @"iPad Simulator"); + } + + return modelIdentifier; +} + +- (UIDeviceFamily) deviceFamily +{ + NSString *modelIdentifier = [self modelIdentifier]; + if ([modelIdentifier hasPrefix:@"iPhone"]) return UIDeviceFamilyiPhone; + if ([modelIdentifier hasPrefix:@"iPod"]) return UIDeviceFamilyiPod; + if ([modelIdentifier hasPrefix:@"iPad"]) return UIDeviceFamilyiPad; + return UIDeviceFamilyUnknown; +} + +-(BOOL)isiPhoneVersionSixOrMore +{ + return [[self modelIdentifier] isEqualToString:@"iPhone7,1"] || [[self modelIdentifier] isEqualToString:@"iPhone7,2"]; +} +@end diff --git a/Signal/src/view controllers/FullImageViewController.h b/Signal/src/view controllers/FullImageViewController.h index d1b8d4834..d3837070d 100644 --- a/Signal/src/view controllers/FullImageViewController.h +++ b/Signal/src/view controllers/FullImageViewController.h @@ -10,12 +10,8 @@ @interface FullImageViewController : UIViewController -@property(nonatomic, strong) IBOutlet UIImageView* fullImageView; -@property(nonatomic, strong) IBOutlet UIButton* saveButton; -@property(nonatomic, strong) IBOutlet UIButton* closeButton; -@property(nonatomic, strong) IBOutlet UIScrollView* pinchView; - -@property(nonatomic, strong) UIImage* image; +- (instancetype)initWithImage:(UIImage*)image fromRect:(CGRect)rect; +-(void)presentFromViewController:(UIViewController*)viewController; @end diff --git a/Signal/src/view controllers/FullImageViewController.m b/Signal/src/view controllers/FullImageViewController.m index 36e169b96..f2b6765d6 100644 --- a/Signal/src/view controllers/FullImageViewController.m +++ b/Signal/src/view controllers/FullImageViewController.m @@ -9,130 +9,379 @@ #import "FullImageViewController.h" #import "DJWActionSheet.h" -@interface FullImageViewController () +#define kImageViewCornerRadius 5.0f + +#define kMinZoomScale 1.0f +#define kMaxZoomScale 8.0f +#define kTargetDoubleTapZoom 3.0f + +#define kBackgroundAlpha 0.6f + +@interface FullImageViewController () + +@property (nonatomic, strong) UIView *backgroundView; + +@property (nonatomic, strong) UIScrollView *scrollView; + +@property (nonatomic, strong) UIImageView *imageView; +@property (nonatomic, strong) UIImage* image; + +@property (nonatomic, strong) UITapGestureRecognizer *singleTap; +@property (nonatomic, strong) UITapGestureRecognizer *doubleTap; + +@property (nonatomic, strong) UIButton *shareButton; + +@property CGRect originRect; +@property BOOL isPresenting; @end @implementation FullImageViewController + +- (instancetype)initWithImage:(UIImage*)image fromRect:(CGRect)rect { + self = [super initWithNibName:nil bundle:nil]; + + if (self) { + self.image = image; + self.imageView.image = image; + self.originRect = rect; + } + + return self; +} + - (void)viewDidLoad { [super viewDidLoad]; - _fullImageView.image = _image; - + [self initializeBackground]; [self initializeScrollView]; + [self initializeImageView]; + [self initializeGestureRecognizers]; + [self populateImageView:self.image]; } + - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. } -#pragma mark - Initializer --(void)initializeScrollView +#pragma mark - Initializers + +- (void)initializeBackground { - UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)]; - [doubleTap setNumberOfTapsRequired:2]; - [_pinchView addGestureRecognizer:doubleTap]; + self.imageView.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha]; + self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha]; + self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.backgroundView = [[UIView alloc] initWithFrame:CGRectInset(self.view.bounds, -512, -512)]; + self.backgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:kBackgroundAlpha]; - _pinchView.delegate = self; - _pinchView.minimumZoomScale=0.9f; - _pinchView.maximumZoomScale=3.0f; - _pinchView.showsVerticalScrollIndicator = NO; - _pinchView.showsHorizontalScrollIndicator = NO; - _pinchView.contentSize=CGSizeMake(CGRectGetWidth(_fullImageView.frame), CGRectGetHeight(_fullImageView.frame)); + [self.view addSubview:self.backgroundView]; } -#pragma mark - IBAction - --(IBAction)close:(id)sender +- (void)initializeScrollView { - [self dismissViewControllerAnimated:YES completion:nil]; + self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; + self.scrollView.delegate = self; + self.scrollView.zoomScale = 1.0f; + self.scrollView.maximumZoomScale = kMaxZoomScale; + self.scrollView.scrollEnabled = NO; + [self.view addSubview:self.scrollView]; } --(IBAction)more:(id)sender +- (void)initializeImageView { - [DJWActionSheet showInView:self.view - withTitle:@"Options" - cancelButtonTitle:@"Cancel" - destructiveButtonTitle:nil - otherButtonTitles:@[@"Save to Camera Roll", @"Delete"] - tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { - if (tappedButtonIndex == actionSheet.cancelButtonIndex) { - NSLog(@"User Cancelled"); - - } else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) { - NSLog(@"Destructive button tapped"); - }else { - NSLog(@"The user tapped button at index: %li", (long)tappedButtonIndex); - } - }]; + self.imageView = [[UIImageView alloc]initWithFrame:self.originRect]; + self.imageView.layer.cornerRadius = kImageViewCornerRadius; + self.imageView.contentMode = UIViewContentModeScaleAspectFill; + self.imageView.userInteractionEnabled = YES; + self.imageView.clipsToBounds = YES; + self.imageView.layer.allowsEdgeAntialiasing = YES; + [self.scrollView addSubview:self.imageView]; } -#pragma mark - Scroll View +- (void)populateImageView:(UIImage*)image +{ + if (image) { + self.imageView.image = image; + } +} -- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView +- (void)initializeGestureRecognizers { - return _fullImageView; + self.doubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(imageDoubleTapped:)]; + self.doubleTap.numberOfTapsRequired = 2; + + self.singleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(imageSingleTapped:)]; + [self.singleTap requireGestureRecognizerToFail:self.doubleTap]; + + self.singleTap.delegate = self; + self.doubleTap.delegate = self; + + [self.view addGestureRecognizer:self.singleTap]; + [self.view addGestureRecognizer:self.doubleTap]; } -- (IBAction)handleDoubleTap:(id)sender { +- (void) initializeShareButton +{ + CGFloat buttonRadius = 50.0f; + CGFloat x = 14.0f; + CGFloat y = self.view.bounds.size.height - buttonRadius - 10.0f; - CGFloat desiredScale = [self doubleTapDestinationZoomScale]; - CGPoint center = [(UITapGestureRecognizer*)sender locationInView:_fullImageView]; - CGRect zoomRect = [self zoomRectForScale:desiredScale withCenter:center]; + self.shareButton = [[UIButton alloc]initWithFrame:CGRectMake(x, y, buttonRadius, buttonRadius)]; + [self.shareButton addTarget:self action:@selector(shareButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; + [self.shareButton setImage:[UIImage imageNamed:@"savephoto"] forState:UIControlStateNormal]; - [_pinchView zoomToRect:zoomRect animated:YES]; - [_pinchView setZoomScale:desiredScale animated:YES]; + [self.view addSubview:self.shareButton]; } +#pragma mark - Gesture Recognizers -- (CGRect)zoomRectForScale:(CGFloat)scale withCenter:(CGPoint)center { - CGRect zoomRect; +- (void)imageDoubleTapped:(UITapGestureRecognizer*)doubleTap +{ + CGPoint tap = [doubleTap locationInView:doubleTap.view]; + CGPoint convertCoord = [self.scrollView convertPoint:tap fromView:doubleTap.view]; + CGRect targetZoomRect; + UIEdgeInsets targetInsets; - zoomRect.size.height = CGRectGetHeight(_pinchView.frame) / scale; - zoomRect.size.width = CGRectGetWidth(_pinchView.frame) / scale; + CGSize zoom ; - zoomRect.origin.x = center.x - ((CGRectGetWidth(zoomRect) / 2.0f)); - zoomRect.origin.y = center.y - ((CGRectGetHeight(zoomRect) / 2.0f)); + if (self.scrollView.zoomScale == 1.0f) { + zoom = CGSizeMake(self.view.bounds.size.width / kTargetDoubleTapZoom, self.view.bounds.size.height / kTargetDoubleTapZoom); + targetZoomRect = CGRectMake(convertCoord.x - (zoom.width/2.0f), convertCoord.y - (zoom.height/2.0f), zoom.width, zoom.height); + targetInsets = [self contentInsetForScrollView:kTargetDoubleTapZoom]; + } else { + zoom = CGSizeMake(self.view.bounds.size.width * self.scrollView.zoomScale, self.view.bounds.size.height * self.scrollView.zoomScale); + targetZoomRect = CGRectMake(convertCoord.x - (zoom.width/2.0f), convertCoord.y - (zoom.height/2.0f), zoom.width, zoom.height); + targetInsets = [self contentInsetForScrollView:1.0f]; + } - return zoomRect; + self.view.userInteractionEnabled = NO; + + [CATransaction begin]; + [CATransaction setCompletionBlock:^{ + self.scrollView.contentInset = targetInsets; + self.view.userInteractionEnabled = YES; + }]; + [self.scrollView zoomToRect:targetZoomRect animated:YES]; + [CATransaction commit]; + } -- (CGFloat)doubleTapDestinationZoomScale +- (void)imageSingleTapped:(UITapGestureRecognizer*)singleTap { - BOOL cond = _pinchView.zoomScale == _pinchView.maximumZoomScale; + [self dismiss]; +} + +#pragma mark - Presentation + +-(void)presentFromViewController:(UIViewController*)viewController +{ + _isPresenting = YES; + self.view.userInteractionEnabled = NO; + [self.view addSubview:self.imageView]; + self.modalPresentationStyle = UIModalPresentationOverCurrentContext; + self.view.alpha = 0; - return cond ? _pinchView.minimumZoomScale : _pinchView.maximumZoomScale; + [viewController presentViewController:self animated:NO completion:^{ + [UIView animateWithDuration:0.4f + delay:0 + options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut + animations:^(){ + self.view.alpha = 1.0f; + self.imageView.frame = [self resizedFrameForImageView:self.image.size]; + self.imageView.center = CGPointMake(self.view.bounds.size.width/2.0f, self.view.bounds.size.height/2.0f); + } completion:^(BOOL completed){ + self.scrollView.frame = self.view.bounds; + [self.scrollView addSubview:self.imageView]; + [self updateLayouts]; + [self initializeShareButton]; + self.view.userInteractionEnabled = YES; + _isPresenting = NO; + }]; + }]; + +} + +- (void)dismiss +{ + self.view.userInteractionEnabled = NO; + [UIView animateWithDuration:0.4f + delay:0 + options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut + animations:^(){ + self.backgroundView.backgroundColor = [UIColor clearColor]; + self.scrollView.alpha = 0; + self.view.alpha = 0; + } completion:^(BOOL completed){ + [self.presentingViewController dismissViewControllerAnimated:NO completion:nil]; + }]; } -#pragma mark - Layout +#pragma mark - Update Layout --(void)centerInSuperview +- (void)viewDidLayoutSubviews { - CGRect frame = _fullImageView.frame; - CGRect superviewFrame = self.view.frame; + [self updateLayouts]; +} + + +- (void) updateLayouts +{ + if (_isPresenting) { + return; + } + + self.scrollView.frame = self.view.bounds; + self.imageView.frame = [self resizedFrameForImageView:self.image.size]; + self.scrollView.contentSize = self.imageView.frame.size; + self.scrollView.contentInset = [self contentInsetForScrollView:self.scrollView.zoomScale]; +} + + +#pragma mark - Resizing + +- (CGRect)resizedFrameForImageView:(CGSize)imageSize { + CGRect frame = self.view.bounds; + CGSize screenSize = CGSizeMake(frame.size.width * self.scrollView.zoomScale, frame.size.height * self.scrollView.zoomScale); + CGSize targetSize = screenSize; - CGFloat dy = (CGRectGetHeight(superviewFrame) - CGRectGetHeight(frame)) / 2.0f; - frame.origin.y = dy; + if ([self isImagePortrait]) { + if ([self getAspectRatioForCGSize:screenSize] < [self getAspectRatioForCGSize:imageSize]) { + targetSize.width = screenSize.height / [self getAspectRatioForCGSize:imageSize]; + } else { + targetSize.height = screenSize.width * [self getAspectRatioForCGSize:imageSize]; + } + } else { + if ([self getAspectRatioForCGSize:screenSize] > [self getAspectRatioForCGSize:imageSize]) { + targetSize.height = screenSize.width * [self getAspectRatioForCGSize:imageSize]; + } else { + targetSize.width = screenSize.height / [self getAspectRatioForCGSize:imageSize]; + } + } - CGFloat dx = (CGRectGetWidth(superviewFrame) - CGRectGetWidth(frame)) / 2.0f; - frame.origin.x = dx; + frame.size = targetSize; + frame.origin = CGPointMake(0, 0); + return frame; +} + +- (UIEdgeInsets)contentInsetForScrollView:(CGFloat)targetZoomScale { + UIEdgeInsets inset = UIEdgeInsetsZero; + + CGSize boundsSize = self.scrollView.bounds.size; + CGSize contentSize = self.image.size; + CGSize minSize; - _fullImageView.frame = frame; + if ([self isImagePortrait]) { + if ([self getAspectRatioForCGSize:boundsSize] < [self getAspectRatioForCGSize:contentSize]) { + minSize.height = boundsSize.height; + minSize.width = minSize.height / [self getAspectRatioForCGSize:contentSize]; + } else { + minSize.width = boundsSize.width; + minSize.height = minSize.width * [self getAspectRatioForCGSize:contentSize]; + } + } else { + if ([self getAspectRatioForCGSize:boundsSize] > [self getAspectRatioForCGSize:contentSize]) { + minSize.width = boundsSize.width; + minSize.height = minSize.width * [self getAspectRatioForCGSize:contentSize]; + } else { + minSize.height = boundsSize.height; + minSize.width = minSize.height / [self getAspectRatioForCGSize:contentSize]; + } + } + + CGSize finalSize = self.view.bounds.size; + + minSize.width *= targetZoomScale; + minSize.height *= targetZoomScale; + + if (minSize.height > finalSize.height && minSize.width > finalSize.width) { + inset = UIEdgeInsetsZero; + } else { + CGFloat dy = boundsSize.height - minSize.height; + CGFloat dx = boundsSize.width - minSize.width; + + dy = (dy > 0) ? dy : 0; + dx = (dx > 0) ? dx : 0; + + inset.top = dy/2.0f; + inset.bottom = dy/2.0f; + inset.left = dx/2.0f; + inset.right = dx/2.0f; + } + return inset; } -/* -#pragma mark - Navigation +#pragma mark - UIScrollViewDelegate + +- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { + return self.imageView; +} -// In a storyboard-based application, you will often want to do a little preparation before navigation -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. +- (void)scrollViewDidZoom:(UIScrollView *)scrollView { + + scrollView.contentInset = [self contentInsetForScrollView:scrollView.zoomScale]; + + if (self.scrollView.scrollEnabled == NO) { + self.scrollView.scrollEnabled = YES; + } +} + +- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale { + self.scrollView.scrollEnabled = (scale > 1); + self.scrollView.contentInset = [self contentInsetForScrollView:scale]; +} + +#pragma mark - Utility + +- (BOOL)isImagePortrait +{ + return ([self getAspectRatioForCGSize:self.image.size] > 1.0f); +} + +- (CGFloat)getAspectRatioForCGSize:(CGSize)size +{ + return size.height / size.width; +} + +#pragma mark - Actions + +-(void)shareButtonTapped:(UIButton*)sender +{ + [DJWActionSheet showInView:self.view withTitle:nil cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete" otherButtonTitles:@[@"Save to Camera Roll", @"Copy"] tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { + if (tappedButtonIndex == actionSheet.cancelButtonIndex) { + + } else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) { + #warning Unimplemented deleting attachments from FullImageView + NSLog(@"Destructive button tapped"); + }else { + switch (tappedButtonIndex) { + case 0: + UIImageWriteToSavedPhotosAlbum(self.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); + break; + case 1: + [[UIPasteboard generalPasteboard] setImage:self.image]; + break; + default: + DDLogWarn(@"Illegal Action sheet field #%ld <%s>",tappedButtonIndex, __PRETTY_FUNCTION__); + break; + } + } + }]; +} + +#pragma mark - Saving images to Camera Roll + +- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error + contextInfo:(void *)contextInfo +{ + if (error) + { + DDLogWarn(@"There was a problem saving <%@> to camera roll from %s ", error.localizedDescription ,__PRETTY_FUNCTION__); + } } -*/ @end diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index 532e1f830..3ebbc9d15 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -322,7 +322,6 @@ typedef enum : NSUInteger { } return cell; - } -(JSQCallCollectionViewCell*)loadCallCellForCall:(id)call atIndexPath:(NSIndexPath*)indexPath @@ -473,7 +472,9 @@ typedef enum : NSUInteger { if ([messageMedia isImage]) { //is a photo tappedImage = ((UIImageView*)[messageMedia mediaView]).image ; - [self performSegueWithIdentifier:@"fullImage" sender:self]; + CGRect convertedRect = [self.collectionView convertRect:[collectionView cellForItemAtIndexPath:indexPath].frame toView:nil]; + FullImageViewController * vc = [[FullImageViewController alloc]initWithImage:tappedImage fromRect:convertedRect]; + [vc presentFromViewController:self]; } else { DDLogWarn(@"Currently unsupported"); @@ -544,12 +545,8 @@ typedef enum : NSUInteger { #pragma mark - Navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - if ([segue.identifier isEqualToString:@"fullImage"]) - { - FullImageViewController* dest = [segue destinationViewController]; - dest.image = tappedImage; - - } else if ([segue.identifier isEqualToString:@"fingerprintSegue"]){ + + if ([segue.identifier isEqualToString:@"fingerprintSegue"]){ FingerprintViewController *vc = [segue destinationViewController]; [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { [vc configWithThread:self.thread]; diff --git a/Signal/src/view controllers/TSAttachementAdapter.m b/Signal/src/view controllers/TSAttachementAdapter.m index c61770811..6dd4d2508 100644 --- a/Signal/src/view controllers/TSAttachementAdapter.m +++ b/Signal/src/view controllers/TSAttachementAdapter.m @@ -8,6 +8,7 @@ #import "TSAttachementAdapter.h" +#import "UIDevice+TSHardwareVersion.h" #import "JSQMessagesMediaViewBubbleImageMasker.h" @interface TSAttachementAdapter () @@ -15,7 +16,6 @@ @property UIImage *image; @property (strong, nonatomic) UIImageView *cachedImageView; - @property (assign, nonatomic, readonly) BOOL isImageAttachment; @end @@ -66,9 +66,59 @@ return self.cachedImageView; } +- (CGSize)mediaViewDisplaySize +{ + return [self getBubbleSizeForImage:_image]; +} + -(BOOL)isImage { return _isImageAttachment; } +#pragma mark - Utility + +-(CGSize)getBubbleSizeForImage:(UIImage*)image +{ + CGFloat aspectRatio = image.size.height / image.size.width ; + + if ([[UIDevice currentDevice] isiPhoneVersionSixOrMore]) + { + return [self getLargeSizeForAspectRatio:aspectRatio]; + } else { + return [self getSmallSizeForAspectRatio:aspectRatio]; + } +} + +-(CGSize)getLargeSizeForAspectRatio:(CGFloat)ratio +{ + return ratio > 1.0f ? [self largePortraitSize] : [self largeLandscapeSize]; +} + +-(CGSize)getSmallSizeForAspectRatio:(CGFloat)ratio +{ + return ratio > 1.0f ? [self smallPortraitSize] : [self smallLandscapeSize]; +} + + +- (CGSize)largePortraitSize +{ + return CGSizeMake(220.0f, 310.0f); +} + +- (CGSize)smallPortraitSize +{ + return CGSizeMake(150.0f, 210.0f); +} + +- (CGSize)largeLandscapeSize +{ + return CGSizeMake(310.0f, 220.0f); +} + +- (CGSize)smallLandscapeSize +{ + return CGSizeMake(210.0f, 150.0f); +} + @end