2
This commit is contained in:
@@ -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 */,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -6,10 +6,13 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#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) AVPlayer *kb_maskPlayer; // 键盘蒙层引导视频播放器
|
||||||
|
@property (nonatomic, strong) AVPlayerLayer *kb_maskPlayerLayer;
|
||||||
@property (nonatomic, strong) UIImageView *tipLabel; // 顶部提示图
|
@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
|
||||||
|
|||||||
BIN
keyBoard/Class/Resource/permiss_video_2.mp4
Normal file
BIN
keyBoard/Class/Resource/permiss_video_2.mp4
Normal file
Binary file not shown.
Reference in New Issue
Block a user