256 lines
8.6 KiB
Mathematica
256 lines
8.6 KiB
Mathematica
|
|
// Copyright (c) 2024 Tencent. All rights reserved.
|
|||
|
|
// Author: eddardliu
|
|||
|
|
|
|||
|
|
#import "TUIMultimediaStickerView.h"
|
|||
|
|
#import <TUICore/TUIThemeManager.h>
|
|||
|
|
#import "Masonry/Masonry.h"
|
|||
|
|
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
|||
|
|
#import "TUIMultimediaPlugin/TUIMultimediaGeometry.h"
|
|||
|
|
#import "TUIMultimediaPlugin/TUIMultimediaImageUtil.h"
|
|||
|
|
|
|||
|
|
@interface TUIMultimediaStickerView () <UIGestureRecognizerDelegate> {
|
|||
|
|
UIButton *_btnEdit;
|
|||
|
|
UIButton *_btnTransform;
|
|||
|
|
UIButton *_btnDelete;
|
|||
|
|
UIView *_borderView;
|
|||
|
|
BOOL _hideEditButton;
|
|||
|
|
BOOL _active;
|
|||
|
|
|
|||
|
|
CGFloat _rawRotation;
|
|||
|
|
|
|||
|
|
UITapGestureRecognizer *_tapRec;
|
|||
|
|
UITapGestureRecognizer *_doubleTapRec;
|
|||
|
|
UIPanGestureRecognizer *_panRec;
|
|||
|
|
UIPinchGestureRecognizer *_pinchRec;
|
|||
|
|
UIRotationGestureRecognizer *_rotationRec;
|
|||
|
|
UIPanGestureRecognizer *_btnTransformPanRec;
|
|||
|
|
UIImpactFeedbackGenerator *_impactGen;
|
|||
|
|
}
|
|||
|
|
@end
|
|||
|
|
|
|||
|
|
@implementation TUIMultimediaStickerView
|
|||
|
|
- (id)initWithFrame:(CGRect)frame {
|
|||
|
|
self = [super initWithFrame:frame];
|
|||
|
|
if (self != nil) {
|
|||
|
|
_contentMargin = 5;
|
|||
|
|
_rotateAdsorptionLimitAngle = M_PI / 180 * 5;
|
|||
|
|
_impactGen = [[UIImpactFeedbackGenerator alloc] init];
|
|||
|
|
[self frv_initUI];
|
|||
|
|
}
|
|||
|
|
return self;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)frv_initUI {
|
|||
|
|
_borderView = [[UIView alloc] init];
|
|||
|
|
_borderView.translatesAutoresizingMaskIntoConstraints = NO;
|
|||
|
|
_borderView.layer.borderWidth = 1;
|
|||
|
|
_borderView.layer.borderColor = UIColor.whiteColor.CGColor;
|
|||
|
|
_borderView.backgroundColor = [UIColor clearColor];
|
|||
|
|
_borderView.hidden = YES;
|
|||
|
|
[self addSubview:_borderView];
|
|||
|
|
[_borderView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|||
|
|
make.edges.equalTo(self);
|
|||
|
|
}];
|
|||
|
|
|
|||
|
|
_btnEdit = [self newCornerButtonWithImage:TUIMultimediaPluginBundleThemeImage(@"sticker_edit_img", @"round_edit") left:YES top:YES];
|
|||
|
|
_btnTransform = [self newCornerButtonWithImage:[TUIMultimediaImageUtil rotateImage:TUIMultimediaPluginBundleThemeImage(@"sticker_transform_img", @"round_transform") angle:90.0f] left:NO top:NO];
|
|||
|
|
_btnDelete = [self newCornerButtonWithImage:TUIMultimediaPluginBundleThemeImage(@"sticker_delete_img", @"round_close") left:NO top:YES];
|
|||
|
|
|
|||
|
|
[_btnEdit addTarget:self action:@selector(onBtnEditClicked) forControlEvents:UIControlEventTouchUpInside];
|
|||
|
|
[_btnDelete addTarget:self action:@selector(onBtnDeleteClicked) forControlEvents:UIControlEventTouchUpInside];
|
|||
|
|
|
|||
|
|
_tapRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap)];
|
|||
|
|
[self addGestureRecognizer:_tapRec];
|
|||
|
|
|
|||
|
|
_panRec = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPan:)];
|
|||
|
|
_panRec.delegate = self;
|
|||
|
|
[self addGestureRecognizer:_panRec];
|
|||
|
|
|
|||
|
|
_pinchRec = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(onPinch:)];
|
|||
|
|
_pinchRec.delegate = self;
|
|||
|
|
[self addGestureRecognizer:_pinchRec];
|
|||
|
|
|
|||
|
|
_rotationRec = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(onGestureRotation:)];
|
|||
|
|
_rotationRec.delegate = self;
|
|||
|
|
[self addGestureRecognizer:_rotationRec];
|
|||
|
|
|
|||
|
|
_btnTransformPanRec = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onBtnTransformPan:)];
|
|||
|
|
[_btnTransform addGestureRecognizer:_btnTransformPanRec];
|
|||
|
|
|
|||
|
|
_doubleTapRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onDoubleTap:)];
|
|||
|
|
_doubleTapRec.delaysTouchesEnded = NO;
|
|||
|
|
_doubleTapRec.numberOfTapsRequired = 2;
|
|||
|
|
[self addGestureRecognizer:_doubleTapRec];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (UIButton *)newCornerButtonWithImage:(UIImage *)image left:(BOOL)left top:(BOOL)top {
|
|||
|
|
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
|
|||
|
|
btn.hidden = YES;
|
|||
|
|
[self addSubview:btn];
|
|||
|
|
btn.translatesAutoresizingMaskIntoConstraints = NO;
|
|||
|
|
[btn setImage:image forState:UIControlStateNormal];
|
|||
|
|
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
|
|||
|
|
make.width.height.mas_equalTo(24);
|
|||
|
|
make.centerX.equalTo(left ? self.mas_left : self.mas_right);
|
|||
|
|
make.centerY.equalTo(top ? self.mas_top : self.mas_bottom);
|
|||
|
|
}];
|
|||
|
|
return btn;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)layoutSubviews {
|
|||
|
|
[super layoutSubviews];
|
|||
|
|
[_delegate onStickerViewSizeChanged:self];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)updateRotation {
|
|||
|
|
// 规范化到[0,2pi)
|
|||
|
|
_rawRotation -= floor(_rawRotation / (M_PI * 2)) * M_PI * 2;
|
|||
|
|
BOOL adsorped = NO;
|
|||
|
|
// 吸附到 0,90,180,270度
|
|||
|
|
for (int i = 0; i <= 3; i++) {
|
|||
|
|
CGFloat diff = fabs(_rawRotation - i * M_PI_2);
|
|||
|
|
CGFloat diff2 = fabs(_rawRotation - M_PI * 2 - i * M_PI_2);
|
|||
|
|
if (diff < _rotateAdsorptionLimitAngle || diff2 < _rotateAdsorptionLimitAngle) {
|
|||
|
|
adsorped = YES;
|
|||
|
|
if (_rotation != i * M_PI_2) {
|
|||
|
|
_rotation = i * M_PI_2;
|
|||
|
|
[_impactGen impactOccurred];
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (!adsorped) {
|
|||
|
|
_rotation = _rawRotation;
|
|||
|
|
}
|
|||
|
|
self.transform = CGAffineTransformMakeRotation(_rotation);
|
|||
|
|
NSLog(@"updateRotation %@",NSStringFromCGRect(self.frame));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#pragma mark - Actions
|
|||
|
|
- (void)onTap {
|
|||
|
|
if (!self.selected) {
|
|||
|
|
self.selected = YES;
|
|||
|
|
[_delegate onStickerViewSelected:self];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)onDoubleTap:(UITapGestureRecognizer *)rec {
|
|||
|
|
[_delegate onStickerViewShouldEdit:self];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)onPan:(UIPanGestureRecognizer *)rec {
|
|||
|
|
CGPoint offset = [rec translationInView:self.superview];
|
|||
|
|
[rec setTranslation:CGPointZero inView:self.superview];
|
|||
|
|
// 平移
|
|||
|
|
self.center = Vec2AddVector(self.center, offset);
|
|||
|
|
NSLog(@"onPan %@",NSStringFromCGRect(self.frame));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
- (void)onPinch:(UIPinchGestureRecognizer *)rec {
|
|||
|
|
// pow用于加快放大/缩小速度,优化体验
|
|||
|
|
CGFloat scale = pow(rec.scale, 1.5);
|
|||
|
|
rec.scale = 1;
|
|||
|
|
CGSize raw = self.bounds.size;
|
|||
|
|
self.bounds = CGRectMake(0, 0, raw.width * scale, raw.height * scale);
|
|||
|
|
NSLog(@"onPinch %@",NSStringFromCGRect(self.frame));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)scale:(CGFloat) scale {
|
|||
|
|
CGSize raw = self.bounds.size;
|
|||
|
|
self.bounds = CGRectMake(0, 0, raw.width * scale, raw.height * scale);
|
|||
|
|
self.center = CGPointMake(self.center.x * scale, self.center.y * scale);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)onGestureRotation:(UIRotationGestureRecognizer *)rec {
|
|||
|
|
_rawRotation += rec.rotation;
|
|||
|
|
[self updateRotation];
|
|||
|
|
rec.rotation = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)onBtnTransformPan:(UIPanGestureRecognizer *)rec {
|
|||
|
|
CGPoint offset = [rec translationInView:self.superview];
|
|||
|
|
[rec setTranslation:CGPointZero inView:self.superview];
|
|||
|
|
|
|||
|
|
CGPoint originSize = CGPointMake(self.bounds.size.width, self.bounds.size.height);
|
|||
|
|
CGPoint originV = Vec2Rotate(Vec2Mul(originSize, 0.5), _rawRotation);
|
|||
|
|
CGPoint newV = Vec2AddVector(originV, offset);
|
|||
|
|
// 缩放
|
|||
|
|
CGPoint newSize = Vec2Mul(originSize, Vec2Len(newV) / Vec2Len(originV));
|
|||
|
|
self.bounds = CGRectMake(0, 0, newSize.x, newSize.y);
|
|||
|
|
// 旋转
|
|||
|
|
_rawRotation += Vec2Degree(originV, newV);
|
|||
|
|
[self updateRotation];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
|
|||
|
|
if (CGRectContainsPoint(self.bounds, point)) {
|
|||
|
|
return YES;
|
|||
|
|
}
|
|||
|
|
for (UIView *v in self.subviews) {
|
|||
|
|
CGPoint localPoint = [v convertPoint:point fromView:self];
|
|||
|
|
if (CGRectContainsPoint(v.bounds, localPoint)) {
|
|||
|
|
return YES;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return NO;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)onBtnEditClicked {
|
|||
|
|
[_delegate onStickerViewShouldEdit:self];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)onBtnDeleteClicked {
|
|||
|
|
[_delegate onStickerViewShouldDelete:self];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#pragma mark - Properties
|
|||
|
|
- (BOOL)editButtonHidden {
|
|||
|
|
return _hideEditButton;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setEditButtonHidden:(BOOL)hideEditButton {
|
|||
|
|
_hideEditButton = hideEditButton;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setContent:(UIView *)content {
|
|||
|
|
[_content removeFromSuperview];
|
|||
|
|
_content = content;
|
|||
|
|
if (content == nil) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
self.bounds = CGRectMake(0, 0, content.bounds.size.width + _contentMargin * 2, content.bounds.size.height + _contentMargin * 2);
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
// [self mas_makeConstraints:^(MASConstraintMaker *make) {
|
|||
|
|
// make.center.equalTo(self.superview);
|
|||
|
|
// }];
|
|||
|
|
|
|||
|
|
|
|||
|
|
[self addSubview:content];
|
|||
|
|
[self bringSubviewToFront:_btnEdit];
|
|||
|
|
[self bringSubviewToFront:_btnTransform];
|
|||
|
|
[self bringSubviewToFront:_btnDelete];
|
|||
|
|
[content mas_makeConstraints:^(MASConstraintMaker *make) {
|
|||
|
|
make.edges.equalTo(self).inset(_contentMargin);
|
|||
|
|
}];
|
|||
|
|
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setSelected:(BOOL)selected {
|
|||
|
|
_selected = selected;
|
|||
|
|
_borderView.hidden = !selected;
|
|||
|
|
_btnEdit.hidden = !selected || _hideEditButton;
|
|||
|
|
_btnTransform.hidden = !selected;
|
|||
|
|
_btnDelete.hidden = !selected;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#pragma mark - UIGestureRecognizerDelegate protocol
|
|||
|
|
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)g1 shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)g2 {
|
|||
|
|
NSArray<UIGestureRecognizer *> *recList = @[ _pinchRec, _rotationRec, _panRec ];
|
|||
|
|
return [recList containsObject:g1] && [recList containsObject:g2];
|
|||
|
|
}
|
|||
|
|
@end
|