6

現在、チャット用のアプリケーションがあります。入力ボックスには UItextField を、表示メッセージにはバブルを使用しました。システム SMS のようなものです。メッセージの吹き出し (ラベル) でコピー ペーストを有効にしたい。問題は、UIMenuController を表示したいときに、コピーする必要があるラベルがファーストレスポンダーになる必要があることです。キーボードが現在表示されている場合、ラベルがファーストレスポンダーになると、テキストフィールドはフォーカスを失い、キーボードは自動的に非表示になります。これにより、UI のスクロールが発生し、気分が悪くなります。メニューを表示する必要がある場合でも、キーボードを表示したままにできる方法はありますか?

ここに画像の説明を入力

ここに画像の説明を入力

4

3 に答える 3

12

ここでまだ答えを探している人はコードです(主なアイデアはneon1に属します。リンクされた質問を参照してください)。

アイデアは次のとおりです。レスポンダーが特定のアクションを処理する方法を知らない場合、チェーン内の次のレスポンダーに伝播します。これまでのところ、ファーストレスポンダーの候補は 2 つあります。

  1. 細胞
  2. テキストフィールド

それぞれがレスポンダーのチェーンを別々に持っています (実際、いいえ、共通の祖先を持っているため、チェーンには共通点がありますが、それを使用することはできません):

UITextField <- UIView <- ... <- UIWindow <- UIApplication
UITableViewCell <- UIView <- ... <- UIWindow <- UIApplication

したがって、次の一連のレスポンダーが必要です。

UITextField <- UITableViewCell <- ..... <- UIWindow <- UIApplication

UITextField をサブクラス化する必要があります (コードはhereから取得されます)。

CustomResponderTextView.h

@interface CustomResponderTextView : UITextView
@property (nonatomic, weak) UIResponder *overrideNextResponder;
@end

CustomResponderTextView.m

@implementation CustomResponderTextView

@synthesize overrideNextResponder;

- (UIResponder *)nextResponder {
    if (overrideNextResponder != nil)
        return overrideNextResponder;
    else
        return [super nextResponder];
}

@end

このコードは非常に単純です。カスタムの次のレスポンダーを設定していない場合は実際のレスポンダーを返し、それ以外の場合はカスタムのレスポンダーを返します。

これで、コードに新しいレスポンダーを設定できます (私の例ではカスタム アクションを追加しています):

CustomCell.m

@implementation CustomCell
- (BOOL) canBecomeFirstResponder {
    return YES;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    return (action == @selector(copyMessage:) || action == @selector(deleteMessage:));
}
@end

- (void) copyMessage:(id)sender {
   // copy logic here
}

- (void) deleteMessage:(id)sender {
   // delete logic here
}

コントローラ

- (void) viewDidLoad {
    ...
    UIMenuItem *copyItem = [[UIMenuItem alloc] initWithTitle:@"Custom copy" action:@selector(copyMessage:)];
    UIMenuItem *deleteItem = [[UIMenuItem alloc] initWithTitle:@"Custom delete" action:@selector(deleteMessage:)];
    UIMenuController *menu = [UIMenuController sharedMenuController];
    [menu setMenuItems:@[copyItem, deleteItem]];
    ...
}

- (void) longCellTap {
    // cell is UITableViewCell, that has received tap
    if ([self.textField isFirstResponder]) {
        self.messageTextView.overrideNextResponder = cell;
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuDidHide:) name:UIMenuControllerDidHideMenuNotification object:nil];
    } else {
        [cell becomeFirstResponder];
    }
}

- (void)menuDidHide:(NSNotification*)notification {
    self.messageTextView.overrideNextResponder = nil;
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerDidHideMenuNotification object:nil];
}

最後のステップは、最初のレスポンダー (この場合はテキスト フィールド) をプロポさせ、次のレスポンダー (この場合はセル) にアクションを実行させることですcopyMessage:。私たちが知っているように、特定のレスポンダーがアクションを処理できる場合、deleteMessage:iOs が知っているように送信します。canPerformAction:withSender:

CustomResponderTextView.m次の関数を変更して追加する必要があります。

CustomResponderTextView.m

...
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    if (overrideNextResponder != nil)
        return NO;
    else
        return [super canPerformAction:action withSender:sender];
}
...

カスタムのネクスト レスポンダーを設定した場合は、それにすべてのアクションを送信します (textField で何らかのアクションが必要な場合は、この部分を変更できます)。

于 2014-05-24T21:48:09.097 に答える
2

ここに画像の説明を入力

Nikita Takeのソリューションを介してSwiftで実行しました。

テキスト入力用のテキスト フィールドとメッセージ用のラベル (表示) があるチャット画面があります。メッセージ ラベルをタップすると、メニュー (コピー/貼り付け/...) が表示されますが、キーボードは開いたままにしておく必要があります。

入力テキスト フィールドをサブクラス化しました。

import UIKit

class TxtInputField: UITextField {

weak var overrideNextResponder: UIResponder?

override func nextResponder() -> UIResponder? {
  if overrideNextResponder != nil {
    return overrideNextResponder
  } else {
    return super.nextResponder()
  }
}

override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
  if overrideNextResponder != nil {
    return false
  } else {
    return super.canPerformAction(action, withSender: sender)
  }
 }
}

次に、UIMenuController を開始するロジックを持つカスタム メッセージ ラベル (UILabel のサブクラスですが、あなたの場合はビュー コントローラーにすることができます) に、後で追加しました

if recognizer.state == UIGestureRecognizerState.Began { ... 

次のチャンク

if let activeTxtField = getMessageThreadInputSMSField() {
  if activeTxtField.isFirstResponder() {
    activeTxtField.overrideNextResponder = self
  } else {
    self.becomeFirstResponder()
  }
} else {
  self.becomeFirstResponder()
}

ユーザーが UIMenuController の外をタップしたとき

func willHideEditMenu() {
    if let activeTxtField = getMessageThreadInputSMSField() {
      activeTxtField.overrideNextResponder = nil
   }
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIMenuControllerWillHideMenuNotification, object: nil)
  }

activeTxtField オブジェクトへの参照を取得する必要があります。ナビゲーション スタックを反復処理し、目的のテキスト フィールドを保持する View Controller を取得してから使用しました。

必要な場合に備えて、その部分のスニペットもここにあります。

var activeTxtField = CutomTxtInputField()
  for vc in navigationController?.viewControllers {
    if vc is CustomMessageThreadVC {
     let msgVC = vc as! CustomMessageThreadVC         
     activeTxtField = msgVC.textBubble
   }
}
于 2016-10-04T11:59:49.307 に答える
2

uitextfield をサブクラス化し、firstresponder をオーバーライドすることができます。uitextfield が最初のレスポンダーである場合は、長押しジェスチャ ハンドラーをチェックインし、nextresponder をオーバーライドします。

于 2012-11-28T12:41:28.313 に答える