web-dev-qa-db-fra.com

Une vue d'accessoire standard peut-elle occuper une position différente dans une UITableViewCell?

Je veux que mon accessoire soit dans un endroit légèrement différent de la normale. C'est possible? Ce code n'a aucun effet:

cell.accessoryType =  UITableViewCellAccessoryDisclosureIndicator;
cell.accessoryView.frame = CGRectMake(5.0, 5.0, 5.0, 5.0);
28
cannyboy

Non, vous ne pouvez pas vous déplacer là où se trouve la vue accessoire. Vous pouvez également ajouter une sous-vue, comme suit:

[cell.contentView addSubview:aView];

De même, en définissant la propriété accessoryView sur quelque chose, la valeur accessoryType est ignorée.

26
rickharrison

Il y a un moyen de déplacer accessoireView par défaut, mais c'est assez hacky. Il est donc possible qu’il cesse de fonctionner un jour à l’arrivée d’un nouveau SDK. 

Utilisez-le à vos risques et périls (cet extrait de code déplace la variable accessoryView de 8 pixels vers la gauche. Insérez-la dans la méthode -(void)layoutSubviews de la sous-classe UITableViewCell souhaitée):

if (self.accessoryView) {
    r = self.accessoryView.frame;
    r.Origin.x -= 8;
    self.accessoryView.frame = r;
} else {
    UIView *defaultAccessoryView = nil;
    for (UIView *subview in self.subviews) {
        if (subview != self.textLabel && 
            subview != self.detailTextLabel && 
            subview != self.backgroundView && 
            subview != self.contentView &&
            subview != self.selectedBackgroundView &&
            subview != self.imageView) {
            defaultAccessoryView = subview;
            break;
        }
    }

    r = defaultAccessoryView.frame;
    r.Origin.x -= 8;
    defaultAccessoryView.frame = r;
}
20
Alexey

J'ai pu modifier le cadre de la vue des accessoires en le faisant simplement dans ma sous-classe de cellules personnalisée.

CGRect adjustedFrame = self.accessoryView.frame;
adjustedFrame.Origin.x += 10.0f;
self.accessoryView.frame = adjustedFrame;
14
James Kuang

Une autre façon de procéder consiste à incorporer votre vue d'accessoire personnalisée dans une autre vue, définie comme vue d'accessoire de la cellule et à contrôler le remplissage à l'aide du cadre.

Voici un exemple avec une vue d'image en tant que vue accessoire personnalisée: 

// Use insets to define the padding on each side within the wrapper view
UIEdgeInsets insets = UIEdgeInsetsMake(24, 0, 0, 0);

// Create custom accessory view, in this case an image view
UIImage *customImage = [UIImage imageNamed:@"customImage.png"];
UIImageView *accessoryView = [[UIImageView alloc] initWithImage:customImage];

// Create wrapper view with size that takes the insets into account 
UIView *accessoryWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, customImage.size.width+insets.left+insets.right, customImage.size.height+insets.top+insets.bottom)];

// Add custom accessory view into wrapper view
[accessoryWrapperView addSubview:accessoryView];

// Use inset's left and top values to position the custom accessory view inside the wrapper view
accessoryView.frame = CGRectMake(insets.left, insets.top, customImage.size.width, customImage.size.height);

// Set accessory view of cell (in this case this code is called from within the cell)
self.accessoryView = accessoryWrapperView;
3
Ole

Le moyen simple de définir une position personnalisée pour le accessoriesView qui est est conservé dans l'état de la cellule consiste à mettre en page l'accessoireView dans layoutSubViews:

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.accessoryView.center = CGPointMake($yourX, $yourY);
}
2
Kappe

Suite à la solution donnée par Ana, j'ai essayé de mieux détecter la vue accessoire, je regarde du côté droit de la cellule.

Créez une classe personnalisée qui étend UITableViewCell et ajoutez cette méthode:

- (void)layoutSubviews {
    [super layoutSubviews];

    if (self.accessoryType != UITableViewCellAccessoryNone) {
        float estimatedAccesoryX = MAX(self.textLabel.frame.Origin.x + self.textLabel.frame.size.width, self.detailTextLabel.frame.Origin.x + self.detailTextLabel.frame.size.width);

        for (UIView *subview in self.subviews) {
            if (subview != self.textLabel &&
                subview != self.detailTextLabel &&
                subview != self.backgroundView &&
                subview != self.contentView &&
                subview != self.selectedBackgroundView &&
                subview != self.imageView &&
                subview.frame.Origin.x > estimatedAccesoryX) {

                // This subview should be the accessory view, change its frame
                frame = subview.frame;
                frame.Origin.x -= 10;
                subview.frame = frame;
                break;
            }
        }
    } 
}
2
Tomasz

Les réponses ci-dessus ne fonctionnaient pas pour moi sous iOS 6.1. J'ai donc essayé d'utiliser UIEdgeInsets, car DetailDisclosure est un UIButton. Et cela fonctionne bien maintenant. Voici la source:

if (cell.accessoryType == UITableViewCellAccessoryDetailDisclosureButton) {
    UIView* defaultAccessoryView = [cell.subviews lastObject];
    if ([defaultAccessoryView isKindOfClass:[UIButton class]]){
        UIButton *bt = (UIButton*)defaultAccessoryView;            
        bt.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 10);
    }
}
2
larshaeuser

Je travaillais avec iOS 5 et la solution proposée par Alexey ne fonctionnait pas entièrement. J'ai découvert que, quand un type d'accessoire est défini sur une table, la valeur de l'accessoire est nulle et le premier "si" ne fonctionne pas. J'ai juste changé un peu le code:

if (self.accessoryType != UITableViewCellAccessoryNone) {
    UIView* defaultAccessoryView = nil;

    for (UIView* subview in self.subviews) {
        if (subview != self.textLabel && 
            subview != self.detailTextLabel && 
            subview != self.backgroundView && 
            subview != self.contentView &&
            subview != self.selectedBackgroundView &&
            subview != self.imageView &&
            subview != self.explanationButton && // Own button
            subview.frame.Origin.x > 0 // Assumption: the checkmark will always have an x position over 0. 
            ) {
            defaultAccessoryView = subview;
            break;
        }
    }
    r = defaultAccessoryView.frame;
    r.Origin.x -= 8;
    defaultAccessoryView.frame = r;

}

et cette solution fonctionne pour moi. Comme Alexey l'a dit, je ne sais pas ce qui va se passer avec les futures versions, mais au moins dans iOS 4, cela fonctionne.

1
Ana

Peut-être que cela vous suffira:

UIImageView* accessoryImageView = [[UIImageView alloc] initWithFrame:
        CGRectMake(0, 0, accessoryImage.size.width + MARGIN_RIGHT, accessoryImage.size.height)];
accessoryImageView.contentMode = UIViewContentModeLeft;
accessoryImageView.image = accessoryImage;

self.accessoryView = accessoryImageView;

De cette façon, j'ai ajouté un rembourrage à droite, le bouton accessoire a donc l'air décalé vers la gauche. Il a une zone plus large qui répond aux contacts, c'est le seul effet secondaire.

1
ancajic

améliorations sur d'autres réponses

Pour James Kuang, Kappe, accessoryView est nil pour la vue des accessoires par défaut.

Pour Matjan, subviews.lastObject est facilement une vue erronée, comme un UITableViewCellSeparatorView.

Pour Alexey, Ana, Tomasz, énumérant les sous-vues jusqu'à ce que nous trouvions un inconnu fonctionne pour le moment. Mais c’est laborieux et pourrait être facilement supprimé dans les versions futures si, disons, Apple ajoute un backgroundAccessoryView.

Pour larshaeuser, il est judicieux d'énumérer les sous-vues jusqu'à ce que nous trouvions un bouton UIB, mais contentEdgeInsets ne modifie pas de manière adéquate la vue accessoire.

solution pour Swift 3.x et 4.0

Nous allons énumérer et rechercher le dernier UIButton.

class AccessoryTableViewCell: UITableViewCell {
    override func layoutSubviews() {
        super.layoutSubviews()
        if let lastButton = subviews.reversed().lazy.flatMap({ $0 as? UIButton }).first {
            // This subview should be the accessory view, change its Origin
            lastButton.frame.Origin.x = bounds.size.width - lastButton.frame.size.width - 5
        }
    }
}

pour Swift 4.1 et plus récent

class AccessoryTableViewCell: UITableViewCell {
    override func layoutSubviews() {
        super.layoutSubviews()
        // https://stackoverflow.com/a/45625959/1033581
        if let lastButton = subviews.reversed().lazy.compactMap({ $0 as? UIButton }).first {
            // This subview should be the accessory view, change its Origin
            lastButton.frame.Origin.x = bounds.size.width - lastButton.frame.size.width - 5
        }
    }
}
0
Cœur