Comment faire un UIPickerView circulaire (iPhone / iPad)?
j'ai beaucoup réfléchi à cette question, mais je ne sais toujours pas si c'est possible ou non. Essentiellement ce que je veux faire est de créer un UIPickerView
qui est continu dans le sens où vous pouvez le tourner pour toujours et vous n'atteindrez jamais la fin de celui-ci (puisque la dernière valeur est suivie par la première valeur).
j'ai eu un regard autour en ligne et il semble y avoir une grande variété de hacks pour atteindre l'effet désiré. Cependant beaucoup de ces solutions semblent augmenter le nombre de lignes dans le UIPickerView
pour faire croire à l'utilisateur que le UIPickerView
est continu (cependant, en réalité, s'ils continuaient à défiler, ils finiraient par atteindre la fin).
ce que je cherche, c'est une façon de créer une vue D'Uipick qui est vraiment infinie dans le sens où vous n'atteindrez jamais la fin si vous continuez à défiler pendant des jours, des semaines, des mois ou des années. Ça ne me dérange pas trop si la solution est un piratage puisque je comprends que Apple n'a pas a fourni un moyen d'obtenir cet effet à ce jour.
S'il vous plaît quelqu'un peut-il me conseiller sur une façon de faire ceci (ou me pointer dans la bonne direction au moins)?
10 réponses
je pense vraiment que le seul hack que vous pouvez faire avec UIPickerView natif est décrit ici:
Comment faire pour qu'un composant UIPickerView s'enroule?
l'autre façon de faire vraiment picker bouclé est de l'appliquer par vous-même.
j'ai vu des pickers qui ont été implémentés avec cocos2d, qui est basé sur OpenGL. Je pense que vous pouvez essayer de le faire en utilisant UIKit si vous en avez vraiment besoin.
ou tout simplement oublier et faire un picker avec des lignes NSIntegerMax avec le contenu répétable. Je pense que personne ne le fera tourner jusqu'à la fin.
c'est possible. Voici comment vous pouvez le faire. Première installation d'une minuterie. Supposons que int maxNumber
est une variable d'instance définie à une certaine valeur arbitraire.
- ( void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.timer invalidate];
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(timerFireMethod:) userInfo:nil repeats:YES];
}
- (void) viewWillDisappear:(BOOL)animated{
[self.timer invalidate];
[super viewWillDisappear:animated];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.infinitePickerView selectRow:(int)maxNumber*5 inComponent:0 animated:YES];
}
dans la méthode de la minuterie, vérifiez si l'une des vues 'edge' de votre vue uipickerview est visible.
- (void)timerFireMethod:(NSTimer*)theTimer{
int rowSelected = [self.infinitePickerView selectedRowInComponent:0];
for (int i=0; i<= 20; i++) {
UIView * viewBelow = [self.infinitePickerView viewForRow:i forComponent:0];
UIView * viewAbove = [self.infinitePickerView viewForRow:maxNumber*10-20+i forComponent:0];
if(viewBelow!=nil || viewAbove!=nil){
int middlePosition = maxNumber * 5 + (rowSelected % maxNumber);
[self.infinitePickerView selectRow:middlePosition inComponent:0 animated:NO];
break;
}
}
}
notez que cela fonctionne parce que [self.infinitePickerView viewForRow:i forComponent:0];
retourne UIView seulement si elle est visible.
bien sûr, votre UIPickerViewDelegate
doit être quelque chose comme
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return maxNumber * 10; //returning a large number
}
//You cannot use this method
/*
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{}
You have to use the method below in lieu of it
*/
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view{
UILabel * label;
if (!view) {
label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.infinitePickerView.bounds.size.width, 30)];
}else {
label = (UILabel *) view;
}
[label setText:[NSString stringWithFormat:@" %d", row % maxNumber]];
return label;
}
espérons que cela fonctionne!! :) Bonne chance!
il n'y a pas de cueilleur autochtone qui enveloppe (pas de iOS7.1 en tout cas.) Vous pouvez faire semblant par quelque chose du genre:
NSArray *picks = @[ @"zero", @"one", @"two", @"three" ]; // 4 entries
// ...
- (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component
{
return (3 * [picks count]); // we're very tricky, displaying only
// ...the SECOND set of entries, to
// ... give the impression of wrap-around
}
// ...
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
// All "3" sets of entries have the same values;
// this is what gives the appearance of wrap-around.
int titleRow = row % [picks count];
return [picks objectAtIndex: titleRow];
}
// ...
- (void)pickerView:(UIPickerView *)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
// If the wheel turns to outside our 2nd-set range,
// ...set it to the matching item in the 2nd set.
// In this way, we always display what looks like a wrap-around.
int count = [picks count];
if ((row < count)
|| (row > (count * 2) )
{
[pickerView selectRow: (row % count) inComponent: component animated: NO];
}
}
Vous aurez à modifier et adapter à vos besoins, bien sûr, mais c'est le sens de base.
bonne chance!
Hm.. J'ai regardé autour de moi, et il y a quelques liens vers quelques possibilités, le plus remarquable étant celui-ci: le Pickerview abusif , l'idée de base étant que vous peuplez le pickerview avec 3 ensembles de vos données et démarrer et le jeu de Centre. Chaque fois que l'utilisateur défile vers le centre de l'un ou l'autre des ensembles supérieurs ou inférieurs, vous réglez la valeur de la rangée de nouveau au centre de l'ensemble Central! Il semble plus sincère que de faire une très longue liste permettra de créer l'illusion de l'infini. Cette solution peut être la plus efficace et simple pour résoudre le problème de picerview infinie! Espérons que cela a aidé!
il me semble que les solutions nécessitant de grandes quantités de données sont gaspillées. La solution suivante utilise smarts au lieu de la mémoire.
La seule les lignes qui doivent être remplis sont les lignes visibles dans l'affichage et la ligne qui précède immédiatement la première, et la ligne qui suit immédiatement le dernier. Lorsque l'utilisateur fait défiler un composant, pickerView:titleForRow:forComponent: est appelé pour chaque ligne qui défile.
ainsi la solution est de mettre à jour les données dans pickerView:titreforrow:forComponent: et puis appeler la méthode appropriée pour recharger les données de sorte qu'elle soit là quand le pickerview défile une autre touche.
je sais que c'est une vieille question, mais je viens de la trouver comme je l'implémente dans mon propre projet.
il suffit d'utiliser la méthode de Morion d'un grand nombre d'éléments avec le contenu de répétition, puis chaque fois qu'il arrête de défiler, réinitialiser la ligne. Le seul moyen pour que quelqu'un comprenne que vous avez triché, c'est s'ils font défiler sans interruption jusqu'à ce qu'il s'arrête, n'appelant jamais la fonction didSelectRow. Il faudra un certain dévouement!
peu importe comment vous le faites, il me semble qu'il va se sentir un peu hacky, et ce doit être la méthode la plus simple hack...
j'ai fait un tableView cyclique basé sur UIScrollView
. Et sur la base de ce tableView, je ré-implémente UIPickerView
. Vous pourriez être intéressé par ce. Et cette vue de picker a toutes les fonctionnalités que UIPickerView
, mais donne aussi de nombreuses nouvelles fonctionnalités, et sur mesure cette vue de picker est beaucoup plus facile.
https://github.com/danleechina/DLPickerView
et sachez que ce défilement cyclique de DLPickerView est vraiment le défilement de manière cyclique. Toute la magie est arrivée à cause d'une autre classe DLTableView
.
j'ai utilisé la sélection pickerView pour en créer une circulaire:
import UIKit
class ViewController:
UIViewController,UIPickerViewDelegate,UIPickerViewDataSource
{
@IBOutlet weak var picker: UIPickerView!
@IBOutlet weak var myLabel: UILabel!
let numbers = [0,1,2,3,4,5,6,7,8,9]
override func viewDidLoad()
{
super.viewDidLoad()
// A.Select Second Row
picker.selectRow(1, inComponent: 0, animated: false)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int
{
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
{
//B. Add 2 rows to your array count
return numbers.count + 2
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
{
var rowTitle = ""
switch row
{
case 0:
// C. Set first row title to last component of the array.
// This row is not visible to the user as it is hidden by the
//selection in ViewDidLoad
rowTitle = "\(numbers.last!)"
case numbers.count + 1:
//D. Set last row title to first array component
rowTitle = "\(numbers.first!)"
default:
// E. Remember [row - 1] to avoid errors
rowTitle = "\(numbers[row - 1])"
}
return rowTitle
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
{
var theText = ""
switch row
{
case 0:
//F. Select Row at array count to load the last row
pickerView.selectRow(numbers.count , inComponent: 0, animated: false)
theText = = "\(numbers.last!)"
case numbers.count + 1:
//G. This Selection will set the picker to initial state
pickerView.selectRow(1, inComponent: 0, animated: false)
theText = "\(numbers.first!)"
default:
theText = "\(numbers[row - 1])"
}
myLabel.text = theText
}
}
une solution très simple qui nous fera penser qu'il n'y a pas de fin de la vue de picker, en augmentant l'échelle de la matrice et faire module choisir l'élément correspondant de la matrice
override func viewDidLoad(){
super.viewDidLoad()
self.yourArray.selectRow((yourArray.count*100)/2, inComponent: 0, animated: false)
}
func pickerView(_ pickerView: UIPickerView, numberOfRowaInComponent component: Int) -> Int{
return yourArray.count*100
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int)->String{
return yourArray[row % yourArray.count]
}