提交
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user