UGCKitRecordControlView.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. // Copyright (c) 2019 Tencent. All rights reserved.
  2. #import "UGCKitRecordControlView.h"
  3. #import "UGCKitSmallButton.h"
  4. #import "SDKHeader.h"
  5. #import "UGCKitVerticalButton.h"
  6. #import "UGCKitLabel.h"
  7. #import "UGCKitLocalization.h"
  8. #define BUTTON_CONTROL_SIZE 32
  9. #define BUTTON_MASK_HEIGHT 200
  10. #define BUTTON_RECORD_SIZE 75
  11. #define BUTTON_PROGRESS_HEIGHT 3
  12. #define BUTTON_SPEED_COUNT 5
  13. #define BUTTON_SPEED_HEIGHT 30
  14. #define BUTTON_SPEED_CHANGE_HEIGHT 34
  15. #define kScaleX [UIScreen mainScreen].bounds.size.width / 375
  16. @implementation UGCKitRecordControlView
  17. {
  18. NSArray <UIView *> * _activeRightSideControlsBeforeHidden;
  19. }
  20. - (instancetype)initWithCoder:(NSCoder *)coder
  21. {
  22. self = [super initWithCoder:coder];
  23. if (self) {
  24. _minDuration = 0;
  25. _maxDuration = 30;
  26. [self commonInit];
  27. }
  28. return self;
  29. }
  30. - (instancetype)initWithFrame:(CGRect)frame minDuration:(NSTimeInterval)minDuration maxDuration:(NSTimeInterval)maxDuration
  31. {
  32. self = [super initWithFrame:frame];
  33. if (self) {
  34. _minDuration = minDuration;
  35. _maxDuration = maxDuration;
  36. [self commonInit];
  37. }
  38. return self;
  39. }
  40. - (void)setControlButtonsHidden:(BOOL)controlButtonsHidden {
  41. _controlButtonsHidden = controlButtonsHidden;
  42. [_activeRightSideControlsBeforeHidden enumerateObjectsUsingBlock:
  43. ^(UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  44. obj.hidden = controlButtonsHidden;
  45. }];
  46. if (self.countDownModeEnabled) {
  47. _btnCountDown.hidden = controlButtonsHidden;
  48. }
  49. self.btnFlash.hidden = controlButtonsHidden;
  50. self.btnCamera.hidden = controlButtonsHidden;
  51. self.btnDelete.hidden = controlButtonsHidden;
  52. self.recordButtonSwitchControl.hidden = controlButtonsHidden;
  53. }
  54. - (void)commonInit {
  55. _videoRatio = VIDEO_ASPECT_RATIO_9_16;
  56. _recordButtonStyle = UGCKitRecordButtonStyleRecord;
  57. _speedControlEnabled = YES;
  58. _musicButtonEnabled = YES;
  59. }
  60. - (void)setMinDuration:(NSTimeInterval)minDuration
  61. maxDuration:(NSTimeInterval)maxDuration
  62. {
  63. _minDuration = minDuration;
  64. _maxDuration = maxDuration;
  65. [_progressView setMinDuration:_minDuration maxDuration:_maxDuration];
  66. }
  67. - (void)setVideoRatio:(TXVideoAspectRatio)videoRatio
  68. {
  69. if (_videoRatio != videoRatio) {
  70. _videoRatio = videoRatio;
  71. for (NSInteger idx = 0; idx < _btnRatioGroup.buttons.count; ++idx) {
  72. UIButton *btn = _btnRatioGroup.buttons[idx];
  73. if (btn.tag == videoRatio) {
  74. _btnRatioGroup.selectedIndex = idx;
  75. break;
  76. }
  77. }
  78. }
  79. }
  80. - (void)setIsFrontCamera:(BOOL)isFrontCamera
  81. {
  82. if (_isFrontCamera != isFrontCamera) {
  83. _isFrontCamera = isFrontCamera;
  84. [self _configCameraSwitchButton];
  85. }
  86. }
  87. - (void)setTorchOn:(BOOL)torchOn
  88. {
  89. if (_torchOn != torchOn) {
  90. _torchOn = torchOn;
  91. [self _configTorchButton];
  92. }
  93. }
  94. - (void)_configRecordButton {
  95. CGPoint center = _btnStartRecord.center;
  96. if (_recordButtonStyle == UGCKitRecordButtonStylePause) {
  97. [_btnStartRecord setImage:_theme.recordButtonPauseInnerIcon forState:UIControlStateNormal];
  98. [_btnStartRecord setBackgroundImage:_theme.recordButtonPauseBackgroundImage forState:UIControlStateNormal];
  99. } else if (_recordButtonStyle == UGCKitRecordButtonStyleRecord) {
  100. [_btnStartRecord setImage:_theme.recordButtonTapModeIcon forState:UIControlStateNormal];
  101. [_btnStartRecord setBackgroundImage:_theme.recordButtonPhotoModeBackgroundImage forState:UIControlStateNormal];
  102. } else if (_recordButtonStyle == UGCKitRecordButtonStylePhoto) {
  103. [_btnStartRecord setImage:_theme.recordButtonPhotoModeIcon forState:UIControlStateNormal];
  104. [_btnStartRecord setBackgroundImage:_theme.recordButtonPhotoModeBackgroundImage forState:UIControlStateNormal];
  105. }
  106. [_btnStartRecord sizeToFit];
  107. _btnStartRecord.center = center;
  108. }
  109. - (void)setRecordButtonStyle:(UGCKitRecordButtonStyle)recordButtonStyle
  110. {
  111. if (_recordButtonStyle == recordButtonStyle) {
  112. return;
  113. }
  114. _recordButtonStyle = recordButtonStyle;
  115. [self _configRecordButton];
  116. }
  117. - (void)setPhotoModeEnabled:(BOOL)photoModeEnabled {
  118. if (photoModeEnabled) {
  119. _recordButtonSwitchControl.disabledIndexes = [NSIndexSet indexSet];
  120. } else {
  121. _recordButtonSwitchControl.disabledIndexes = [NSIndexSet indexSetWithIndex:UGCKitRecordButtonStylePhoto];
  122. }
  123. }
  124. - (void)setSpeedControlEnabled:(BOOL)enabled {
  125. _speedControlEnabled = enabled;
  126. _speedView.hidden = !enabled;
  127. _speedChangeBtn.hidden = !enabled;
  128. }
  129. - (void)setCountDownModeEnabled:(BOOL)countDownModeEnabled {
  130. _countDownModeEnabled = countDownModeEnabled;
  131. if (!_controlButtonsHidden) {
  132. _btnCountDown.hidden = !countDownModeEnabled;
  133. }
  134. }
  135. // 添加子View
  136. -(void)setupViews
  137. {
  138. CGFloat top = 0;
  139. CGFloat y = top;
  140. const CGFloat centerX = CGRectGetWidth(self.bounds) - 20 - BUTTON_CONTROL_SIZE/2;
  141. const CGFloat space = 20;
  142. // 音乐按钮
  143. if (self.musicButtonEnabled) {
  144. _btnMusic = [self _createButton:[_theme localizedString:@"UGCKit.Record.BeautyLabelMusic"]
  145. image:_theme.recordMusicIcon
  146. position:CGPointMake(centerX, y)];
  147. [self addSubview:_btnMusic];
  148. y = CGRectGetMaxY(_btnMusic.frame) + space;
  149. }
  150. // 比例切换按钮
  151. CGPoint p = CGPointMake(centerX, y);
  152. UIButton *btnRatio169 = [self _createButton:@"16:9"
  153. image:_theme.recordAspect169Icon
  154. position:p];
  155. btnRatio169.tag = VIDEO_ASPECT_RATIO_16_9;
  156. UIButton *btnRatio916 = [self _createButton:@"9:16"
  157. image:_theme.recordAspect916Icon
  158. position:p];
  159. btnRatio916.tag = VIDEO_ASPECT_RATIO_9_16;
  160. UIButton *btnRatio11 = [self _createButton:@"1:1"
  161. image:_theme.recordAspect11Icon
  162. position:p];
  163. btnRatio11.tag = VIDEO_ASPECT_RATIO_1_1;
  164. UIButton *btnRatio43 = [self _createButton:@"4:3"
  165. image:_theme.recordAspect43Icon
  166. position:p];
  167. btnRatio43.tag = VIDEO_ASPECT_RATIO_4_3;
  168. UIButton *btnRatio34 = [self _createButton:@"3:4"
  169. image:_theme.recordAspect34Icon
  170. position:p];
  171. btnRatio34.tag = VIDEO_ASPECT_RATIO_3_4;
  172. _btnRatioGroup = [[UGCKitSlideButton alloc] initWithButtons:@[btnRatio43, btnRatio34, btnRatio11, btnRatio916, btnRatio169]
  173. buttonSize:btnRatio43.frame.size
  174. spacing:30];
  175. _btnRatioGroup.selectedIndex = 3;
  176. CGRect frame = _btnRatioGroup.frame;
  177. frame.size = _btnRatioGroup.intrinsicContentSize;
  178. frame.origin = CGPointMake(centerX + CGRectGetWidth(btnRatio169.frame) / 2 - CGRectGetWidth(frame), y);
  179. _btnRatioGroup.frame = frame;
  180. [self addSubview:_btnRatioGroup];
  181. [_btnRatioGroup.buttons enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  182. if (obj.tag == _videoRatio) {
  183. _btnRatioGroup.selectedIndex = idx;
  184. *stop = YES;
  185. }
  186. }];
  187. y = CGRectGetMaxY(_btnRatioGroup.frame)+space;
  188. // 美颜按钮
  189. _btnBeauty = [self _createButton:[_theme localizedString:@"UGCKit.Record.BeautyLabelBeauty"]
  190. image:_theme.recordBeautyIcon
  191. position:CGPointMake(centerX, y)];
  192. [self addSubview:_btnBeauty];
  193. y = CGRectGetMaxY(_btnBeauty.frame) + space;
  194. // 混音近钮
  195. _btnAudioEffect = [self _createButton:[_theme localizedString:@"UGCKit.Record.AudioMix"]
  196. image:_theme.recordAudioEffectIcon
  197. position:CGPointMake(centerX, y)];
  198. [self addSubview:_btnAudioEffect];
  199. y = CGRectGetMaxY(_btnAudioEffect.frame) + space;
  200. _btnCountDown = [self _createButton:[_theme localizedString:@"UGCKit.Record.CountDown"]
  201. image:_theme.recordCountDownIcon
  202. position:CGPointMake(centerX, y)];
  203. [self addSubview:_btnCountDown];
  204. _btnCountDown.hidden = YES;
  205. // y = CGRectGetMaxY(_btnCountDown.frame) + space;
  206. NSArray<UIView *> *rightSideControls = @[_btnMusic, _btnRatioGroup, _btnBeauty,
  207. _btnCountDown, _btnAudioEffect];
  208. // 底部按钮
  209. CGRect bottomFrame = CGRectMake(0, self.frame.size.height - BUTTON_MASK_HEIGHT, self.frame.size.width, BUTTON_MASK_HEIGHT);
  210. UIView *bottomMask = [[UIView alloc] initWithFrame:bottomFrame];
  211. bottomMask.backgroundColor = [UIColor colorWithWhite:0 alpha:0.3];
  212. _bottomMask = bottomMask;
  213. [self addSubview:bottomMask];
  214. UIView *bottomContentView = bottomMask;//.contentView;
  215. _btnStartRecord = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, BUTTON_RECORD_SIZE, BUTTON_RECORD_SIZE)];
  216. _btnStartRecord.center = CGPointMake(bottomContentView.frame.size.width / 2, bottomContentView.frame.size.height - BUTTON_RECORD_SIZE/2 - 50);
  217. [bottomContentView addSubview:_btnStartRecord];
  218. _btnFlash = [UIButton buttonWithType:UIButtonTypeCustom];
  219. _btnFlash.bounds = CGRectMake(0, 0, BUTTON_CONTROL_SIZE, BUTTON_CONTROL_SIZE);
  220. _btnFlash.center = CGPointMake(25 + BUTTON_CONTROL_SIZE / 2, _btnStartRecord.center.y);
  221. [bottomContentView addSubview:_btnFlash];
  222. _btnCamera = [UIButton buttonWithType:UIButtonTypeCustom];
  223. _btnCamera.bounds = CGRectMake(0, 0, BUTTON_CONTROL_SIZE, BUTTON_CONTROL_SIZE);
  224. _btnCamera.center = CGPointMake(CGRectGetMaxX(_btnFlash.frame) + 25 + BUTTON_CONTROL_SIZE / 2, _btnStartRecord.center.y);
  225. [_btnCamera setImage:_theme.recordSwitchCameraIcon forState:UIControlStateNormal];
  226. [bottomContentView addSubview:_btnCamera];
  227. _btnDelete = [UIButton buttonWithType:UIButtonTypeCustom];
  228. _btnDelete.bounds = CGRectMake(0, 0, BUTTON_CONTROL_SIZE, BUTTON_CONTROL_SIZE);
  229. _btnDelete.center = CGPointMake((CGRectGetMaxX(_btnStartRecord.frame) + CGRectGetMaxX(self.bounds)) / 2, _btnStartRecord.center.y);
  230. [_btnDelete setImage:_theme.recordDeleteIcon forState:UIControlStateNormal];
  231. [_btnDelete setImage:_theme.recordDeleteHighlightedIcon forState:UIControlStateHighlighted];
  232. [bottomContentView addSubview:_btnDelete];
  233. _progressView = [[UGCKitVideoRecordProcessView alloc] initWithTheme:_theme
  234. frame:CGRectMake(0, 0, bottomContentView.bounds.size.width, BUTTON_PROGRESS_HEIGHT)
  235. minDuration:_minDuration
  236. maxDuration:_maxDuration];
  237. _progressView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
  238. _progressView.backgroundColor = [_theme.backgroundColor colorWithAlphaComponent:0.8];
  239. [bottomContentView addSubview:_progressView];
  240. UGCKitLabel *recordTimeLabel = [[UGCKitLabel alloc] init];
  241. recordTimeLabel.edgeInsets = UIEdgeInsetsMake(2, 8, 2, 8);
  242. [recordTimeLabel setText:LocalizationNotNeeded(@"00:00")];
  243. recordTimeLabel.font = [UIFont systemFontOfSize:12];
  244. recordTimeLabel.textColor = [UIColor whiteColor];
  245. recordTimeLabel.textAlignment = NSTextAlignmentCenter;
  246. [recordTimeLabel sizeToFit];
  247. recordTimeLabel.layer.cornerRadius = recordTimeLabel.bounds.size.height / 2;
  248. recordTimeLabel.layer.masksToBounds = YES;
  249. recordTimeLabel.center = CGPointMake(CGRectGetMidX(_bottomMask.frame),
  250. CGRectGetMinY(_bottomMask.frame) - 5 - CGRectGetMidY(recordTimeLabel.bounds));
  251. recordTimeLabel.backgroundColor = [_theme.backgroundColor colorWithAlphaComponent:0.5];
  252. [self addSubview:recordTimeLabel];
  253. _recordTimeLabel = recordTimeLabel;
  254. if (_isFrontCamera) {
  255. _torchOn = NO;
  256. }
  257. [self _configRecordButton];
  258. [self _configCameraSwitchButton];
  259. [self _configTorchButton];
  260. if (self.speedControlEnabled) {
  261. [self _createSpeedControl];
  262. }
  263. CGRect actionModeFrame = CGRectMake(0, CGRectGetHeight(bottomContentView.bounds) - 40, CGRectGetWidth(bottomContentView.bounds), 40);
  264. _recordButtonSwitchControl = [[UGCKitSlideOptionControl alloc] initWithFrame:actionModeFrame
  265. theme:_theme
  266. options:@[[_theme localizedString:@"UGCKit.Record.StillPhoto"],
  267. [_theme localizedString:@"相册"],
  268. // @"UGCKit.Record.TapCapture"],
  269. [_theme localizedString:@"UGCKit.Record.PressCapture"]]];
  270. _recordButtonSwitchControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
  271. _recordButtonSwitchControl.selectedIndex = 1;
  272. [bottomContentView addSubview:_recordButtonSwitchControl];
  273. _activeRightSideControlsBeforeHidden = [rightSideControls filteredArrayUsingPredicate:
  274. [NSPredicate predicateWithFormat:@"hidden=NO"]];
  275. }
  276. #pragma mark - setup buttons by state
  277. - (UIButton *)_createButton:(NSString *)title image:(UIImage *)image position:(CGPoint)location
  278. {
  279. UGCKitVerticalButton *button = [[UGCKitVerticalButton alloc] initWithTitle:title];
  280. [button setImage:image forState:UIControlStateNormal];
  281. [button setTitleColor:_theme.titleColor forState:UIControlStateNormal];
  282. button.titleLabel.font = [UIFont systemFontOfSize:12];
  283. button.frame = CGRectMake(location.x-BUTTON_CONTROL_SIZE/2, location.y, BUTTON_CONTROL_SIZE, BUTTON_CONTROL_SIZE);
  284. [button sizeToFit];
  285. button.center = CGPointMake(location.x, button.center.y);
  286. return button;
  287. }
  288. - (void)_configTorchButton {
  289. if (_torchOn) {
  290. [_btnFlash setImage:_theme.recordTorchOnIcon forState:UIControlStateNormal];
  291. [_btnFlash setImage:_theme.recordTorchOnHighlightedIcon forState:UIControlStateHighlighted];
  292. }else{
  293. [_btnFlash setImage:_theme.recordTorchOffIcon forState:UIControlStateNormal];
  294. [_btnFlash setImage:_theme.recordTorchOffHighlightedIcon forState:UIControlStateHighlighted];
  295. }
  296. }
  297. - (void)_configCameraSwitchButton {
  298. if (_isFrontCamera) {
  299. [_btnFlash setImage:_theme.recordTorchDisabledIcon forState:UIControlStateNormal];
  300. _btnFlash.enabled = NO;
  301. }else{
  302. [self _configTorchButton];
  303. _btnFlash.enabled = YES;
  304. }
  305. }
  306. -(void)_createSpeedControl
  307. {
  308. _speedBtnList = [NSMutableArray array];
  309. _speedView = [[UIView alloc] initWithFrame:CGRectMake(30, 13, CGRectGetWidth(_bottomMask.frame)-60, BUTTON_SPEED_HEIGHT)];
  310. _speedView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
  311. [_bottomMask addSubview:_speedView];
  312. _speedView.layer.cornerRadius = _speedView.bounds.size.height / 2;
  313. _speedView.layer.masksToBounds = YES;
  314. _speedView.backgroundColor = [UIColor blackColor];
  315. _speedView.alpha = 0.5;
  316. //合唱暂不支持变速录制
  317. _speedChangeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  318. _speedChangeBtn.titleLabel.adjustsFontSizeToFitWidth = YES;
  319. _speedChangeBtn.titleLabel.minimumScaleFactor = 0.5;
  320. [_speedChangeBtn setTitle:[self getSpeedText:2] forState:UIControlStateNormal];
  321. [_speedChangeBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  322. [_speedChangeBtn setBackgroundImage:_theme.recordSpeedCenterIcon forState:UIControlStateNormal];
  323. CGFloat btnSpace = 0;
  324. CGFloat padding = 16 * kScaleX;
  325. CGFloat btnWidth = (CGRectGetWidth(_speedView.bounds) - 2 * padding - btnSpace * 4 ) / 5;
  326. NSMutableArray *speedButtonArray = [[NSMutableArray alloc] initWithCapacity:BUTTON_SPEED_COUNT];
  327. for(int i = 0 ; i < BUTTON_SPEED_COUNT ; i ++)
  328. {
  329. UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
  330. btn.titleLabel.minimumScaleFactor = 0.5;
  331. btn.frame = CGRectMake(padding + (btnSpace + btnWidth) * i, 0, btnWidth, CGRectGetHeight(_speedView.bounds));
  332. [btn setTitle:[self getSpeedText:(SpeedMode)i] forState:UIControlStateNormal];
  333. [btn setTitleColor:_theme.speedControlSelectedTitleColor forState:UIControlStateNormal];
  334. [btn.titleLabel setFont:[UIFont systemFontOfSize:15]];
  335. btn.titleLabel.minimumScaleFactor = 0.5;
  336. btn.titleLabel.adjustsFontSizeToFitWidth = YES;
  337. btn.tag = i;
  338. [_speedView addSubview:btn];
  339. [speedButtonArray addObject:btn];
  340. }
  341. _speedBtnList = speedButtonArray;
  342. [self setSelectedSpeed:SpeedMode_Standard];
  343. [self.bottomMask addSubview:_speedChangeBtn];
  344. }
  345. -(NSString *)getSpeedText:(SpeedMode)speedMode
  346. {
  347. NSString *text = nil;
  348. switch (speedMode) {
  349. case SpeedMode_VerySlow:
  350. text = [_theme localizedString:@"UGCKit.Record.SpeedSlow0"];
  351. break;
  352. case SpeedMode_Slow:
  353. text = [_theme localizedString:@"UGCKit.Record.SpeedSlow"];
  354. break;
  355. case SpeedMode_Standard:
  356. text = [_theme localizedString:@"UGCKit.Record.SpeedStandard"];
  357. break;
  358. case SpeedMode_Quick:
  359. text = [_theme localizedString:@"UGCKit.Record.SpeedFast"];
  360. break;
  361. case SpeedMode_VeryQuick:
  362. text = [_theme localizedString:@"UGCKit.Record.SpeedFast0"];
  363. break;
  364. default:
  365. break;
  366. }
  367. return text;
  368. }
  369. - (void)setSelectedSpeed:(SpeedMode)tag
  370. {
  371. if (tag >= _speedBtnList.count) {
  372. return;
  373. }
  374. const float padding = 16 * kScaleX;
  375. UIButton *btn = _speedBtnList[(NSInteger)tag];
  376. // UIButton *btn = [self.speedView viewWithTag:(NSInteger)tag];
  377. CGRect rect = CGRectIntegral([_speedView convertRect:btn.frame toView:self.bottomMask]);
  378. CGRect frame = rect;
  379. frame.origin.y -= (BUTTON_SPEED_CHANGE_HEIGHT - rect.size.height) * 0.5;
  380. frame.size.height = BUTTON_SPEED_CHANGE_HEIGHT;
  381. UIImage *bgImage = _theme.recordSpeedCenterIcon;// @"speedChange_center";
  382. if (tag == 0) {
  383. frame.origin.x -= padding;
  384. frame.size.width += padding;
  385. bgImage = _theme.recordSpeedLeftIcon;
  386. } else if (tag == 4) {
  387. frame.size.width += padding;
  388. bgImage = _theme.recordSpeedRightIcon;
  389. }
  390. [_speedChangeBtn setBackgroundImage:bgImage forState:UIControlStateNormal];
  391. [CATransaction begin];
  392. [CATransaction setDisableActions:YES];
  393. _speedChangeBtn.frame = frame;
  394. [CATransaction commit];
  395. [_speedChangeBtn setTitle:[self getSpeedText:(SpeedMode)tag] forState:UIControlStateNormal];
  396. _speedMode = tag;
  397. }
  398. // Ignore touch events hit in the blank area
  399. - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  400. UIView *hitView = [super hitTest:point withEvent:event];
  401. if (hitView == self) {
  402. return nil;
  403. }
  404. return hitView;
  405. }
  406. @end