前言
下圖為目前市面上一種常見的聊天會話Cell結(jié)構圖,如果模糊可以點擊放大。
其中主要的自定義媒體區(qū)域為CustomContentView,涵蓋大部分媒體類型的展示。

曾經(jīng)做IM以及朋友圈類似功能的時候,遇到過一個最疑惑的細節(jié)問題:
如何實現(xiàn)UIMenuController的指向視圖保持選中高亮狀態(tài)
如下圖對比效果:


雖然這只是一個細節(jié),但是我們常說:細節(jié)決定成敗。
過程
一切問題都是會則不難,難則不會。思考這個問題的過程中,走了不少彎路,曾經(jīng)想過多種可能解決的方法:
- 利用cell的selected狀態(tài),在selected方法中做操作。(嘗試失敗)
- 系統(tǒng)自帶tableViewcell觸發(fā)menu高亮效果,來實現(xiàn)這個操作。(嘗試失?。?/li>
- 聯(lián)系騰訊的相關iOS開發(fā)人員問一問。(沒去試,誰有門路嗎- -)
- 其他忘了。
這個問題擱置了許久,雖然暫時沒有好的思路,但是也一直沒有放棄這個問題。
一次機緣巧合,鼠標點了一下這個東西:

原來是可以拿到這個customContentView的!
不過UIMenuController里沒有直接提供接口,如圖:

但是iOS開發(fā)者都知道,經(jīng)常需要用到KVC來取值。沒錯,我們能看到有這個targetView就可以嘗試用 valueForKey:來獲取這個視圖。
注意,這種獲取_targetView的方法在iOS11之前會導致崩潰,所以可以在控制器觸發(fā)onLongPressCell:里用一個全局變量記錄這個View。
思路理清以后,事實證明這種方法是可行的,終于解決了這個困擾許久的問題。
實現(xiàn)
1. 對cell添加長按事件,可以用delegate或者block返回事件傳給ViewController。
- (void)longGesturePress:(UIGestureRecognizer*)gestureRecognizer {
if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] &&
gestureRecognizer.state == UIGestureRecognizerStateBegan) {
if (_messageDelegate && [_messageDelegate respondsToSelector:@selector(onLongPressCell:inView:)]) {
// 實現(xiàn)來自ViewController的messageDelegate 并將 CustomContentView傳遞
[_messageDelegate onLongPressCell:self.model.message inView:_customContentView];
}
}
}
接下來,ViewController立刻來響應這個代理方法。 侍從,來我身邊!
- (void)onLongPressCell:(NIMMessage *)message inView:(UIView *)view {
NSArray *items = [self menusItems:message];
if ([items count] && [self becomeFirstResponder]) {
UIMenuController *controller = [UIMenuController sharedMenuController];
controller.menuItems = items;
self.messageForMenu = message;
self.menuTargetView = view; // 記錄該被點擊的視圖
[controller setTargetRect:view.bounds inView:view]; // 這一步很關鍵
[controller setMenuVisible:YES animated:YES];
}
}
2. ViewController添加監(jiān)聽UIMenuController的通知。
// 這5種NSNotificationName都是UIMenuController的系統(tǒng)自帶監(jiān)聽方案
UIKIT_EXTERN NSNotificationName const UIMenuControllerWillShowMenuNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIMenuControllerDidShowMenuNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIMenuControllerWillHideMenuNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIMenuControllerDidHideMenuNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIMenuControllerMenuFrameDidChangeNotification __TVOS_PROHIBITED;
我們根據(jù)需要添加一種Show以及一種Hide即可。Show:設置高亮,Hide:恢復正常。
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(menuWillShow:)
name:UIMenuControllerWillShowMenuNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(menuDidHide:)
name:UIMenuControllerDidHideMenuNotification
object:nil];
3. 在通知觸發(fā)方法中,對targetView進行變更高亮/正常處理操作。
// 菜單將要出現(xiàn)
- (void)menuWillShow:(NSNotification *)notification {
[self showMenuControllerChangeTargetView:YES notification:notification];
}
// 菜單已經(jīng)隱藏
- (void)menuDidHide:(NSNotification *)notification {
[UIMenuController sharedMenuController].menuItems = nil;
[self showMenuControllerChangeTargetView:NO notification:notification];
}
// 設置圖片方法
- (void)showMenuControllerChangeTargetView:(BOOL)isHighlight notification:(NSNotification *)noti {
UIControlState state = isHighlight ? UIControlStateHighlighted : UIControlStateNormal;
// id base = [noti.object valueForKey:@"_targetView"];
// 拿到MenuController指向的targetView
id base = self.menuTargetView;
if ([base isKindOfClass:[IM_Session_ContentView_BaseView class]]) {
NIMMessageModel * model = [base model];
// 此方法是根據(jù)1.消息來源 2.選中狀態(tài) 來拿到bubbleImage(resizable)
UIImage *image = [base chatBubbleImageForState:state outgoing:model.message.isOutgoingMsg];
[[base bubbleImageView] setImage:image]; // 設置圖片樣式
}
}
我把IM_Session_ContentView_BaseView里這個方法拿出來供參考。BaseView是個基類視圖,其他類型的contentView繼承該父類即可避免多處寫入該方法。
- (UIImage *)chatBubbleImageForState:(UIControlState)state outgoing:(BOOL)outgoing{
NSString * imageName;
if (outgoing) { // 發(fā)出的消息
if (state == UIControlStateNormal) {
imageName = @"im_msgbg_white_right";
} else if (state == UIControlStateHighlighted) {
imageName = @"im_msgbg_white_right_click";
}
} else { // 收到的消息
if (state == UIControlStateNormal) {
imageName = @"im_msgbg_white_left";
} else if (state == UIControlStateHighlighted) {
imageName = @"im_msgbg_white_left_click";
}
}
if (imageName) {
UIImage *image = [UIImage imageNamed:imageName];
return [image resizableImageWithCapInsets:UIEdgeInsetsMake(18,25,17,25)
resizingMode:UIImageResizingModeStretch];
}
return nil;
}
最后
如有疑問或者有其他iOS問題可以留言,一起學習進步。
也可以聯(lián)系我,郵箱xingjl@outlook.com、 qq12087014。