增加换肤功能
This commit is contained in:
29
TUIKit/TUIChat/CommonUI/Pop/TUIChatFlexViewController.h
Normal file
29
TUIKit/TUIChat/CommonUI/Pop/TUIChatFlexViewController.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// TUIChatFlexViewController.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/10/27.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIChatFlexViewController : UIViewController
|
||||
|
||||
@property(nonatomic, strong) UIView *topGestureView;
|
||||
@property(nonatomic, strong) UIImageView *topImgView;
|
||||
|
||||
@property(nonatomic, strong) UIView *containerView;
|
||||
|
||||
- (void)updateSubContainerView;
|
||||
|
||||
- (void)setnormalTop;
|
||||
|
||||
- (void)setNormalBottom;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
163
TUIKit/TUIChat/CommonUI/Pop/TUIChatFlexViewController.m
Normal file
163
TUIKit/TUIChat/CommonUI/Pop/TUIChatFlexViewController.m
Normal file
@@ -0,0 +1,163 @@
|
||||
//
|
||||
// TUIChatFlexViewController.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/10/27.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIChatFlexViewController.h"
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
FLEX_TOP,
|
||||
FLEX_Bottom,
|
||||
} FLEX_Location;
|
||||
|
||||
CGFloat topMargin = NavBar_Height + 30;
|
||||
|
||||
@interface TUIChatFlexViewController ()
|
||||
|
||||
@property(nonatomic, assign) FLEX_Location currentLoaction;
|
||||
|
||||
@property(nonatomic, strong) UIPanGestureRecognizer *panCover;
|
||||
|
||||
@property(nonatomic, strong) UITapGestureRecognizer *singleTap;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIChatFlexViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:.5];
|
||||
|
||||
self.containerView.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
self.topImgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:TUIChatImagePath_Minimalist(@"icon_flex_arrow")]];
|
||||
[self.topGestureView addSubview:self.topImgView];
|
||||
|
||||
[self addSingleTapGesture];
|
||||
|
||||
if (!_currentLoaction) {
|
||||
self.currentLoaction = FLEX_TOP;
|
||||
}
|
||||
|
||||
[self updateSubContainerView];
|
||||
}
|
||||
|
||||
- (void)addSingleTapGesture {
|
||||
// When clicking on the shadow, the page disappears
|
||||
self.view.userInteractionEnabled = YES;
|
||||
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
|
||||
singleTap.cancelsTouchesInView = NO;
|
||||
[self.view addGestureRecognizer:singleTap];
|
||||
}
|
||||
|
||||
- (void)singleTap:(UITapGestureRecognizer *)tap {
|
||||
CGPoint translation = [tap locationInView:self.containerView];
|
||||
|
||||
if (translation.x < 0 || translation.y < 0) {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
} else if (translation.x > self.containerView.frame.size.width || translation.y > self.containerView.frame.size.height) {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setnormalTop {
|
||||
self.currentLoaction = FLEX_TOP;
|
||||
}
|
||||
|
||||
- (void)setNormalBottom {
|
||||
self.currentLoaction = FLEX_Bottom;
|
||||
}
|
||||
|
||||
- (void)setCurrentLoaction:(FLEX_Location)currentLoaction {
|
||||
_currentLoaction = currentLoaction;
|
||||
if (currentLoaction == FLEX_TOP) {
|
||||
self.containerView.frame = CGRectMake(0, topMargin, self.view.frame.size.width, self.view.frame.size.height - topMargin);
|
||||
} else if (currentLoaction == FLEX_Bottom) {
|
||||
self.containerView.frame = CGRectMake(0, self.view.frame.size.height - kScale390(393), self.view.frame.size.width, kScale390(393));
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - lazy
|
||||
|
||||
- (UIView *)containerView {
|
||||
if (_containerView == nil) {
|
||||
_containerView = [[UIView alloc] init];
|
||||
_containerView.layer.cornerRadius = kScale390(12);
|
||||
[self.view addSubview:_containerView];
|
||||
}
|
||||
return _containerView;
|
||||
}
|
||||
|
||||
- (UIView *)topGestureView {
|
||||
if (_topGestureView == nil) {
|
||||
_topGestureView = [[UIView alloc] init];
|
||||
[_topGestureView addGestureRecognizer:self.panCover];
|
||||
[self.containerView addSubview:_topGestureView];
|
||||
}
|
||||
return _topGestureView;
|
||||
}
|
||||
|
||||
- (UIPanGestureRecognizer *)panCover {
|
||||
if (_panCover == nil) {
|
||||
_panCover = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPanCover:)];
|
||||
}
|
||||
return _panCover;
|
||||
}
|
||||
|
||||
- (void)onPanCover:(UIPanGestureRecognizer *)pan {
|
||||
CGPoint translation = [pan translationInView:self.topGestureView];
|
||||
CGFloat absX = fabs(translation.x);
|
||||
CGFloat absY = fabs(translation.y);
|
||||
|
||||
if (MAX(absX, absY) < 2) return;
|
||||
if (absX > absY) {
|
||||
if (translation.x < 0) {
|
||||
// scroll left
|
||||
} else {
|
||||
// scroll right
|
||||
}
|
||||
} else if (absY > absX) {
|
||||
if (translation.y < 0) {
|
||||
// scroll up
|
||||
[self.topGestureView removeGestureRecognizer:self.panCover];
|
||||
[UIView animateWithDuration:0.3
|
||||
animations:^{
|
||||
self.currentLoaction = FLEX_TOP;
|
||||
[self.topGestureView addGestureRecognizer:self.panCover];
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
[self updateSubContainerView];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
// scroll down
|
||||
if (self.currentLoaction == FLEX_Bottom) {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
[self.topGestureView removeGestureRecognizer:self.panCover];
|
||||
[UIView animateWithDuration:0.3
|
||||
animations:^{
|
||||
self.currentLoaction = FLEX_Bottom;
|
||||
[self.topGestureView addGestureRecognizer:self.panCover];
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self updateSubContainerView];
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateSubContainerView {
|
||||
self.topGestureView.frame = CGRectMake(0, 0, self.containerView.frame.size.width, kScale390(40));
|
||||
self.topImgView.frame = CGRectMake((self.topGestureView.frame.size.width - kScale390(24)) * 0.5, kScale390(22), kScale390(24), kScale390(6));
|
||||
}
|
||||
@end
|
||||
48
TUIKit/TUIChat/CommonUI/Pop/TUIChatPopContextController.h
Normal file
48
TUIKit/TUIChat/CommonUI/Pop/TUIChatPopContextController.h
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// TUIChatPopContextController.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/10/24.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIMessageCell.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIChatPopContextExtionView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, BlurEffectStyle) {
|
||||
BlurEffectStyleLight,
|
||||
BlurEffectStyleExtraLight,
|
||||
BlurEffectStyleDarkEffect,
|
||||
};
|
||||
@interface TUIChatPopContextController : UIViewController
|
||||
|
||||
@property(nonatomic, strong) Class alertCellClass;
|
||||
|
||||
@property(nonatomic, strong) TUIMessageCellData *alertViewCellData;
|
||||
|
||||
@property(nonatomic, assign) CGRect originFrame;
|
||||
|
||||
@property(copy, nonatomic) void (^viewWillShowHandler)(TUIMessageCell *alertView);
|
||||
|
||||
@property(copy, nonatomic) void (^viewDidShowHandler)(TUIMessageCell *alertView);
|
||||
|
||||
// dismiss controller completed block
|
||||
@property(nonatomic, copy) void (^dismissComplete)(void);
|
||||
|
||||
@property(nonatomic, copy) void (^reactClickCallback)(NSString *faceName);
|
||||
|
||||
@property(nonatomic, strong) NSMutableArray<TUIChatPopContextExtionItem *> *items;
|
||||
|
||||
- (void)setBlurEffectWithView:(UIView *)view;
|
||||
|
||||
- (void)blurDismissViewControllerAnimated:(BOOL)animated completion:(void (^__nullable)(BOOL finished))completion;
|
||||
|
||||
- (void)updateExtionView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
462
TUIKit/TUIChat/CommonUI/Pop/TUIChatPopContextController.m
Normal file
462
TUIKit/TUIChat/CommonUI/Pop/TUIChatPopContextController.m
Normal file
@@ -0,0 +1,462 @@
|
||||
//
|
||||
// TUIChatPopContextController.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/10/24.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIChatPopContextController.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import "UIImage+ImageEffects.h"
|
||||
#import <TUICore/TUICore.h>
|
||||
|
||||
@interface TUIChatPopContextController ()<V2TIMAdvancedMsgListener>
|
||||
@property(nonatomic, strong) UIView *recentView;
|
||||
@property(nonatomic, strong) UIView *alertContainerView;
|
||||
@property(nonatomic, strong) TUIMessageCell *alertView;
|
||||
@property(nonatomic, strong) TUIChatPopContextExtionView *extionView;
|
||||
|
||||
@property(nonatomic, strong) UIColor *backgroundColor; // set backgroundColor
|
||||
@property(nonatomic, strong) UIView *backgroundView; // you set coustom view to it
|
||||
@property(nonatomic, strong) UITapGestureRecognizer *singleTap;
|
||||
@property(nonatomic, assign) BOOL backgoundTapDismissEnable; // default NO
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIChatPopContextController
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
[self configureController];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configureController {
|
||||
self.providesPresentationContextTransitionStyle = YES;
|
||||
self.definesPresentationContext = YES;
|
||||
self.modalPresentationStyle = UIModalPresentationCustom;
|
||||
|
||||
_backgroundColor = [UIColor clearColor];
|
||||
_backgoundTapDismissEnable = YES;
|
||||
[[V2TIMManager sharedInstance] addAdvancedMsgListener:self];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
|
||||
[self addBackgroundView];
|
||||
|
||||
[self addSingleTapGesture];
|
||||
|
||||
[self configureAlertView];
|
||||
|
||||
[self configRecentView];
|
||||
|
||||
[self configExtionView];
|
||||
|
||||
[self.view layoutIfNeeded];
|
||||
|
||||
[self showHapticFeedback];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (_viewWillShowHandler) {
|
||||
_viewWillShowHandler(_alertView);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
if (_viewDidShowHandler) {
|
||||
_viewDidShowHandler(_alertView);
|
||||
}
|
||||
|
||||
//
|
||||
// Too far to the top
|
||||
CGFloat moveY = 0;
|
||||
if (self.recentView.frame.origin.y < NavBar_Height) {
|
||||
CGFloat deal = NavBar_Height - self.recentView.frame.origin.y;
|
||||
moveY = deal + NavBar_Height + 50;
|
||||
}
|
||||
//
|
||||
// Too far to the right
|
||||
CGFloat moveX = 0;
|
||||
if (self.recentView.frame.origin.x + self.recentView.frame.size.width > self.view.frame.size.width) {
|
||||
CGFloat deal = self.recentView.frame.origin.x + self.recentView.frame.size.width - self.view.frame.size.width;
|
||||
moveX = deal + 5;
|
||||
}
|
||||
//
|
||||
// too far down
|
||||
if (self.extionView.frame.origin.y + self.extionView.frame.size.height > self.view.frame.size.height) {
|
||||
CGFloat deal = self.extionView.frame.origin.y + self.extionView.frame.size.height - self.view.frame.size.height;
|
||||
moveY = -deal - 50;
|
||||
}
|
||||
|
||||
BOOL oneScreenCanFillCheck = NO;
|
||||
// Can only one screen fit
|
||||
if (self.recentView.frame.size.height + self.originFrame.size.height + self.extionView.frame.size.height + kScale390(100) > self.view.bounds.size.height) {
|
||||
oneScreenCanFillCheck = YES;
|
||||
}
|
||||
|
||||
if (oneScreenCanFillCheck) {
|
||||
// recentView
|
||||
CGFloat recentViewMoveY = NavBar_Height + 50;
|
||||
|
||||
self.recentView.frame =
|
||||
CGRectMake(self.recentView.frame.origin.x - moveX, recentViewMoveY, self.recentView.frame.size.width, self.recentView.frame.size.height);
|
||||
|
||||
// alertView
|
||||
[UIView animateWithDuration:0.3
|
||||
animations:^{
|
||||
self.alertContainerView.frame =
|
||||
CGRectMake(0, self.recentView.frame.origin.y + kScale390(8) + self.recentView.frame.size.height,
|
||||
self.view.frame.size.width, self.originFrame.size.height);
|
||||
}
|
||||
completion:^(BOOL finished){
|
||||
|
||||
}];
|
||||
|
||||
// extionView
|
||||
CGFloat deal = self.extionView.frame.origin.y + self.extionView.frame.size.height - self.view.frame.size.height;
|
||||
|
||||
CGFloat extionViewMoveY = -deal - 50;
|
||||
|
||||
self.extionView.frame = CGRectMake(self.extionView.frame.origin.x - moveX, self.extionView.frame.origin.y + extionViewMoveY,
|
||||
self.extionView.frame.size.width, self.extionView.frame.size.height);
|
||||
self.extionView.transform = CGAffineTransformMakeScale(0.1, 0.1);
|
||||
[UIView animateWithDuration:0.5
|
||||
animations:^{
|
||||
// Bounces
|
||||
self.extionView.transform = CGAffineTransformMakeScale(1, 1);
|
||||
}
|
||||
completion:^(BOOL finished){
|
||||
|
||||
}];
|
||||
|
||||
return;
|
||||
} else {
|
||||
// When the container need a displacement change
|
||||
// Or do nothing
|
||||
if (moveY != 0) {
|
||||
[UIView animateWithDuration:0.3
|
||||
animations:^{
|
||||
self.alertContainerView.frame = CGRectMake(0, self.originFrame.origin.y + moveY, self.view.frame.size.width, self.originFrame.size.height);
|
||||
}
|
||||
completion:^(BOOL finished){
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
self.recentView.frame = CGRectMake(self.recentView.frame.origin.x - moveX, self.recentView.frame.origin.y, self.recentView.frame.size.width,
|
||||
self.recentView.frame.size.height);
|
||||
[UIView animateWithDuration:0.2
|
||||
animations:^{
|
||||
// When recentView needs to have displacement animation
|
||||
self.recentView.frame = CGRectMake(self.recentView.frame.origin.x, self.recentView.frame.origin.y + moveY,
|
||||
self.recentView.frame.size.width, self.recentView.frame.size.height);
|
||||
}
|
||||
completion:^(BOOL finished){
|
||||
|
||||
}];
|
||||
|
||||
self.extionView.frame = CGRectMake(self.extionView.frame.origin.x - moveX, self.extionView.frame.origin.y + moveY, self.extionView.frame.size.width,
|
||||
self.extionView.frame.size.height);
|
||||
self.extionView.transform = CGAffineTransformMakeScale(0.1, 0.1);
|
||||
[UIView animateWithDuration:0.5
|
||||
animations:^{
|
||||
// Bounces
|
||||
self.extionView.transform = CGAffineTransformMakeScale(1, 1);
|
||||
}
|
||||
completion:^(BOOL finished){
|
||||
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addBackgroundView {
|
||||
if (_backgroundView == nil) {
|
||||
UIView *backgroundView = [[UIView alloc] init];
|
||||
backgroundView.backgroundColor = _backgroundColor;
|
||||
_backgroundView = backgroundView;
|
||||
}
|
||||
_backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.view insertSubview:_backgroundView atIndex:0];
|
||||
[self addConstraintToView:_backgroundView edgeInset:UIEdgeInsetsZero];
|
||||
}
|
||||
|
||||
- (void)setBackgroundView:(UIView *)backgroundView {
|
||||
if (_backgroundView == nil) {
|
||||
_backgroundView = backgroundView;
|
||||
} else if (_backgroundView != backgroundView) {
|
||||
backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.view insertSubview:backgroundView aboveSubview:_backgroundView];
|
||||
[self addConstraintToView:backgroundView edgeInset:UIEdgeInsetsZero];
|
||||
backgroundView.alpha = 0;
|
||||
[UIView animateWithDuration:0.3
|
||||
animations:^{
|
||||
backgroundView.alpha = 1;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
[_backgroundView removeFromSuperview];
|
||||
_backgroundView = backgroundView;
|
||||
[self addSingleTapGesture];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addSingleTapGesture {
|
||||
self.view.userInteractionEnabled = YES;
|
||||
_backgroundView.userInteractionEnabled = YES;
|
||||
|
||||
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
|
||||
singleTap.enabled = _backgoundTapDismissEnable;
|
||||
|
||||
[_backgroundView addGestureRecognizer:singleTap];
|
||||
_singleTap = singleTap;
|
||||
}
|
||||
|
||||
- (void)configureAlertView {
|
||||
self.alertContainerView = [[UIView alloc] init];
|
||||
[self.view addSubview:self.alertContainerView];
|
||||
_alertView = [[self.alertCellClass alloc] init];
|
||||
[self.alertContainerView addSubview:_alertView];
|
||||
_alertView.userInteractionEnabled = YES;
|
||||
if ([self.alertView isKindOfClass:NSClassFromString(@"TUIMergeMessageCell_Minimalist")]) {
|
||||
_alertView.userInteractionEnabled = NO;
|
||||
}
|
||||
[_alertView fillWithData:self.alertViewCellData];
|
||||
[_alertView layoutIfNeeded];
|
||||
|
||||
self.alertContainerView.frame = CGRectMake(0, _originFrame.origin.y, self.view.frame.size.width, _originFrame.size.height);
|
||||
_alertView.frame = CGRectMake(0, 0, self.alertContainerView.frame.size.width, self.alertContainerView.frame.size.height);
|
||||
for (UIView *view in _alertView.contentView.subviews) {
|
||||
if(view != _alertView.container) {
|
||||
view.hidden = YES;
|
||||
}
|
||||
}
|
||||
[_alertView.container mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(_originFrame.origin.x);
|
||||
make.top.mas_equalTo(0);
|
||||
make.size.mas_equalTo(_originFrame.size);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)configRecentView {
|
||||
_recentView = [[UIView alloc] init];
|
||||
_recentView.backgroundColor = [UIColor clearColor];
|
||||
[self.view addSubview:_recentView];
|
||||
_recentView.frame = CGRectMake(_originFrame.origin.x,
|
||||
_originFrame.origin.y - kScale390(8 + 40),
|
||||
MAX(kTIMDefaultEmojiSize.width *8,kScale390(208)),
|
||||
kScale390(40));
|
||||
NSDictionary *param = @{TUICore_TUIChatExtension_ChatPopMenuReactRecentView_Delegate : self};
|
||||
[TUICore raiseExtension:TUICore_TUIChatExtension_ChatPopMenuReactRecentView_MinimalistExtensionID parentView:self.recentView param:param];
|
||||
|
||||
}
|
||||
|
||||
- (void)configExtionView {
|
||||
_extionView = [[TUIChatPopContextExtionView alloc] init];
|
||||
_extionView.backgroundColor = [UIColor tui_colorWithHex:@"f9f9f9"];
|
||||
_extionView.layer.cornerRadius = kScale390(16);
|
||||
[self.view addSubview:_extionView];
|
||||
|
||||
CGFloat height = [self configAndCaculateExtionHeight];
|
||||
_extionView.frame = CGRectMake(_originFrame.origin.x, _originFrame.origin.y + _originFrame.size.height + kScale390(8), kScale390(180), height);
|
||||
}
|
||||
|
||||
- (CGFloat)configAndCaculateExtionHeight {
|
||||
NSMutableArray *items = self.items;
|
||||
CGFloat height = 0;
|
||||
for (int i = 0; i < items.count; i++) {
|
||||
height += kScale390(40);
|
||||
}
|
||||
|
||||
CGFloat topMargin = kScale390(6);
|
||||
CGFloat bottomMargin = kScale390(6);
|
||||
height += topMargin;
|
||||
height += bottomMargin;
|
||||
|
||||
[_extionView configUIWithItems:items topBottomMargin:topMargin];
|
||||
return height;
|
||||
}
|
||||
|
||||
- (void)updateExtionView {
|
||||
CGFloat height = [self configAndCaculateExtionHeight];
|
||||
_extionView.frame = CGRectMake(_extionView.frame.origin.x, _extionView.frame.origin.y, _extionView.frame.size.width, height);
|
||||
}
|
||||
|
||||
- (void)setBackgoundTapDismissEnable:(BOOL)backgoundTapDismissEnable {
|
||||
_backgoundTapDismissEnable = backgoundTapDismissEnable;
|
||||
_singleTap.enabled = backgoundTapDismissEnable;
|
||||
}
|
||||
|
||||
- (void)addConstraintToView:(UIView *)view edgeInset:(UIEdgeInsets)edgeInset {
|
||||
[self addConstraintWithView:view topView:self.view leftView:self.view bottomView:self.view rightView:self.view edgeInset:edgeInset];
|
||||
}
|
||||
|
||||
- (void)addConstraintWithView:(UIView *)view
|
||||
topView:(UIView *)topView
|
||||
leftView:(UIView *)leftView
|
||||
bottomView:(UIView *)bottomView
|
||||
rightView:(UIView *)rightView
|
||||
edgeInset:(UIEdgeInsets)edgeInset {
|
||||
if (topView) {
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:topView
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1
|
||||
constant:edgeInset.top]];
|
||||
}
|
||||
|
||||
if (leftView) {
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view
|
||||
attribute:NSLayoutAttributeLeft
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:leftView
|
||||
attribute:NSLayoutAttributeLeft
|
||||
multiplier:1
|
||||
constant:edgeInset.left]];
|
||||
}
|
||||
|
||||
if (rightView) {
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view
|
||||
attribute:NSLayoutAttributeRight
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:rightView
|
||||
attribute:NSLayoutAttributeRight
|
||||
multiplier:1
|
||||
constant:edgeInset.right]];
|
||||
}
|
||||
|
||||
if (bottomView) {
|
||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:bottomView
|
||||
attribute:NSLayoutAttributeBottom
|
||||
multiplier:1
|
||||
constant:edgeInset.bottom]];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - public
|
||||
|
||||
- (void)setBlurEffectWithView:(UIView *)view {
|
||||
[self setBlurEffectWithView:view style:BlurEffectStyleDarkEffect];
|
||||
}
|
||||
|
||||
- (void)setBlurEffectWithView:(UIView *)view style:(BlurEffectStyle)blurStyle {
|
||||
UIImage *snapshotImage = [UIImage snapshotImageWithView:view];
|
||||
UIImage *blurImage = [self blurImageWithSnapshotImage:snapshotImage style:blurStyle];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIImageView *blurImageView = [[UIImageView alloc] initWithImage:blurImage];
|
||||
self.backgroundView = blurImageView;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setBlurEffectWithView:(UIView *)view effectTintColor:(UIColor *)effectTintColor {
|
||||
UIImage *snapshotImage = [UIImage snapshotImageWithView:view];
|
||||
UIImage *blurImage = [snapshotImage applyTintEffectWithColor:effectTintColor];
|
||||
UIImageView *blurImageView = [[UIImageView alloc] initWithImage:blurImage];
|
||||
self.backgroundView = blurImageView;
|
||||
}
|
||||
|
||||
#pragma mark - private
|
||||
- (void)showHapticFeedback {
|
||||
if (@available(iOS 10.0, *)) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
|
||||
[generator prepare];
|
||||
[generator impactOccurred];
|
||||
});
|
||||
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
- (UIImage *)blurImageWithSnapshotImage:(UIImage *)snapshotImage style:(BlurEffectStyle)blurStyle {
|
||||
switch (blurStyle) {
|
||||
case BlurEffectStyleLight:
|
||||
return [snapshotImage applyLightEffect];
|
||||
case BlurEffectStyleDarkEffect:
|
||||
return [snapshotImage applyDarkEffect];
|
||||
case BlurEffectStyleExtraLight:
|
||||
return [snapshotImage applyExtraLightEffect];
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)blurDismissViewControllerAnimated:(BOOL)animated completion:(void (^__nullable)(BOOL finished))completion {
|
||||
[self dismissViewControllerAnimated:animated];
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if (completion) {
|
||||
completion(YES);
|
||||
}
|
||||
});
|
||||
}
|
||||
- (void)dismissViewControllerAnimated:(BOOL)animated {
|
||||
[UIView animateWithDuration:0.3
|
||||
animations:^{
|
||||
self.recentView.frame = CGRectMake(self.recentView.frame.origin.x, self.originFrame.origin.y - self.recentView.frame.size.height,
|
||||
self.recentView.frame.size.width, self.recentView.frame.size.height);
|
||||
}
|
||||
completion:^(BOOL finished){
|
||||
|
||||
}];
|
||||
|
||||
[UIView animateWithDuration:0.3
|
||||
animations:^{
|
||||
self.alertContainerView.frame = CGRectMake(0, self.originFrame.origin.y , self.view.frame.size.width, self.originFrame.size.height);
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
[self dismissViewControllerAnimated:animated completion:self.dismissComplete];
|
||||
}
|
||||
}];
|
||||
|
||||
self.extionView.transform = CGAffineTransformMakeScale(1, 1);
|
||||
[UIView animateWithDuration:0.2
|
||||
animations:^{
|
||||
self.extionView.transform = CGAffineTransformMakeScale(0.1, 0.1);
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
self.extionView.transform = CGAffineTransformMakeScale(0, 0);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - action
|
||||
|
||||
- (void)singleTap:(UITapGestureRecognizer *)sender {
|
||||
[self dismissViewControllerAnimated:NO];
|
||||
}
|
||||
|
||||
// MARK: V2TIMAdvancedMsgListener
|
||||
- (void)onRecvMessageRevoked:(NSString *)msgID operateUser:(V2TIMUserFullInfo *)operateUser reason:(NSString *)reason {
|
||||
|
||||
if ([msgID isEqualToString:self.alertViewCellData.msgID]) {
|
||||
UIViewController *controller = self;
|
||||
while(controller.presentingViewController != nil){
|
||||
controller = controller.presentingViewController;
|
||||
}
|
||||
[controller dismissViewControllerAnimated:YES completion:^{
|
||||
[self blurDismissViewControllerAnimated:NO completion:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
40
TUIKit/TUIChat/CommonUI/Pop/TUIChatPopContextExtionView.h
Normal file
40
TUIKit/TUIChat/CommonUI/Pop/TUIChatPopContextExtionView.h
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// TUIChatPopContextExtionView.h
|
||||
// TUIEmojiPlugin
|
||||
//
|
||||
// Created by wyl on 2023/12/1.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIChatPopContextExtionItem : NSObject
|
||||
|
||||
@property(nonatomic, copy) NSString *title;
|
||||
|
||||
@property(nonatomic, strong) UIColor *titleColor;
|
||||
|
||||
@property(nonatomic, strong) UIFont *titleFont;
|
||||
|
||||
@property(nonatomic, assign) CGFloat weight;
|
||||
|
||||
@property(nonatomic, strong) UIImage *markIcon;
|
||||
|
||||
@property(nonatomic, assign) CGFloat itemHeight;
|
||||
|
||||
@property(nonatomic, assign) BOOL needBottomLine;
|
||||
|
||||
@property(nonatomic, copy) void (^actionHandler)(TUIChatPopContextExtionItem *item);
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title markIcon:(UIImage *)markIcon weight:(NSInteger)weight withActionHandler:(void (^)(id action))actionHandler;
|
||||
|
||||
@end
|
||||
|
||||
@interface TUIChatPopContextExtionView : UIView
|
||||
|
||||
- (void)configUIWithItems:(NSMutableArray<TUIChatPopContextExtionItem *> *)items topBottomMargin:(CGFloat)topBottomMargin;
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
114
TUIKit/TUIChat/CommonUI/Pop/TUIChatPopContextExtionView.m
Normal file
114
TUIKit/TUIChat/CommonUI/Pop/TUIChatPopContextExtionView.m
Normal file
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// TUIChatPopContextExtionView.m
|
||||
// TUIEmojiPlugin
|
||||
//
|
||||
// Created by wyl on 2023/12/1.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIChatPopContextExtionView.h"
|
||||
#import <TIMCommon/NSString+TUIEmoji.h>
|
||||
#import <TIMCommon/TIMCommonModel.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TIMCommon/TUIFitButton.h>
|
||||
#import <TIMCommon/TIMCommonMediator.h>
|
||||
#import <TIMCommon/TUIEmojiMeditorProtocol.h>
|
||||
|
||||
@implementation TUIChatPopContextExtionItem
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title markIcon:(UIImage *)markIcon weight:(NSInteger)weight withActionHandler:(void (^)(id action))actionHandler {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_title = title;
|
||||
_markIcon = markIcon;
|
||||
_weight = weight;
|
||||
_actionHandler = actionHandler;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface TUIChatPopContextExtionItemView : UIView
|
||||
@property(nonatomic, strong) TUIChatPopContextExtionItem *item;
|
||||
@property(nonatomic, strong) UIImageView *icon;
|
||||
@property(nonatomic, strong) UILabel *l;
|
||||
- (void)configBaseUIWithItem:(TUIChatPopContextExtionItem *)item;
|
||||
@end
|
||||
|
||||
@implementation TUIChatPopContextExtionItemView
|
||||
|
||||
- (void)configBaseUIWithItem:(TUIChatPopContextExtionItem *)item {
|
||||
self.item = item;
|
||||
CGFloat itemWidth = self.frame.size.width;
|
||||
CGFloat padding = kScale390(16);
|
||||
CGFloat itemHeight = self.frame.size.height;
|
||||
|
||||
UIImageView *icon = [[UIImageView alloc] init];
|
||||
[self addSubview:icon];
|
||||
icon.frame = CGRectMake(itemWidth - padding - kScale390(18), itemHeight * 0.5 - kScale390(18) * 0.5, kScale390(18), kScale390(18));
|
||||
icon.image = self.item.markIcon;
|
||||
|
||||
UILabel *l = [[UILabel alloc] init];
|
||||
l.frame = CGRectMake(padding, 0, itemWidth * 0.5, itemHeight);
|
||||
l.text = self.item.title;
|
||||
l.font = item.titleFont ?: [UIFont systemFontOfSize:kScale390(16)];
|
||||
l.textAlignment = isRTL()? NSTextAlignmentRight:NSTextAlignmentLeft;
|
||||
l.textColor = item.titleColor ?: [UIColor blackColor];
|
||||
l.userInteractionEnabled = false;
|
||||
[self addSubview:l];
|
||||
|
||||
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[backButton addTarget:self action:@selector(buttonclick) forControlEvents:UIControlEventTouchUpInside];
|
||||
backButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
backButton.frame = CGRectMake(0, 0, itemWidth, itemHeight);
|
||||
|
||||
[self addSubview:backButton];
|
||||
|
||||
if (item.needBottomLine) {
|
||||
UIView *line = [UIView new];
|
||||
line.backgroundColor = [UIColor tui_colorWithHex:@"DDDDDD"];
|
||||
line.frame = CGRectMake(0, itemHeight - kScale390(0.5), itemWidth, kScale390(0.5));
|
||||
[self addSubview:line];
|
||||
}
|
||||
self.layer.masksToBounds = YES;
|
||||
if (isRTL()) {
|
||||
for (UIView *subview in self.subviews) {
|
||||
[subview resetFrameToFitRTL];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)buttonclick {
|
||||
if (self.item.actionHandler) {
|
||||
self.item.actionHandler(self.item);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface TUIChatPopContextExtionView ()
|
||||
|
||||
@property(nonatomic, strong) NSMutableArray<TUIChatPopContextExtionItem *> *items;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIChatPopContextExtionView
|
||||
- (void)configUIWithItems:(NSMutableArray<TUIChatPopContextExtionItem *> *)items topBottomMargin:(CGFloat)topBottomMargin {
|
||||
if (self.subviews.count > 0) {
|
||||
for (UIView *subview in self.subviews) {
|
||||
if (subview) {
|
||||
[subview removeFromSuperview];
|
||||
}
|
||||
}
|
||||
}
|
||||
int i = 0;
|
||||
for (TUIChatPopContextExtionItem *item in items) {
|
||||
TUIChatPopContextExtionItemView *itemView = [[TUIChatPopContextExtionItemView alloc] init];
|
||||
itemView.frame = CGRectMake(0, (kScale390(40)) * i + topBottomMargin, kScale390(180), kScale390(40));
|
||||
[itemView configBaseUIWithItem:item];
|
||||
[self addSubview:itemView];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@end
|
||||
30
TUIKit/TUIChat/CommonUI/Pop/UIImage+ImageEffects.h
Normal file
30
TUIKit/TUIChat/CommonUI/Pop/UIImage+ImageEffects.h
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/*
|
||||
File: UIImage+ImageEffects.h
|
||||
Abstract: This is a category of UIImage that adds methods to apply blur and tint effects to an image. This is the code you’ll want to look out to find out how
|
||||
to use vImage to efficiently calculate a blur. Version: 1.0
|
||||
*/
|
||||
|
||||
@import UIKit;
|
||||
|
||||
@interface UIImage (ImageEffects)
|
||||
|
||||
- (UIImage *)applyLightEffect;
|
||||
- (UIImage *)applyExtraLightEffect;
|
||||
- (UIImage *)applyDarkEffect;
|
||||
- (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor;
|
||||
|
||||
- (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius
|
||||
tintColor:(UIColor *)tintColor
|
||||
saturationDeltaFactor:(CGFloat)saturationDeltaFactor
|
||||
maskImage:(UIImage *)maskImage;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIImage (SnapshotImage)
|
||||
|
||||
+ (UIImage *)snapshotImageWithView:(UIView *)view;
|
||||
|
||||
@end
|
||||
202
TUIKit/TUIChat/CommonUI/Pop/UIImage+ImageEffects.m
Normal file
202
TUIKit/TUIChat/CommonUI/Pop/UIImage+ImageEffects.m
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
#import "UIImage+ImageEffects.h"
|
||||
|
||||
@import Accelerate;
|
||||
#import <float.h>
|
||||
|
||||
@implementation UIImage (ImageEffects)
|
||||
|
||||
- (UIImage *)applyLightEffect {
|
||||
UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3];
|
||||
return [self applyBlurWithRadius:10 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)applyExtraLightEffect {
|
||||
UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82];
|
||||
return [self applyBlurWithRadius:20 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)applyDarkEffect {
|
||||
UIColor *tintColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.4];
|
||||
return [self applyBlurWithRadius:20 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor {
|
||||
const CGFloat effectColorAlpha = 0.6;
|
||||
UIColor *effectColor = tintColor;
|
||||
int componentCount = CGColorGetNumberOfComponents(tintColor.CGColor);
|
||||
if (componentCount == 2) {
|
||||
CGFloat b;
|
||||
if ([tintColor getWhite:&b alpha:NULL]) {
|
||||
effectColor = [UIColor colorWithWhite:b alpha:effectColorAlpha];
|
||||
}
|
||||
} else {
|
||||
CGFloat r, g, b;
|
||||
if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) {
|
||||
effectColor = [UIColor colorWithRed:r green:g blue:b alpha:effectColorAlpha];
|
||||
}
|
||||
}
|
||||
return [self applyBlurWithRadius:10 tintColor:effectColor saturationDeltaFactor:-1.0 maskImage:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius
|
||||
tintColor:(UIColor *)tintColor
|
||||
saturationDeltaFactor:(CGFloat)saturationDeltaFactor
|
||||
maskImage:(UIImage *)maskImage {
|
||||
// Check pre-conditions.
|
||||
if (self.size.width < 1 || self.size.height < 1) {
|
||||
NSLog(@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self);
|
||||
return nil;
|
||||
}
|
||||
if (!self.CGImage) {
|
||||
NSLog(@"*** error: image must be backed by a CGImage: %@", self);
|
||||
return nil;
|
||||
}
|
||||
if (maskImage && !maskImage.CGImage) {
|
||||
NSLog(@"*** error: maskImage must be backed by a CGImage: %@", maskImage);
|
||||
return nil;
|
||||
}
|
||||
|
||||
CGRect imageRect = {CGPointZero, self.size};
|
||||
UIImage *effectImage = self;
|
||||
|
||||
BOOL hasBlur = blurRadius > __FLT_EPSILON__;
|
||||
BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__;
|
||||
if (hasBlur || hasSaturationChange) {
|
||||
UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
|
||||
CGContextRef effectInContext = UIGraphicsGetCurrentContext();
|
||||
CGContextScaleCTM(effectInContext, 1.0, -1.0);
|
||||
CGContextTranslateCTM(effectInContext, 0, -self.size.height);
|
||||
CGContextDrawImage(effectInContext, imageRect, self.CGImage);
|
||||
|
||||
vImage_Buffer effectInBuffer;
|
||||
effectInBuffer.data = CGBitmapContextGetData(effectInContext);
|
||||
effectInBuffer.width = CGBitmapContextGetWidth(effectInContext);
|
||||
effectInBuffer.height = CGBitmapContextGetHeight(effectInContext);
|
||||
effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
|
||||
CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
|
||||
vImage_Buffer effectOutBuffer;
|
||||
effectOutBuffer.data = CGBitmapContextGetData(effectOutContext);
|
||||
effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext);
|
||||
effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext);
|
||||
effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);
|
||||
|
||||
if (hasBlur) {
|
||||
// A description of how to compute the box kernel width from the Gaussian
|
||||
// radius (aka standard deviation) appears in the SVG spec:
|
||||
// http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
|
||||
//
|
||||
// For larger values of 's' (s >= 2.0), an approximation can be used: Three
|
||||
// successive box-blurs build a piece-wise quadratic convolution kernel, which
|
||||
// approximates the Gaussian kernel to within roughly 3%.
|
||||
//
|
||||
// let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
|
||||
//
|
||||
// ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
|
||||
//
|
||||
CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
|
||||
NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
|
||||
if (radius % 2 != 1) {
|
||||
radius += 1; // force radius to be odd so that the three box-blur methodology works.
|
||||
}
|
||||
vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
|
||||
vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
|
||||
vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
|
||||
}
|
||||
BOOL effectImageBuffersAreSwapped = NO;
|
||||
if (hasSaturationChange) {
|
||||
CGFloat s = saturationDeltaFactor;
|
||||
CGFloat floatingPointSaturationMatrix[] = {
|
||||
0.0722 + 0.9278 * s,
|
||||
0.0722 - 0.0722 * s,
|
||||
0.0722 - 0.0722 * s,
|
||||
0,
|
||||
0.7152 - 0.7152 * s,
|
||||
0.7152 + 0.2848 * s,
|
||||
0.7152 - 0.7152 * s,
|
||||
0,
|
||||
0.2126 - 0.2126 * s,
|
||||
0.2126 - 0.2126 * s,
|
||||
0.2126 + 0.7873 * s,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
};
|
||||
const int32_t divisor = 256;
|
||||
NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix) / sizeof(floatingPointSaturationMatrix[0]);
|
||||
int16_t saturationMatrix[matrixSize];
|
||||
for (NSUInteger i = 0; i < matrixSize; ++i) {
|
||||
saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor);
|
||||
}
|
||||
if (hasBlur) {
|
||||
vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
|
||||
effectImageBuffersAreSwapped = YES;
|
||||
} else {
|
||||
vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
|
||||
}
|
||||
}
|
||||
if (!effectImageBuffersAreSwapped) effectImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
if (effectImageBuffersAreSwapped) effectImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
||||
// Set up output context.
|
||||
UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
|
||||
CGContextRef outputContext = UIGraphicsGetCurrentContext();
|
||||
CGContextScaleCTM(outputContext, 1.0, -1.0);
|
||||
CGContextTranslateCTM(outputContext, 0, -self.size.height);
|
||||
|
||||
// Draw base image.
|
||||
CGContextDrawImage(outputContext, imageRect, self.CGImage);
|
||||
|
||||
// Draw effect image.
|
||||
if (hasBlur) {
|
||||
CGContextSaveGState(outputContext);
|
||||
if (maskImage) {
|
||||
CGContextClipToMask(outputContext, imageRect, maskImage.CGImage);
|
||||
}
|
||||
CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
|
||||
CGContextRestoreGState(outputContext);
|
||||
}
|
||||
|
||||
// Add in color tint.
|
||||
if (tintColor) {
|
||||
CGContextSaveGState(outputContext);
|
||||
CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
|
||||
CGContextFillRect(outputContext, imageRect);
|
||||
CGContextRestoreGState(outputContext);
|
||||
}
|
||||
|
||||
// Output image is ready.
|
||||
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return outputImage;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIImage (SnapshotImage)
|
||||
|
||||
+ (UIImage *)snapshotImageWithView:(UIView *)view {
|
||||
// currentView The current view creates a bitmap-based graphics context and specifies the size of
|
||||
UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, [UIScreen mainScreen].scale);
|
||||
// renderInContext Renders the receiver and its subscopes to the specified context
|
||||
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
|
||||
// Returns an image based on the current graphics context
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
// generated image
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user