#import "TGPhotoEntitiesContainerView.h" #import "TGPhotoPaintEntityView.h" #import "TGPhotoStickerEntityView.h" #import "TGPhotoTextEntityView.h" #import "TGPaintingData.h" #import @interface TGPhotoEntitiesContainerView () { 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) { [self createEntityViewWithEntity:entity]; } } - (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]; [self addSubview:stickerView]; return stickerView; } - (TGPhotoTextEntityView *)_createTextViewWithEntity:(TGPhotoPaintTextEntity *)entity { TGPhotoTextEntityView *textView = [[TGPhotoTextEntityView alloc] initWithEntity:entity]; [textView sizeToFit]; [self _commonEntityViewSetup:textView entity:entity]; [self addSubview:textView]; 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