mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
454 lines
13 KiB
Objective-C
454 lines
13 KiB
Objective-C
#import "TGPhotoEntitiesContainerView.h"
|
|
#import "TGPhotoPaintEntityView.h"
|
|
#import "TGPhotoStickerEntityView.h"
|
|
#import "TGPhotoTextEntityView.h"
|
|
#import "TGPaintingData.h"
|
|
|
|
#import <LegacyComponents/TGPhotoEditorUtils.h>
|
|
|
|
@interface TGPhotoEntitiesContainerView () <UIGestureRecognizerDelegate>
|
|
{
|
|
TGPhotoPaintEntityView *_currentView;
|
|
UITapGestureRecognizer *_tapGestureRecognizer;
|
|
}
|
|
@end
|
|
|
|
@implementation TGPhotoEntitiesContainerView
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame
|
|
{
|
|
self = [super initWithFrame:frame];
|
|
if (self != nil)
|
|
{
|
|
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
|
|
_tapGestureRecognizer.delegate = self;
|
|
[self addGestureRecognizer:_tapGestureRecognizer];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)updateVisibility:(bool)visible
|
|
{
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
|
|
[(TGPhotoStickerEntityView *)view updateVisibility:visible];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)seekTo:(double)timestamp {
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
|
|
[(TGPhotoStickerEntityView *)view seekTo:timestamp];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)play {
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
|
|
[(TGPhotoStickerEntityView *)view play];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)pause {
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
|
|
[(TGPhotoStickerEntityView *)view pause];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- (void)resetToStart {
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
|
|
[(TGPhotoStickerEntityView *)view resetToStart];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)__unused gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)__unused otherGestureRecognizer
|
|
{
|
|
return false;
|
|
}
|
|
|
|
- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer
|
|
{
|
|
CGPoint point = [gestureRecognizer locationInView:self];
|
|
|
|
NSMutableArray *intersectedViews = [[NSMutableArray alloc] init];
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if ([view pointInside:[view convertPoint:point fromView:self] withEvent:nil])
|
|
[intersectedViews addObject:view];
|
|
}
|
|
|
|
TGPhotoPaintEntityView *result = nil;
|
|
if (intersectedViews.count > 1)
|
|
{
|
|
__block TGPhotoPaintEntityView *subresult = nil;
|
|
[intersectedViews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(TGPhotoPaintEntityView *view, __unused NSUInteger index, BOOL *stop)
|
|
{
|
|
if ([view precisePointInside:[view convertPoint:point fromView:self]])
|
|
{
|
|
subresult = view;
|
|
*stop = true;
|
|
}
|
|
}];
|
|
|
|
result = subresult ?: intersectedViews.lastObject;
|
|
}
|
|
else if (intersectedViews.count == 1)
|
|
{
|
|
result = intersectedViews.firstObject;
|
|
}
|
|
|
|
if (self.entitySelected != nil)
|
|
self.entitySelected(result);
|
|
}
|
|
|
|
- (UIColor *)colorAtPoint:(CGPoint)point {
|
|
NSMutableArray *intersectedViews = [[NSMutableArray alloc] init];
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if ([view pointInside:[view convertPoint:point fromView:self] withEvent:nil])
|
|
[intersectedViews addObject:view];
|
|
}
|
|
|
|
TGPhotoPaintEntityView *result = nil;
|
|
if (intersectedViews.count > 1)
|
|
{
|
|
__block TGPhotoPaintEntityView *subresult = nil;
|
|
[intersectedViews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(TGPhotoPaintEntityView *view, __unused NSUInteger index, BOOL *stop)
|
|
{
|
|
if ([view precisePointInside:[view convertPoint:point fromView:self]])
|
|
{
|
|
subresult = view;
|
|
*stop = true;
|
|
}
|
|
}];
|
|
|
|
result = subresult ?: intersectedViews.lastObject;
|
|
}
|
|
else if (intersectedViews.count == 1)
|
|
{
|
|
result = intersectedViews.firstObject;
|
|
}
|
|
|
|
return [result colorAtPoint:[result convertPoint:point fromView:self]];
|
|
}
|
|
|
|
- (NSUInteger)entitiesCount
|
|
{
|
|
return MAX(0, (NSInteger)self.subviews.count - 1);
|
|
}
|
|
|
|
- (void)setupWithPaintingData:(TGPaintingData *)paintingData {
|
|
[self removeAll];
|
|
// for (TGPhotoPaintEntity *entity in paintingData.entities) {
|
|
// UIView * entityView = [self createEntityViewWithEntity:entity];
|
|
// [self addSubview:entityView];
|
|
// }
|
|
}
|
|
|
|
- (TGPhotoPaintEntityView *)createEntityViewWithEntity:(TGPhotoPaintEntity *)entity {
|
|
if ([entity isKindOfClass:[TGPhotoPaintStickerEntity class]])
|
|
return [self _createStickerViewWithEntity:(TGPhotoPaintStickerEntity *)entity];
|
|
else if ([entity isKindOfClass:[TGPhotoPaintTextEntity class]])
|
|
return [self _createTextViewWithEntity:(TGPhotoPaintTextEntity *)entity];
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (TGPhotoStickerEntityView *)_createStickerViewWithEntity:(TGPhotoPaintStickerEntity *)entity
|
|
{
|
|
TGPhotoStickerEntityView *stickerView = [[TGPhotoStickerEntityView alloc] initWithEntity:entity context:self.stickersContext];
|
|
[self _commonEntityViewSetup:stickerView entity:entity];
|
|
|
|
return stickerView;
|
|
}
|
|
|
|
- (TGPhotoTextEntityView *)_createTextViewWithEntity:(TGPhotoPaintTextEntity *)entity
|
|
{
|
|
TGPhotoTextEntityView *textView = [[TGPhotoTextEntityView alloc] initWithEntity:entity];
|
|
[textView sizeToFit];
|
|
|
|
[self _commonEntityViewSetup:textView entity:entity];
|
|
|
|
return textView;
|
|
}
|
|
|
|
- (void)_commonEntityViewSetup:(TGPhotoPaintEntityView *)entityView entity:(TGPhotoPaintEntity *)entity
|
|
{
|
|
entityView.transform = CGAffineTransformRotate(CGAffineTransformMakeScale(entity.scale, entity.scale), entity.angle);
|
|
entityView.center = entity.position;
|
|
}
|
|
|
|
- (TGPhotoPaintEntityView *)viewForUUID:(NSInteger)uuid
|
|
{
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if (view.entityUUID == uuid)
|
|
return view;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (void)removeViewWithUUID:(NSInteger)uuid
|
|
{
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if (view.entityUUID == uuid)
|
|
{
|
|
[view removeFromSuperview];
|
|
|
|
if (self.entityRemoved != nil)
|
|
self.entityRemoved(view);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)removeAll
|
|
{
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
[view removeFromSuperview];
|
|
}
|
|
}
|
|
|
|
- (void)handlePinch:(UIPinchGestureRecognizer *)gestureRecognizer
|
|
{
|
|
CGPoint location = [gestureRecognizer locationInView:self];
|
|
|
|
switch (gestureRecognizer.state)
|
|
{
|
|
case UIGestureRecognizerStateBegan:
|
|
{
|
|
if (_currentView != nil)
|
|
return;
|
|
|
|
_currentView = [self viewForLocation:location];
|
|
}
|
|
break;
|
|
|
|
case UIGestureRecognizerStateChanged:
|
|
{
|
|
if (_currentView == nil)
|
|
return;
|
|
|
|
CGFloat scale = gestureRecognizer.scale;
|
|
[_currentView scale:scale absolute:false];
|
|
|
|
[gestureRecognizer setScale:1.0f];
|
|
}
|
|
break;
|
|
|
|
case UIGestureRecognizerStateEnded:
|
|
{
|
|
_currentView = nil;
|
|
}
|
|
break;
|
|
|
|
case UIGestureRecognizerStateCancelled:
|
|
{
|
|
_currentView = nil;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (void)handleRotate:(UIRotationGestureRecognizer *)gestureRecognizer
|
|
{
|
|
CGPoint location = [gestureRecognizer locationInView:self];
|
|
|
|
switch (gestureRecognizer.state)
|
|
{
|
|
case UIGestureRecognizerStateBegan:
|
|
{
|
|
if (_currentView != nil)
|
|
return;
|
|
|
|
_currentView = [self viewForLocation:location];
|
|
}
|
|
break;
|
|
|
|
case UIGestureRecognizerStateChanged:
|
|
{
|
|
if (_currentView == nil)
|
|
return;
|
|
|
|
CGFloat rotation = gestureRecognizer.rotation;
|
|
[_currentView rotate:rotation absolute:false];
|
|
|
|
[gestureRecognizer setRotation:0.0f];
|
|
}
|
|
break;
|
|
|
|
case UIGestureRecognizerStateEnded:
|
|
{
|
|
|
|
}
|
|
break;
|
|
|
|
case UIGestureRecognizerStateCancelled:
|
|
{
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (TGPhotoPaintEntityView *)viewForLocation:(CGPoint)__unused location
|
|
{
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if (view.selectionView != nil)
|
|
return view;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (UIImage *)imageInRect:(CGRect)rect background:(UIImage *)background still:(bool)still
|
|
{
|
|
if (self.subviews.count < 2)
|
|
return nil;
|
|
|
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(rect.size.width, rect.size.height), false, 1.0f);
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
|
|
CGRect bounds = CGRectMake(0, 0, rect.size.width, rect.size.height);
|
|
[background drawInRect:bounds];
|
|
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if ([view isKindOfClass:[TGPhotoStickerEntityView class]])
|
|
{
|
|
[self drawView:view inContext:context withBlock:^
|
|
{
|
|
TGPhotoStickerEntityView *stickerView = (TGPhotoStickerEntityView *)view;
|
|
UIImage *image = stickerView.image;
|
|
if (image != nil) {
|
|
CGSize fittedSize = TGScaleToSize(image.size, view.bounds.size);
|
|
|
|
CGContextTranslateCTM(context, view.bounds.size.width / 2.0f, view.bounds.size.height / 2.0f);
|
|
if (stickerView.isMirrored)
|
|
CGContextScaleCTM(context, -1, 1);
|
|
|
|
[image drawInRect:CGRectMake(-fittedSize.width / 2.0f, -fittedSize.height / 2.0f, fittedSize.width, fittedSize.height)];
|
|
}
|
|
}];
|
|
}
|
|
else if ([view isKindOfClass:[TGPhotoTextEntityView class]])
|
|
{
|
|
[self drawView:view inContext:context withBlock:^
|
|
{
|
|
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:false];
|
|
}];
|
|
}
|
|
}
|
|
|
|
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
|
|
return image;
|
|
}
|
|
|
|
- (void)drawView:(UIView *)view inContext:(CGContextRef)context withBlock:(void (^)(void))block
|
|
{
|
|
CGContextSaveGState(context);
|
|
|
|
CGContextTranslateCTM(context, view.center.x, view.center.y);
|
|
CGContextConcatCTM(context, view.transform);
|
|
CGContextTranslateCTM(context, -view.bounds.size.width / 2.0f, -view.bounds.size.height / 2.0f);
|
|
|
|
block();
|
|
|
|
CGContextRestoreGState(context);
|
|
}
|
|
|
|
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
|
|
{
|
|
bool pointInside = [super pointInside:point withEvent:event];
|
|
if (!pointInside)
|
|
{
|
|
for (UIView *subview in self.subviews)
|
|
{
|
|
CGPoint convertedPoint = [self convertPoint:point toView:subview];
|
|
if ([subview pointInside:convertedPoint withEvent:event])
|
|
pointInside = true;
|
|
}
|
|
}
|
|
return pointInside;
|
|
}
|
|
|
|
- (bool)isTrackingAnyEntityView
|
|
{
|
|
bool tracking = false;
|
|
for (TGPhotoPaintEntityView *view in self.subviews)
|
|
{
|
|
if (![view isKindOfClass:[TGPhotoPaintEntityView class]])
|
|
continue;
|
|
|
|
if (view.isTracking)
|
|
{
|
|
tracking = true;
|
|
break;
|
|
}
|
|
}
|
|
return tracking;
|
|
}
|
|
|
|
@end
|