This commit is contained in:
2025-11-28 16:06:34 +08:00
parent 9268a21eb8
commit a2bb61408b
4 changed files with 109 additions and 30 deletions

View File

@@ -57,6 +57,7 @@
04791FFF2ED830FA004E8522 /* KBKeyboardMaskView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04791FFE2ED830FA004E8522 /* KBKeyboardMaskView.m */; }; 04791FFF2ED830FA004E8522 /* KBKeyboardMaskView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04791FFE2ED830FA004E8522 /* KBKeyboardMaskView.m */; };
047920072ED86ABC004E8522 /* kb_guide_keyboard.gif in Resources */ = {isa = PBXBuildFile; fileRef = 047920062ED86ABC004E8522 /* kb_guide_keyboard.gif */; }; 047920072ED86ABC004E8522 /* kb_guide_keyboard.gif in Resources */ = {isa = PBXBuildFile; fileRef = 047920062ED86ABC004E8522 /* kb_guide_keyboard.gif */; };
0479200B2ED87CEE004E8522 /* permiss_video.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 0479200A2ED87CEE004E8522 /* permiss_video.mp4 */; }; 0479200B2ED87CEE004E8522 /* permiss_video.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 0479200A2ED87CEE004E8522 /* permiss_video.mp4 */; };
047920112ED98E7D004E8522 /* permiss_video_2.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 047920102ED98E7D004E8522 /* permiss_video_2.mp4 */; };
047C650D2EBC8A840035E841 /* KBPanModalView.m in Sources */ = {isa = PBXBuildFile; fileRef = 047C650C2EBC8A840035E841 /* KBPanModalView.m */; }; 047C650D2EBC8A840035E841 /* KBPanModalView.m in Sources */ = {isa = PBXBuildFile; fileRef = 047C650C2EBC8A840035E841 /* KBPanModalView.m */; };
047C65102EBCA8DD0035E841 /* HomeRankContentVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 047C650F2EBCA8DD0035E841 /* HomeRankContentVC.m */; }; 047C65102EBCA8DD0035E841 /* HomeRankContentVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 047C650F2EBCA8DD0035E841 /* HomeRankContentVC.m */; };
047C65502EBCBA9E0035E841 /* KBShopVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 047C654F2EBCBA9E0035E841 /* KBShopVC.m */; }; 047C65502EBCBA9E0035E841 /* KBShopVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 047C654F2EBCBA9E0035E841 /* KBShopVC.m */; };
@@ -272,6 +273,7 @@
04791FFE2ED830FA004E8522 /* KBKeyboardMaskView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBKeyboardMaskView.m; sourceTree = "<group>"; }; 04791FFE2ED830FA004E8522 /* KBKeyboardMaskView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBKeyboardMaskView.m; sourceTree = "<group>"; };
047920062ED86ABC004E8522 /* kb_guide_keyboard.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = kb_guide_keyboard.gif; sourceTree = "<group>"; }; 047920062ED86ABC004E8522 /* kb_guide_keyboard.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = kb_guide_keyboard.gif; sourceTree = "<group>"; };
0479200A2ED87CEE004E8522 /* permiss_video.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = permiss_video.mp4; sourceTree = "<group>"; }; 0479200A2ED87CEE004E8522 /* permiss_video.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = permiss_video.mp4; sourceTree = "<group>"; };
047920102ED98E7D004E8522 /* permiss_video_2.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = permiss_video_2.mp4; sourceTree = "<group>"; };
047C650B2EBC8A840035E841 /* KBPanModalView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBPanModalView.h; sourceTree = "<group>"; }; 047C650B2EBC8A840035E841 /* KBPanModalView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBPanModalView.h; sourceTree = "<group>"; };
047C650C2EBC8A840035E841 /* KBPanModalView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBPanModalView.m; sourceTree = "<group>"; }; 047C650C2EBC8A840035E841 /* KBPanModalView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBPanModalView.m; sourceTree = "<group>"; };
047C650E2EBCA8DD0035E841 /* HomeRankContentVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeRankContentVC.h; sourceTree = "<group>"; }; 047C650E2EBCA8DD0035E841 /* HomeRankContentVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeRankContentVC.h; sourceTree = "<group>"; };
@@ -630,6 +632,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0479200A2ED87CEE004E8522 /* permiss_video.mp4 */, 0479200A2ED87CEE004E8522 /* permiss_video.mp4 */,
047920102ED98E7D004E8522 /* permiss_video_2.mp4 */,
047920062ED86ABC004E8522 /* kb_guide_keyboard.gif */, 047920062ED86ABC004E8522 /* kb_guide_keyboard.gif */,
04286A122ECDEBF900CE730C /* KBSkinIconMap.strings */, 04286A122ECDEBF900CE730C /* KBSkinIconMap.strings */,
04286A0E2ECDA71B00CE730C /* 001.zip */, 04286A0E2ECDA71B00CE730C /* 001.zip */,
@@ -1471,6 +1474,7 @@
04C6EABA2EAF86530089C901 /* Assets.xcassets in Resources */, 04C6EABA2EAF86530089C901 /* Assets.xcassets in Resources */,
04A9FE212EB893F10020DB6D /* Localizable.strings in Resources */, 04A9FE212EB893F10020DB6D /* Localizable.strings in Resources */,
047920072ED86ABC004E8522 /* kb_guide_keyboard.gif in Resources */, 047920072ED86ABC004E8522 /* kb_guide_keyboard.gif in Resources */,
047920112ED98E7D004E8522 /* permiss_video_2.mp4 in Resources */,
04C6EABC2EAF86530089C901 /* LaunchScreen.storyboard in Resources */, 04C6EABC2EAF86530089C901 /* LaunchScreen.storyboard in Resources */,
04286A132ECDEBF900CE730C /* KBSkinIconMap.strings in Resources */, 04286A132ECDEBF900CE730C /* KBSkinIconMap.strings in Resources */,
04C6EABD2EAF86530089C901 /* Main.storyboard in Resources */, 04C6EABD2EAF86530089C901 /* Main.storyboard in Resources */,

View File

@@ -6,21 +6,19 @@
// //
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <FLAnimatedImage/FLAnimatedImage.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
/// 覆盖在 KBGuideVC 上方的“请选择自家键盘”蒙层 /// 覆盖在 KBGuideVC 上方的“请选择自家键盘”蒙层
/// - 左上角:返回箭头 /// - 左上角:返回箭头
/// - 中间:播放 GIF 的区域(宽=屏幕宽,高=300 /// - 中间:播放引导视频的区域(宽=屏幕宽,高=300
/// - 点击任意空白区域:回调给外部(用于激活输入框) /// - 点击任意空白区域:回调给外部(用于激活输入框)
@interface KBKeyboardMaskView : UIView @interface KBKeyboardMaskView : UIView
@property (nonatomic, strong, readonly) UIButton *backButton; @property (nonatomic, strong, readonly) UIButton *backButton;
@property (nonatomic, strong, readonly) FLAnimatedImageView *gifView;
/// 点击蒙层空白时回调(不包括 backButton /// 点击蒙层空白时回调(不包括 backButton
@property (nonatomic, copy) void (^tapHandler)(void); @property (nonatomic, copy) void (^tapHandler)(void);
/// 更新内部 GIF 与键盘的相对位置,保证不被遮挡 /// 更新内部视频与键盘的相对位置,保证不被遮挡
- (void)updateForKeyboardHeight:(CGFloat)kbHeight - (void)updateForKeyboardHeight:(CGFloat)kbHeight
duration:(NSTimeInterval)duration duration:(NSTimeInterval)duration
curve:(UIViewAnimationOptions)curve; curve:(UIViewAnimationOptions)curve;

View File

@@ -6,11 +6,14 @@
// //
#import "KBKeyboardMaskView.h" #import "KBKeyboardMaskView.h"
#import <AVFoundation/AVFoundation.h>
@interface KBKeyboardMaskView () @interface KBKeyboardMaskView ()
@property (nonatomic, strong) UIButton *backButton; @property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) FLAnimatedImageView *gifView; @property (nonatomic, strong) UIView *videoContainerView; // GIF
@property (nonatomic, strong) UIImageView *tipLabel; // @property (nonatomic, strong) AVPlayer *kb_maskPlayer; //
@property (nonatomic, strong) AVPlayerLayer *kb_maskPlayerLayer;
@property (nonatomic, strong) UIImageView *tipLabel; //
@property (nonatomic, assign) CGFloat keyboardHeight; @property (nonatomic, assign) CGFloat keyboardHeight;
@end @end
@@ -36,16 +39,15 @@ static const CGFloat KGifViewH = (209);
make.width.height.mas_equalTo(40); make.width.height.mas_equalTo(40);
}]; }];
// GIF // GIF
_gifView = [FLAnimatedImageView new]; _videoContainerView = [UIView new];
_gifView.contentMode = UIViewContentModeScaleAspectFit; _videoContainerView.backgroundColor = [UIColor clearColor];
_gifView.clipsToBounds = YES; _videoContainerView.clipsToBounds = YES;
_gifView.layer.cornerRadius = 30; _videoContainerView.layer.cornerRadius = 30.0;
// _gifView.clipsToBounds = true; _videoContainerView.layer.masksToBounds = YES;
_gifView.layer.masksToBounds = true; [self addSubview:_videoContainerView];
[self addSubview:_gifView];
[_gifView mas_makeConstraints:^(MASConstraintMaker *make) { [_videoContainerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self); make.centerX.equalTo(self);
make.width.mas_equalTo(KBFit(316)); make.width.mas_equalTo(KBFit(316));
make.height.mas_equalTo(KBFit(KGifViewH)); make.height.mas_equalTo(KBFit(KGifViewH));
@@ -62,23 +64,30 @@ static const CGFloat KGifViewH = (209);
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapMask:)]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapMask:)];
[self addGestureRecognizer:tap]; [self addGestureRecognizer:tap];
// GIF // App
NSString *gifPath = [[NSBundle mainBundle] pathForResource:@"kb_guide_keyboard" ofType:@"gif"]; [[NSNotificationCenter defaultCenter] addObserver:self
if (gifPath.length > 0) { selector:@selector(kb_maskAppDidBecomeActive:)
NSData *data = [NSData dataWithContentsOfFile:gifPath]; name:UIApplicationDidBecomeActiveNotification
if (data.length > 0) { object:nil];
FLAnimatedImage *img = [FLAnimatedImage animatedImageWithGIFData:data]; [[NSNotificationCenter defaultCenter] addObserver:self
_gifView.animatedImage = img; selector:@selector(kb_maskAppDidEnterBackground:)
} name:UIApplicationDidEnterBackgroundNotification
} object:nil];
//
[self kb_setupGuideVideoPlayerIfNeeded];
return self; return self;
} }
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)layoutSubviews { - (void)layoutSubviews {
[super layoutSubviews]; [super layoutSubviews];
// GIF //
// - // -
// - 20pt safe // - 20pt safe
CGFloat viewH = CGRectGetHeight(self.bounds); CGFloat viewH = CGRectGetHeight(self.bounds);
@@ -97,11 +106,11 @@ static const CGFloat KGifViewH = (209);
if (y < topMargin) y = topMargin; if (y < topMargin) y = topMargin;
} }
CGRect gifFrame = self.gifView.frame; CGRect gifFrame = self.videoContainerView.frame;
gifFrame.origin.y = y; gifFrame.origin.y = y;
self.gifView.frame = gifFrame; self.videoContainerView.frame = gifFrame;
// gifView 20pt // 20pt
CGFloat labelMaxWidth = CGRectGetWidth(self.bounds) - 40.0; // 20 CGFloat labelMaxWidth = CGRectGetWidth(self.bounds) - 40.0; // 20
if (labelMaxWidth < 0) { labelMaxWidth = 0; } if (labelMaxWidth < 0) { labelMaxWidth = 0; }
UIImage *tipImage = self.tipLabel.image; UIImage *tipImage = self.tipLabel.image;
@@ -117,9 +126,23 @@ static const CGFloat KGifViewH = (209);
CGFloat labelH = imgH * scale; CGFloat labelH = imgH * scale;
CGFloat labelX = (CGRectGetWidth(self.bounds) - labelW) * 0.5; CGFloat labelX = (CGRectGetWidth(self.bounds) - labelW) * 0.5;
CGFloat labelBottom = CGRectGetMinY(self.gifView.frame) - 20.0; CGFloat labelBottom = CGRectGetMinY(self.videoContainerView.frame) - 20.0;
CGFloat labelY = labelBottom - labelH; CGFloat labelY = labelBottom - labelH;
self.tipLabel.frame = CGRectMake(labelX, labelY, labelW, labelH); self.tipLabel.frame = CGRectMake(labelX, labelY, labelW, labelH);
// 使
if (self.kb_maskPlayerLayer) {
self.kb_maskPlayerLayer.frame = self.videoContainerView.bounds;
}
}
- (void)setHidden:(BOOL)hidden {
[super setHidden:hidden];
if (hidden) {
[self.kb_maskPlayer pause];
} else {
[self.kb_maskPlayer play];
}
} }
- (void)onTapMask:(UITapGestureRecognizer *)gr { - (void)onTapMask:(UITapGestureRecognizer *)gr {
@@ -137,7 +160,7 @@ static const CGFloat KGifViewH = (209);
duration:(NSTimeInterval)duration duration:(NSTimeInterval)duration
curve:(UIViewAnimationOptions)curve { curve:(UIViewAnimationOptions)curve {
self.keyboardHeight = MAX(kbHeight, 0); self.keyboardHeight = MAX(kbHeight, 0);
// 便 layoutSubviews gifView Y // 便 layoutSubviews Y
[self setNeedsLayout]; [self setNeedsLayout];
[UIView animateWithDuration:duration [UIView animateWithDuration:duration
delay:0 delay:0
@@ -148,4 +171,58 @@ static const CGFloat KGifViewH = (209);
completion:nil]; completion:nil];
} }
#pragma mark - App Lifecycle
- (void)kb_maskAppDidBecomeActive:(NSNotification *)note {
//
if (self.window && !self.hidden) {
if (!self.kb_maskPlayer) {
[self kb_setupGuideVideoPlayerIfNeeded];
} else {
[self.kb_maskPlayer play];
}
}
}
- (void)kb_maskAppDidEnterBackground:(NSNotification *)note {
[self.kb_maskPlayer pause];
}
#pragma mark - Video
- (void)kb_setupGuideVideoPlayerIfNeeded {
if (self.kb_maskPlayer) { return; }
NSURL *videoURL = [[NSBundle mainBundle] URLForResource:@"permiss_video_2" withExtension:@"mp4"];
if (!videoURL) { return; }
AVPlayerItem *item = [AVPlayerItem playerItemWithURL:videoURL];
self.kb_maskPlayer = [AVPlayer playerWithPlayerItem:item];
self.kb_maskPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
self.kb_maskPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.kb_maskPlayer];
self.kb_maskPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
self.kb_maskPlayerLayer.cornerRadius = 30.0;
self.kb_maskPlayerLayer.masksToBounds = YES;
[self.videoContainerView.layer addSublayer:self.kb_maskPlayerLayer];
//
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(kb_maskPlayerDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:item];
[self.kb_maskPlayer play];
}
- (void)kb_maskPlayerDidReachEnd:(NSNotification *)note {
AVPlayerItem *item = (AVPlayerItem *)note.object;
if (!item) return;
__weak typeof(self) weakSelf = self;
[item seekToTime:kCMTimeZero completionHandler:^(BOOL finished) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf.kb_maskPlayer play];
}];
}
@end @end

Binary file not shown.