UIPickerView中的多行选择

时间:2010-05-25 08:13:45

标签: iphone uipicker uitableview

我想在UIPickerView中选择多行,所以我有想法在表格中显示我的数据并将此表作为子视图添加到选择器中。我试过这个,但没有成功。

有任何建议怎么做?

7 个答案:

答案 0 :(得分:14)

你可以在没有UITableView的情况下使用UITapGestureRecognizer来做到这一点:)

另外,在.h文件中的某处添加NSMutableArray *selectedItems

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
    UITableViewCell *cell = (UITableViewCell *)view;

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
        [cell setBackgroundColor:[UIColor clearColor]];
        [cell setBounds: CGRectMake(0, 0, cell.frame.size.width -20 , 44)];
        UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleSelection:)];
        singleTapGestureRecognizer.numberOfTapsRequired = 1;
        [cell addGestureRecognizer:singleTapGestureRecognizer];
    }

    if ([selectedItems indexOfObject:[NSNumber numberWithInt:row]] != NSNotFound) {
        [cell setAccessoryType:UITableViewCellAccessoryCheckmark];
    } else {
        [cell setAccessoryType:UITableViewCellAccessoryNone];
    }
    cell.textLabel.text = [datasource objectAtIndex:row];
    cell.tag = row;

    return cell;
}

- (void)toggleSelection:(UITapGestureRecognizer *)recognizer {
    NSNumber *row = [NSNumber numberWithInt:recognizer.view.tag];
    NSUInteger index = [selectedItems indexOfObject:row];
    if (index != NSNotFound) {
        [selectedItems removeObjectAtIndex:index];
        [(UITableViewCell *)(recognizer.view) setAccessoryType:UITableViewCellAccessoryNone];
    } else {
        [selectedItems addObject:row];
        [(UITableViewCell *)(recognizer.view) setAccessoryType:UITableViewCellAccessoryCheckmark];
    }
}

答案 1 :(得分:13)

在iPhone上进行多项选择的“已接受”用户界面是使用带有复选标记的UITableView(即不要使用UIPickerView进行多项选择)。

但是,如果必须的话,可以通过制作一个空的选择器并在其顶部放置一个表格视图来说明如何在http://www.iphonedevsdk.com/forum/iphone-sdk-development/14634-uipickerview-multiple-selection.html伪造它。

答案 2 :(得分:7)

如果有人感兴趣的话,实施快速入侵以获取UIPickerView多选行为(如在Mobile Safari中)而不在pickerview前面添加其他视图:https://github.com/alexleutgoeb/ALPickerView

非常感谢改进!

答案 3 :(得分:2)

以下代码适用于 iOS 10 。我没有机会在旧版本上测试它,但我认为它应该可行。这个想法类似于@ suda的一个,但它直接在选择器视图中添加了一个点击识别器,而不是为每一行添加点击识别器,因为这在iOS7 +上不起作用。

为简洁起见,我没有包括UIPickerViewDataSourceUIPickerViewDelegate协议的完整实现,只包括实现多重选择的相关部分。

// 1. Conform to UIGestureRecognizerDelegate protocol
@interface MyViewController () <UIPickerViewDataSource, UIPickerViewDelegate, UIGestureRecognizerDelegate>

@property (nonatomic, strong) NSMutableArray *selectedArray;    // To store which rows are selected
@property (nonatomic, strong) NSArray *dataArray;           // Picker data
@property (weak, nonatomic) IBOutlet UIPickerView *pickerView;

@end

@implementation MyViewController

- (void)viewDidLoad 
{    
    [super viewDidLoad];

    self.selectedArray = [NSMutableArray array]; 
    self.dataArray = @[@"Option 1", @"Option 2", @"Option 3", @"Option 4"];

    // 2. Add tap recognizer to your picker view
    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(pickerTapped:)];
    tapRecognizer.delegate = self;
    [self.pickerView addGestureRecognizer:tapRecognizer];
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return true;
}

- (void)pickerTapped:(UITapGestureRecognizer *)tapRecognizer
{
    // 3. Find out wich row was tapped (idea based on https://stackoverflow.com/a/25719326) 
    if (tapRecognizer.state == UIGestureRecognizerStateEnded) {
        CGFloat rowHeight = [self.pickerView rowSizeForComponent:0].height;
        CGRect selectedRowFrame = CGRectInset(self.pickerView.bounds, 0.0, (CGRectGetHeight(self.pickerView.frame) - rowHeight) / 2.0 );
        BOOL userTappedOnSelectedRow = (CGRectContainsPoint(selectedRowFrame, [tapRecognizer locationInView:self.pickerView]));
        if (userTappedOnSelectedRow) {
            NSInteger selectedRow = [self.pickerView selectedRowInComponent:0];
            NSUInteger index = [self.selectedArray indexOfObject:[NSNumber numberWithInteger:selectedRow]];

            if (index != NSNotFound) {
                NSLog(@"Row %ld OFF", (long)selectedRow);
                [self.selectedArray removeObjectAtIndex:index];
            } else {
                NSLog(@"Row %ld ON",  (long)selectedRow);
                [self.selectedArray addObject:[NSNumber numberWithInteger:selectedRow]];
            }
            // I don't know why calling reloadAllComponents sometimes scrolls to the first row
            //[self.pickerView reloadAllComponents];
            // This workaround seems to work correctly:
            self.pickerView.dataSource = self;            
            NSLog(@"Rows reloaded");
        }
    }
}

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view 
{    
    // 4. Customize your Picker row as needed. 
    // This is a very simple view just to make the point

    UILabel *pickerLabel = (UILabel *)view;

    if (pickerLabel == nil) {
        pickerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 400, 32)];
    }

    BOOL isSelected = [self.selectedArray indexOfObject:[NSNumber numberWithInteger:row]] != NSNotFound;
    NSString *text = [self.dataArray objectAtIndex:row];
    text = [text stringByAppendingString:isSelected ? @"☒ " : @"☐ "];
    [pickerLabel setText:text];

    return pickerLabel;
}


// Do not forget to add the remaining methods to conform the UIPickerViewDataSource and UIPickerViewDelegate protocols!

@end

答案 4 :(得分:1)

class StudentsController < ApplicationController
  before_action :set_student, only: [:show, :edit, :update, :destroy]

  # GET /students
  # GET /students.json
  def index
    @students = Student.all
  end

  # GET /students/1
  # GET /students/1.json
  def show
  end

  # GET /students/new
  def new
    @student = Student.new
  end

  # GET /students/1/edit
  def edit
  end

  # POST /students
  # POST /students.json
  def create
    @student = Student.new(student_params)

    respond_to do |format|
      if @student.save
        format.html { redirect_to @student, notice: 'Student was successfully created.' }
        format.json { render :show, status: :created, location: @student }
      else
        format.html { render :new }
        format.json { render json: @student.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /students/1
  # PATCH/PUT /students/1.json
  def update
    respond_to do |format|
      if @student.update(student_params)
        format.html { redirect_to @student, notice: 'Student was successfully updated.' }
        format.json { render :show, status: :ok, location: @student }
      else
        format.html { render :edit }
        format.json { render json: @student.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /students/1
  # DELETE /students/1.json
  def destroy
    @student.destroy
    respond_to do |format|
      format.html { redirect_to students_url, notice: 'Student was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_student
      @student = Student.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def student_params
      params.require(:student).permit(:name, :faculty_id)
    end
end

你需要覆盖这个方法,其他形式的ios9,点击手势识别将无法正常工作。

答案 5 :(得分:0)

为此case

迅速做出相同的决心
  //set up tap gesture
        let tapGestureRecognaizer = UITapGestureRecognizer(target: self, action: #selector(pickerTapp))
        tapGestureRecognaizer.delegate = self
        tapGestureRecognaizer.numberOfTapsRequired = 2
        self.pickerView.addGestureRecognizer(tapGestureRecognaizer)

@objc func pickerTapp(tapGesture: UITapGestureRecognizer) {
    if tapGesture.state == .ended {
        // looking for frame selection row
        let selectedItem = dataSource2[pickerView.selectedRow(inComponent: 1)]
        let rowHeight = self.pickerView.rowSize(forComponent: 1).height
        let rowWidth = self.pickerView.rowSize(forComponent: 1).width
        let selectRowFrame = CGRect(x: CGFloat(Int(self.pickerView.frame.width) / self.pickerView.numberOfComponents), y: (self.pickerView.frame.height - rowHeight) / 2, width: rowWidth, height: rowHeight)

        let userTappedOnSelectedRow = selectRowFrame.contains(tapGesture.location(in: self.pickerView))
        // if tap to selection row ....
        if userTappedOnSelectedRow {
            if selectedArray.contains(selectedItem) {
                var index = 0
                for item in selectedArray {
                    if item == selectedItem {
                        selectedArray.remove(at: index)
                    } else {
                        index += 1
                    }
                }
            } else {
                selectedArray.append(selectedItem)
            }
        }

        //reload Data
        self.pickerView.dataSource = self
        self.pickerView.selectRow(selectedRow, inComponent: 1, animated: false)
        self.pickerView(self.pickerView, didSelectRow: selectedRow, inComponent: 1)
    }

全面解决:https://github.com/akonoplev/Features/blob/master/MultiSelectUIPickerView/MultiSelectUIPickerView/ViewController.swift

答案 6 :(得分:0)

这是我对这个问题的看法:https://github.com/aselivanov/UIMultiPicker。我想要与移动Safari用于处理<select multiple>的选择器完全相同。它看起来与UIPickerView非常相似(我敢打赌他们共享相同的代码)。