【问题标题】:Handle tap on same segmented button?处理点击相同的分段按钮?
【发布时间】:2018-12-06 21:35:57
【问题描述】:

我正在尝试处理分段控件上的点击事件,但是当再次单击所选按钮时。比如下面的截图中已经选择了“秒”,再次点击“秒”按钮时,该如何处理?

我尝试了IBOutlet,但它仅在值更改时触发。然后我尝试了下面的代码,但它仅在值更改时触发相同。在两种情况下,选择“第二”时,再次单击“第二”都不会发射任何东西。有没有办法做到这一点?

segmentedControl.addTarget(self, action: "segementedAnyTap:", forControlEvents: .AllEvents)

【问题讨论】:

  • 你想完成什么?分段控件用于在不同选项之间切换,因此只有在所选选项更改时才会收到回调。
  • 我的分段控件是:全部和未读。当用户点击未读时,它会显示未读记录,但我想要它,所以当他们再次点击未读时,它会弹出一个警报,询问他们是否确定是否要将所有标记都标记为未读。我不想通过在导航栏中添加一个额外的按钮来将我的应用程序弄乱,只是为了将所有内容标记为已读,因为导航栏中已经有其他按钮了。
  • 我认为您可以使用momentary 属性来完成您正在寻找的功能。 developer.apple.com/library/ios/documentation/UIKit/Reference/…
  • 这似乎会极大地改变分段控件的行为,因为不再跟踪选定的索引,这将破坏现有的功能。我希望获得存储每个段的按钮的子视图并附加一个动作。
  • 很抱歉,您的设计需要更改。段控制永远不应该有这样的动作。如果您需要在应用中执行更多操作,请为其添加更多按钮,这并没有什么坏处,但请不要尝试以这种方式更改默认行为。

标签: ios ios8 uisegmentedcontrol iboutlet


【解决方案1】:

这对我有用,向 UISegmentedControl 添加手势识别器

- (void)viewDidLoad
{
  [super viewDidLoad];

  [self.segmentedControl addTarget:self action:@selector(valueChanged:) forControlEvents:UIControlEventValueChanged];
  UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touched:)];
  [self.segmentedControl addGestureRecognizer:tapGesture];
}

- (void)valueChanged:(id)sender
{
  // value change code
}

- (void)touched:(id)sender
{
  // code to check if the segmented controls index has not changed.
  // execute desired functionality
}

【讨论】:

  • 这确实会触发,但我的分段控件@IBAction 停止工作。
  • 这将停止触发值更改事件。
【解决方案2】:

很好的答案@Sgorbyo,这是它的 Swift 3 版本:

override func viewDidLoad() {
    super.viewDidLoad()
    let segmentedTapGesture = UITapGestureRecognizer(target: self, action: #selector(onTapGestureSegment(_:)))
    segmentedControl.addGestureRecognizer(segmentedTapGesture)
}

@IBAction func onTapGestureSegment(_ tapGesture: UITapGestureRecognizer) {
    let point = tapGesture.location(in: segmentedControl)
    let segmentSize = tipSegmentedControl.bounds.size.width / CGFloat(segmentedControl.numberOfSegments)
    let touchedSegment = Int(point.x / segmentSize)

    if segmentedControl.selectedSegmentIndex != touchedSegment {
        // Normal behaviour the segment changes
        segmentedControl.selectedSegmentIndex = touchedSegment
    } else {
        // Tap on the already selected segment
        segmentedControl.selectedSegmentIndex = touchedSegment
    }
    onSegment(segmentedControl)
}

@IBAction func onSegment(_ sender: Any) {
// Your segment changed selector
}

【讨论】:

    【解决方案3】:

    添加 KVO 观察。

    例如:

    #pragma mark - 
    
    - (void)viewDidLoad {
        [_segmentControl addObserver:self forKeyPath:@"selectedSegmentIndex" options:NSKeyValueObservingOptionInitial context:nil];
    }
    
    
    #pragma mark - KVO
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
        NSLog(@"segment index: %ld", (long)_segmentControl.selectedSegmentIndex);
    }
    

    结果:

    2015-06-22 12:31:54.155 Location test[27082:13176230] segment index: 0
    2015-06-22 12:31:54.740 Location test[27082:13176230] segment index: 0
    2015-06-22 12:31:55.821 Location test[27082:13176230] segment index: 1
    2015-06-22 12:31:56.529 Location test[27082:13176230] segment index: 1
    

    【讨论】:

      【解决方案4】:

      我不太确定您为什么要尝试实现这一目标,但我想建议继承 UISegmentedControl 并覆盖 touchesEnded:withEvent:

      - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
          [super touchesEnded:touches withEvent:event];
      
          [self sendActionsForControlEvents:UIControlEventTouchUpInside];
      }
      

      现在,您的 UIControlEventTouchUpInside 的预定选择器将在您每次按下每个段时被调用,并且仍保留 UISegmentedControl 的默认功能。

      注意:如果这是段的第一个选择,您需要自己处理(例如,保留前一个值的私有属性)。如果为UIControlEventValueChanged 添加选择器,它也会触发UIControlEventTouchUpInside 的选择器,这可能会导致一些混乱或错误。

      祝你好运,希望对你有所帮助。

      【讨论】:

        【解决方案5】:

        我认为这可以解决问题:

        - (void)viewDidLoad {
            [super viewDidLoad];
        
            UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touched:)];
            [self.segmentedControl addGestureRecognizer:tapGesture];
        }
        
        - (void) valueChanged:(id) sender {
            // Your segment changed selector
        }
        
        - (void) touched:(UITapGestureRecognizer *) tapGesture {
            CGPoint point = [tapGesture locationInView:self.segmentedControl];
            NSUInteger segmentSize = self.segmentedControl.bounds.size.width / self.segmentedControl.numberOfSegments;
            // Warning: If you are using segments not equally sized, you have to adapt the code in the next line
            NSUInteger touchedSegment = point.x / segmentSize;
            if (self.segmentedControl.selectedSegmentIndex != touchedSegment) {
                // Normal behaviour the segment changes
                self.segmentedControl.selectedSegmentIndex = touchedSegment;
            } else {
                // Tap on the already selected segment, I'm switching to No segment selected just to show the effect
                self.segmentedControl.selectedSegmentIndex = UISegmentedControlNoSegment;
            }
            // You have to call your selector because the UIControlEventValueChanged can't work together with UITapGestureRecognizer
            [self valueChanged:self.segmentedControl];
        }
        

        【讨论】:

          【解决方案6】:

          我对具有 3 个选项(“类别”)的分段控件执行以下操作。 _selectedCategory 是一个属性NSInteger,它跟踪分段控件当前选定的索引。在点击时,如果我发现 _selectedCategory 与按下的相同,则他们正在按下选定的分段控件,我将其关闭。

           -(IBAction)categorySelected:(id)sender {
                  if (_selectedCategory == [sender selectedSegmentIndex]) {
                      sender.selectedSegmentIndex = UISegmentedControlNoSegment;
                      // update my model, etc...
                  } else {
                      _selectedCategory = [sender selectedSegmentIndex];
                      switch (_selectedCategory) {
                          case 0:
                          // do logic...
                      }
                  }
          }
          

          【讨论】:

          • 我实际上并没有使用您的代码,但是 [sender selectedSegmentIndex] 位给了我几天来一直在寻找的线索!谢谢!!
          【解决方案7】:

          我遇到一种情况,我需要在选择之前使用段索引,并且我不希望 .valueChanged 事件停止触发,因此我想出了这个。

          UISegmentedControl 创建一个子类并覆盖touchesEnded

          override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
          
              // Previous selected segment index
              let oldIdx = selectedSegmentIndex
          
              super.touchesEnded(touches, with: event)
          
              // New selected segment index
              let newIdx = selectedSegmentIndex
          
              // If the previously selected segment index is equal to the new one, 
              // then you are tapping on the same segment button.
              // Call a block, delegate method or whatever to notify this
          }
          

          【讨论】:

            【解决方案8】:

            在您对问题的评论中,您提到您试图让用户选择“未读”以显示所有未读邮件,然后让他们再次单击以将所有邮件标记为未读。我建议不要为此使用段控件,而是添加一个“标记所有未读”按钮,该按钮会在选择“未读”段时出现。这将完成您尝试添加的功能,同时让用户清楚地知道他们有办法将所有内容标记为未读。

            【讨论】:

            • 我没有地方可以在导航标题中放置“标记全部已读”按钮。左侧和右侧按钮用于其他功能(菜单滑块和搜索)。所以我试图节省屏幕空间并利用现有空间发挥创意。
            • 如果空间是一个问题,请考虑使用两个按钮而不是分段控件,即“button1”、“button2”。当 button2 被点击时,更改它的颜色和文本以明确它现在用于另一个目的,您将能够在第二次点击的同时让您的用户看到他们可用的选项。
            【解决方案9】:

            您可以将您的应用设置为显示来自未读段的弹出框,并使用一个按钮将所有内容显示为未读。要放置弹出框,请使用:

            CGRect frame = [segmentControl frame];
            frame =CGRectMake((frame.size.width/2*butIndex), 0, frame.size.width/2, segmentControl.bounds.size.height);
            
            [popOver presentPopoverFromRect:frame inView:segmentControl permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
            

            【讨论】:

              【解决方案10】:

              我知道有点晚了,但另一种对我很有效的技术......

              在 UISegmentedControl 的每个段上添加一个具有清晰背景的 UIButton。每个 UIButton 都可以有自己的 UIControlEventTouchUpInside 事件处理程序——它可以改变 UISegmentedControl 的 selectedSegmentIndex。

              然后将 UISegmentedControl.userInteractionEnabled 设置为 NO,并删除其 UIControlEventValueChanged 事件处理程序。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2012-08-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-01-24
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多