iOS小米遙控器的手勢監聽及UI實現,ios小米遙控器ui
這篇文章通過執行個體實現了一個類似小米手勢遙控器的功能頁面。
如下所示:
觸摸事件的響應通過對系統的觸摸實踐監聽來進行。
通過一個數組來對點的集合進行緩衝和分析。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ if (!self.allowsInteraction) return; UITouch *touch = [touches anyObject]; CGPoint start = [touch locationInView:self.view]; [_gestureManager beginMonitorWithPoint:start]; [self showLightAtPoint:start]; NSLog(@"touch begin");}- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ if (!self.allowsInteraction) return; UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self.view]; __weak typeof(&*self) weakSelf = self; [_gestureManager updateMonitorWithPoint:point action:^{ [weakSelf showLightAtPoint:point]; }];}
在觸摸開始和移動的時候,通過一個類來對手勢相關方法的觸發和管理及其他行為。即成員_gestureManager。
- (void)beginMonitorWithPoint:(CGPoint)point{ [self addPoint:point];}- (void)updateMonitorWithPoint:(CGPoint)point action:(dispatch_block_t)actionBlock{ _curTime++; int delta = (int)(_curTime - _lastSpawnTime); if (delta >= TIME_GAP) { if (actionBlock) { actionBlock(); } _lastSpawnTime = _curTime; [self addPoint:point]; }}
在開始監聽後,我們不需要把系統傳遞的每個點都進行觸發貼圖顯示點的軌跡,所以設定了成員來設定間隙位,已達到對點的密集程度進行控制。
- (void)endMonitor{ _curTime = 0; _lastSpawnTime = 0; [self pathAnalysis]; [self.pointPath removeAllObjects];}
在觸摸結束,結束這次監聽的時候,對這些成員進行了重設,分析手勢,並清空了點數組。
下面則開始進行分析手勢,分析的思路比較簡單。
計算起始點和終點之間的差值,對x,y進行分析,判斷方向,再判斷有無突出(是否是返回,功能等手勢)
- (void)pathAnalysis{ int count = self.pointPath.count; NSLog(@"points count: %d", count); if (count > JUDGE_CONTAIN) { goto SendNone; } else if (count == 1) { [self sendDelegateResult:MonitorResultTypeChosen]; } else { CGPoint start = valueToPoint([self.pointPath firstObject]); CGPoint end = valueToPoint([self.pointPath lastObject]); int deltaX = pSub(start, end).x; int deltaY = pSub(start, end).y; int midIndex = count/2; CGPoint mid = valueToPoint(self.pointPath[midIndex]); if (abs(deltaX) > JUDGE_X && abs(deltaY) < JUDGE_Y) { // horizontal direction if (deltaX < 0) { //right direction if (![self checkIsAlwaysCorrectDirection:MonitorResultTypeRight start:0 end:self.pointPath.count-1]) goto SendNone; if (pSub(start, mid).y > JUDGE_Y/2) { if ([self checkTrackIsMenu]) [self sendDelegateResult:MonitorResultTypeMenu]; else goto SendNone; } else if (abs(pSub(start, mid).y) < JUDGE_Y) { [self sendDelegateResult:MonitorResultTypeRight]; } else goto SendNone; } else { //left if (![self checkIsAlwaysCorrectDirection:MonitorResultTypeLeft start:0 end:self.pointPath.count-1]) goto SendNone; if (pSub(start, mid).y > JUDGE_Y/2) { if ([self checkTrackIsMenu]) { [self sendDelegateResult:MonitorResultTypeMenu]; } else goto SendNone; } else if (abs(pSub(start, mid).y) < JUDGE_Y) { [self sendDelegateResult:MonitorResultTypeLeft]; } else goto SendNone; } } else if (abs(deltaX) < JUDGE_X && abs(deltaY) > JUDGE_Y) { // vertical direction if (deltaY < 0) { // down if (![self checkIsAlwaysCorrectDirection:MonitorResultTypeDownwards start:0 end:self.pointPath.count-1]) goto SendNone; if (pSub(start, mid).x > JUDGE_X/2) { if ([self checkTrackIsBack]) [self sendDelegateResult:MonitorResultTypeBack]; else goto SendNone; } else if (abs(pSub(start, mid).x) < JUDGE_X) { [self sendDelegateResult:MonitorResultTypeDownwards]; } else goto SendNone; } else { // up if (![self checkIsAlwaysCorrectDirection:MonitorResultTypeUpwards start:0 end:self.pointPath.count-1]) goto SendNone; if (abs(pSub(start, mid).x) < JUDGE_X) [self sendDelegateResult:MonitorResultTypeUpwards]; else goto SendNone; } } else goto SendNone; } return; SendNone: [self sendDelegateResult:MonitorResultTypeNone]; return;}
還有一些需要用到的函數
UIKIT_STATIC_INLINE UIImageView * quickImageView(NSString * imgName) { UIImageView *iv = [[UIImageView alloc] initWithImage:ImageCache(imgName)]; return iv;}UIKIT_STATIC_INLINE CGPoint pSub(CGPoint a, CGPoint b) { return CGPointMake(a.x - b.x, a.y - b.y);}UIKIT_STATIC_INLINE NSValue * pointToValue(CGPoint a) { return [NSValue valueWithCGPoint:a];}UIKIT_STATIC_INLINE CGPoint valueToPoint(NSValue *v) { return [v CGPointValue];}
因為這些函數調用頻率都比較高,所以就聲明為內聯靜態了。
那些檢驗是否一個方向,或者是否突出的方法則如下所示:
- (BOOL)checkIsAlwaysCorrectDirection:(MonitorResultType)direct start:(int)start end:(int)end{ PathLogicBlock block; switch (direct) { case MonitorResultTypeRight: { block = ^(CGPoint v) { BOOL ret = (v.x >= 0)? NO: YES; return ret; }; } break; case MonitorResultTypeLeft: { block = ^(CGPoint v) { BOOL ret = (v.x <= 0)? NO: YES; return ret; }; } break; case MonitorResultTypeUpwards: { block = ^(CGPoint v) { BOOL ret = (v.y <= 0)? NO: YES; return ret; }; } break; case MonitorResultTypeDownwards: { block = ^(CGPoint v) { BOOL ret = (v.y >= 0)? NO: YES; return ret; }; } break; default: {return NO;} break; } for (int i = start; i+POINT_GAP < end; i += POINT_GAP) { CGPoint s = valueToPoint(self.pointPath[i]); CGPoint e = valueToPoint(self.pointPath[i+POINT_GAP]); CGPoint d = pSub(s, e); if (!block(d)) {return NO;} } return YES;}
這裡通過block設定條件,然後在遍曆中進行檢查及返回BOOL值。
其他也多用遍曆進行判斷,大部分分析都在一次遍曆以內。例如檢查是不是快顯功能表手勢或者返回手勢。
- (BOOL)checkTrackIsMenu{ int start = 0; int end = self.pointPath.count-1; BOOL flag = NO; while (valueToPoint(self.pointPath[start]).y >= valueToPoint(self.pointPath[start+1]).y) {start++;} while (valueToPoint(self.pointPath[end]).y >= valueToPoint(self.pointPath[end-1]).y) {end--;} if (abs(start-end) < 2*POINT_GAP) { flag = YES; } return flag;}- (BOOL)checkTrackIsBack{ int start = 0; int end = self.pointPath.count-1; BOOL flag = NO; while (valueToPoint(self.pointPath[start]).x >= valueToPoint(self.pointPath[start+1]).x) {start++;} while (valueToPoint(self.pointPath[end]).x >= valueToPoint(self.pointPath[end-1]).x) {end--;} if (abs(start-end) < 2*POINT_GAP) { flag = YES; } return flag;}
在圖片顯示方面,我對要用到的圖片在Controller載入之後進行了預先載入。
- (void)loadGestureManager{ _gestureManager = [MIGestureManager sharedManager]; _gestureManager.delegate = self; [_gestureManager preloadResources];}//gesture manager method- (void)preloadResources{ for (int i = 0; i < INITIAL_COUNT; i++) { UIImageView *iv = quickImageView(PointImage); [self.imageSet addObject:iv]; } _upImageView = quickImageView(UpwardsImage); _downImageView = quickImageView(DownwardsImage); _leftImageView = quickImageView(LeftImage); _rightImageView = quickImageView(RightImage); _homeImageView = quickImageView(HomeImage); _backImageView = quickImageView(BackImage); _menuImageView = quickImageView(MenuImage); _chosenImageView = quickImageView(chosenImages[0]); NSMutableArray *aniArr = [NSMutableArray array]; for (int i = 0; i < 4; i++) { UIImage *image = ImageCache(chosenImages[i]); [aniArr addObject:image]; } _chosenImageView.animationImages = aniArr; _chosenImageView.animationDuration = 0.7; _chosenImageView.animationRepeatCount = 1;}
視圖層次問題:
我們看小米遙控器的效果是都在一個網格下面,這裡就是在顯示點軌跡的視圖上覆蓋了一層網格視圖,以達到那樣的效果。
原始碼地址:Rannie/MIRemoteControl
當然,這個項目裡也有很多要解決的問題,在項目Readme.md中也有提到:
1.點的集合通過系統內建的NSMutableArray來維護,由於不能存結構體,導致需要不停的封包拆包動作如下:
static inline NSValue * pointToValue(CGPoint a) {
return [NSValue valueWithCGPoint:a];
}
static inline CGPoint valueToPoint(NSValue *v) {
return [v CGPointValue];
}
可以通過自己實現資料結構來維護點循序集合。
2.貼圖使用的是UIImageView,可以通過輕量級一些的layer設定content來實現。
3.這裡監聽的是控制器中的touch事件,也可以通過子類化UIGestureRecognizer來監聽UITouch,需要匯入一個UIGestureRecognizer子類化的一個標頭檔即可監聽touch事件。具體可以看Using UIGestureRecognizer with Swift Tutorial
4.點的路徑分析比較簡單,如果對統計有研究會有更出色的分析公式。
以上就是本篇部落格全部內容,歡迎指正和評論。
小米手機遙控器怎使用
1、下載並安裝小米遙控器進入介面,是一個模仿傳統遙控器的介面,可以上下左右,可以確定,下面三個是安卓系統的按鍵,基本和小米盒子內建的遙控器一樣。
小米遙控器
2、向右拉,彈出不同的操作介面選擇,包括按鍵、手勢、手柄和體感,上方還有搜尋,體感筆者測試良久由於家裡路由器不怎麼好,所以總是卡頓,希望會玩的朋友指導指導,這裡就不介紹了。
3、先看設定,可以選取震動反饋、體感控制、反向投屏和遙控器鍵盤。如果不喜歡每次點擊手機都震動,關了震動反饋就行,其他的建議開啟。
4、遊戲手柄的的介面,配合玩遊戲很帶勁,特別是對一些老遊戲機的模擬器。
5、手勢操作筆者感覺最好用的一個,因為只要在螢幕上畫畫手勢就可以操作手機,相比傳統方式不用看手機的虛擬鍵位。
6、手指向四個方向滑動分別對應不同的方向鍵。
7、搖動手機是回到主介面,橫著畫折線是返回鍵,豎著畫折線是菜單鍵,手指單機是確認。
8、可以把搜尋到的視頻直接傳到小米盒子播放,但是最好家裡的路由器能好些,否則有卡頓的危險,如果手機不能連線路由器了,請在手機刪除網路後重新輸入密碼串連即可。
以上就是小編為大家介紹的用手機來控制小米盒子的方法,這裡主要是大家要下載並安裝小米遙控器這款應用,不管是什麼品牌的手機都可以下載安裝後,進行對小米盒子的控制,怎麼樣,學會了吧。
小米2s是否有防竊聽功可以?
嗨!
可以用第三方軟體進行支援
更詳細的描述問題有助於網友理解你遇到的麻煩,協助你更準確的解決問題。謝謝你支援小米手機!
小米企業平台[官方認證]