ユーザーがリストの順序を選択できるセグメント化されたコントロールがあります。正常に動作します。
ただし、既に選択されているセグメントをタップすると、順序が逆になります。すべてのコードが用意されていますが、それらのセグメントのタップを登録する方法がわかりません。使用できるコントロールイベントはUIControlEventValueChangedのみですが、機能していません(選択したセグメントが実際に変更されていないため)。
これに対する解決策はありますか?もしそうなら、それは何ですか?
前もって感謝します!
UISegmentedControl
をサブクラス化してから、override setSelectedSegmentIndex:
- (void) setSelectedSegmentIndex:(NSInteger)toValue {
if (self.selectedSegmentIndex == toValue) {
[super setSelectedSegmentIndex:UISegmentedControlNoSegment];
} else {
[super setSelectedSegmentIndex:toValue];
}
}
IBを使用する場合は、UISegmentedControl
のクラスをサブクラスに設定してください。
これで、通常と同じUIControlEventValueChanged
をリッスンできます。ただし、ユーザーがセグメントを選択解除した場合は、selectedSegmentIndex
に等しいUISegmentedControlNoSegment
が表示されます。
-(IBAction) valueChanged: (id) sender {
UISegmentedControl *segmentedControl = (UISegmentedControl*) sender;
switch ([segmentedControl selectedSegmentIndex]) {
case 0:
// do something
break;
case 1:
// do something
break;
case UISegmentedControlNoSegment:
// do something
break;
default:
NSLog(@"No option for: %d", [segmentedControl selectedSegmentIndex]);
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
を使用するとさらに良いと思います----これはUISegmentedControlの動作です。さらに、-(void)setSelectedSegmentIndex:(NSInteger)toValue
をオーバーロードする必要はないようです
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSInteger current = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
if (current == self.selectedSegmentIndex)
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
自分でこれを望んでいました。ジュリアンのソリューションを取得し(ありがとう!)、わずかに修正しました。
次のUISegmentedControlサブクラスは、値が変更されなかった場合でもUIControlEventValueChanged
イベントをトリガーするだけです。
AKSegmentedControl.h
#import <UIKit/UIKit.h>
@interface AKSegmentedControl : UISegmentedControl {
}
@end
AKSegmentedControl.m
#import "AKSegmentedControl.h"
@implementation AKSegmentedControl
- (void)setSelectedSegmentIndex:(NSInteger)toValue {
// Trigger UIControlEventValueChanged even when re-tapping the selected segment.
if (toValue==self.selectedSegmentIndex) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
[super setSelectedSegmentIndex:toValue];
}
@end
これはiOS 4と5の両方で機能します:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
int oldValue = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
if ( oldValue == self.selectedSegmentIndex )
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
実際に新しいセグメントがタップされない限り、setSelectedSegmentIndexが呼び出されることはないため、現在のソリューションはまだ機能しません。少なくとも私の場合、これは機能しませんでしたが、iOS5を使用していますが、おそらくこのソリューションはiOS4でも機能しました。とにかく、これが私の解決策です。次の追加のサブクラスメソッドが1つ必要です。
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self setSelectedSegmentIndex:self.selectedSegmentIndex];
[super touchesEnded:touches withEvent:event];
}
幸運を!
これはかなり古い質問だったので、iOS 5+アプリでこれに遭遇したときに使用したかなりシンプルなソリューションを追加すると思いました。
私がしたことは、Tap Gesture Recognizerを.xibファイルのUISegmentedControlにドラッグすることだけでした。新しいジェスチャーレコグナイザーの接続をチェックして、セグメント化されたコントロールが唯一のgestureRecognizerアウトレットであることを確認し、コントローラーで起動するメソッドにそのアクションを結び付けます。
このメソッドは、セグメント化されたコントロールに触れるたびに起動する必要があるため、選択されたインデックスをおそらくインスタンス変数でチェックして、ユーザーが既に選択されているセグメントに触れたかどうかを確認します。
@implementation MyViewController
@synthesize selectedSegment;
@synthesize segmentedControl;
// connected to Tap Gesture Recognizer's action
- (IBAction)segmentedControlTouched:(id)sender
{
NSLog(@"Segmented Control Touched");
if (selectedSegment == [segmentedControl selectedSegmentIndex]) {
// Do some cool stuff
}
selectedSegment = [segmentedControl selectedSegmentIndex];
}
@end
IOS8では、ここでのすべての回答は機能しないか、変更を2回トリガーします。私は非常に簡単な解決策を思いついた-それで私はそれを共有したいと思います。
サブクラスには次のものしかありません:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self setSelectedSegmentIndex:UISegmentedControlNoSegment];
}
セグメント化されたコントロールセグメントを変更する前に、選択したセグメントを-1にリセットします。 touchesEndedメソッドで発生します。
これは動作します:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
int oldValue = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
if (oldValue == self.selectedSegmentIndex)
{
[super setSelectedSegmentIndex:UISegmentedControlNoSegment];
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
Swiftでは、少なくとも次の2つのソリューションを想像できます。特別な場合のために、選択したセグメントがトグルボタンとして機能する3番目のソリューションも投稿しました。
解決策1:
ヒント:このソリューションは、瞬間的なcontrolSegmentsでのみ機能します!固定制御のソリューションが必要な場合は、ソリューション2を選択してください
アイデアは、タッチアップ内部で起動する2番目のイベントを登録することです。
// this solution works only for momentary segment control:
class Solution1ViewController : UIViewController {
var current:Int = UISegmentedControlNoSegment
@IBOutlet weak var mySegmentedControl: UISegmentedControl! {
didSet {
guard mySegmentedControl != nil else { return }
self.mySegmentedControl!.addTarget(
self,
action:#selector(changeSelection(sender:)),
for: .valueChanged)
self.mySegmentedControl!.addTarget(
self,
action:#selector(changeSelection(sender:)),
for: .touchUpInside)
}
}
func changeSelection(sender: UISegmentedControl) {
if current == sender.selectedSegmentIndex {
// user hit the same button again!
print("user hit the same button again!")
}
else {
current = sender.selectedSegmentIndex
// user selected a new index
print("user selected a new index")
}
}
}
解決策2:もう1つの方法は、セグメントインデックスが変更されていない場合でも、UISegmentedControl
のタッチ関数をオーバーライドし、valueChanged
を起動することです。したがって、次のようにUISegmentedControl
をオーバーライドできます。
// override UISegmentControl to fire event on second hit:
class Solution2SegmentControl : UISegmentedControl
{
private var current:Int = UISegmentedControlNoSegment
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
current = self.selectedSegmentIndex
super.touchesBegan(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
if current == self.selectedSegmentIndex {
self.sendActions(for: .valueChanged)
}
}
}
// solution2 works with stationary (default) segment controls
class Solution2ViewController : UIViewController {
var current:Int = UISegmentedControlNoSegment
@IBOutlet weak var mySegmentedControl: UISegmentedControl! {
didSet {
guard mySegmentedControl != nil else { return }
self.mySegmentedControl!.addTarget(
self,
action:#selector(changeSelection(sender:)),
for: .valueChanged)
}
}
func changeSelection(sender: UISegmentedControl) {
if current == sender.selectedSegmentIndex {
// user hit the same button again!
print("user hit the same button again!")
}
else {
current = sender.selectedSegmentIndex
// user selected a new index
print("user selected a new index")
}
}
}
ソリューション3:選択したセグメントをトグルボタンにする場合は、ソリューション2を変更して次のようにコードをクリーンアップできます。
class MyToggleSegmentControl : UISegmentedControl {
/// you could enable or disable the toggle behaviour here
var shouldToggle:Bool = true
private var current:Int = UISegmentedControlNoSegment
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
current = self.selectedSegmentIndex
super.touchesBegan(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
if shouldToggle, current == self.selectedSegmentIndex {
self.selectedSegmentIndex = UISegmentedControlNoSegment
}
}
}
次のようにchangeSelection
関数をクリーンアップできます。
func changeSelection(sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case UISegmentedControlNoSegment:
print("none")
default:
print("selected: \(sender.selectedSegmentIndex)")
}
}
これが私の解決策です。 ValueChangedイベントをすべてのタッチで発生させたい場合、最もエレガントだと思います...
。h
@interface UISegmentedControlAllChanges : UISegmentedControl
@end
。m
@implementation UISegmentedControlAllChanges
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self sendActionsForControlEvents:UIControlEventValueChanged];
[super touchesEnded:touches withEvent:event];
}
@end
私のために働いたコードの下。同じセグメントを繰り返しタップすると、選択が解除されます。
@implementation WUUnselectableSegmentedControl
{
NSUInteger _selectedIndexBeforeTouches;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
_selectedIndexBeforeTouches = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
if (_selectedIndexBeforeTouches == self.selectedSegmentIndex)
{
// Selection didn't change after touch - deselect
self.selectedSegmentIndex = UISegmentedControlNoSegment;
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
@end
答えるのは楽しい質問のようです。このスキームは、Steve Eとyershuachuのソリューションを拡張したものです。このバージョンでは、UITapGestureRecognizerを使用してすべてのタッチをキャプチャし、selectedSegmentIndexを-1に設定します。ただし、すべてのタッチをUISegmentedControlに渡して、通常のタッチを処理できるようにします。サブクラス化は必要ありません。
UISegmentedControl *mySegControl;
- (void)viewDidLoad
{
[super viewDidLoad];
[mySegControl addTarget:self action:@selector(segmentAction:)
forControlEvents:UIControlEventValueChanged];
// allow the a seg button tap to be seen even if already selected
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(unitsSegTap:)];
tapGesture.cancelsTouchesInView = NO; // pass touches through for normal taps
[mySegControl addGestureRecognizer:tapGesture];
// continue setup ...
}
- (void)segTap:(UITapGestureRecognizer *)sender
{
mySegControl.selectedSegmentIndex = -1;
}
// called for UIControlEventValueChanged
- (void)segmentAction:(UISegmentedControl *)sender
{
NSInteger index = sender.selectedSegmentIndex;
NSLog(@"unitsSegmentAction %d",(int)index);
// process the segment
}
ボブ・デ・グラーフの回答に基づく:
_-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self sendActionsForControlEvents:UIControlEventAllTouchEvents];
[super touchesEnded:touches withEvent:event];
}
_
UIControlEventAllTouchEvents
の代わりにUIControlEventValueChanged
を使用していることに注意してください。
また、-(void)setSelectedSegmentIndex:
を呼び出す必要もありません。
iOS 9ソリューション。そのようなクラスでUISegmentedControlをオーバーライドします。
@interface SegmentedControl()
@property (nonatomic) NSInteger previousCurrent;
@end
@implementation SegmentedControl
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.previousCurrent = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
if (self.selectedSegmentIndex == self.previousCurrent) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
self.previousCurrent = NSNotFound;
}
@end
大きな助け!私がやりたいことは、ボタンを1つまたはまったく設定しないというオプションを使用することです。ボタンが設定されると、2回目のタップでボタンが設定解除されます。これは私の修正です:
- (void)setSelectedSegmentIndex:(NSInteger)toValue
{
// Trigger UIControlEventValueChanged even when re-tapping the selected segment.
if (toValue==self.selectedSegmentIndex) {
[super setSelectedSegmentIndex:UISegmentedControlNoSegment]; // notify first
[self sendActionsForControlEvents:UIControlEventValueChanged]; // then unset
} else {
[super setSelectedSegmentIndex:toValue];
}
}
Notifyでは、最初にコントロールを読み取って現在の設定を見つけてからリセットします。
現在のiOS v8.xでは、選択した回答のソリューションは機能しませんでした。しかし、@ Andyは解決策を提供します。
UISegmentedControl
をサブクラス化し、サブクラスのtouchesEnded
メソッドを上書きします。
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self sendActionsForControlEvents:UIControlEventAllTouchEvents];
[super touchesEnded:touches withEvent:event];
}
UISegmentedControlを使用する予定のクラスで、iVar currentSelectedSegIndexを作成します。 UIControlEventValueChangedアクションメソッドの横にUIControlEventAllTouchEventsを追加します。
NSInteger currentSelectedSegIndex;
[aSegmentedController addTarget:self action:@selector(aSegmentedControllerSelection:) forControlEvents:UIControlEventValueChanged];
[aSegmentedController addTarget:self action:@selector(aSegmentedControllerAllTouchEvent:) forControlEvents:UIControlEventAllTouchEvents];
2つのアクションメソッドを実装します。
-(void)aSegmentedControllerAllTouchEvent:(MyUISegmentedControl *)seg
{
seg.selectedSegmentIndex = UISegmentedControlNoSegment;
}
-(void)aSegmentedControllerSelection:(MyUISegmentedControl *)seg
{
if (currentSelectedSegIndex == seg.selectedSegmentIndex)
{
seg.selectedSegmentIndex = UISegmentedControlNoSegment;
currentSelectedSegIndex = NSIntegerMax;
NSLog(@"%s Deselected.", __PRETTY_FUNCTION__);
// do your stuff if deselected
return;
}
NSLog(@"%s Selected index:%u", __PRETTY_FUNCTION__, seg.selectedSegmentIndex);
currentSelectedSegIndex = seg.selectedSegmentIndex;
//do your stuff if selected index
}
KVOを使用して、既に選択されているiOS8のセグメントを反転しています。
#import "QCSegmentedControl.h"
static void *QCSegmentedControlContext = &QCSegmentedControlContext;
@implementation QCSegmentedControl
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self registerKVO];
}
return self;
}
- (void)dealloc {
[self removeObserver:self forKeyPath:@"selectedSegmentIndex"];
}
#pragma mark -
- (void)registerKVO {
[self addObserver:self
forKeyPath:@"selectedSegmentIndex"
options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
context:QCSegmentedControlContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if (context == QCSegmentedControlContext) {
NSNumber *new = change[NSKeyValueChangeNewKey];
NSNumber *old = change[NSKeyValueChangeOldKey];
if (new.integerValue == old.integerValue) {
self.selectedSegmentIndex = UISegmentedControlNoSegment;
}
}
}
@end