web-dev-qa-db-fra.com

le cadre iOS change une propriété (par exemple, la largeur)

Cette question a été posée à l'origine pour le langage de programmation objective-c. Au moment de la rédaction de cet article, Swift n'existait même pas encore.

Question

Est-il possible de ne changer que la propriété one d'un CGRect?

Par exemple:

self.frame.size.width = 50;

au lieu de

self.frame = CGRectMake(self.frame.Origin.x, 
                        self.frame.Origin.y, 
                        self.frame.size.width, 
                        50);

bien sûr, je comprends que self.frame.size.width est en lecture seule, alors je me demande comment faire cela?

ANALOGIE CSS procède à vos risques et périls

pour ceux d'entre vous qui connaissent CSS, l'idée est très similaire à celle d'utiliser:

margin-left: 2px;

au lieu d'avoir à changer toute la valeur:

margin: 5px 5px 5px 2px;
36
Jacksonkr

Pour répondre à votre question initiale: oui, il est possible de changer un seul membre d'une structure CGRect. Ce code ne génère aucune erreur:

myRect.size.width = 50;

Ce qui est pas possible, cependant, est de changer un seul membre d'un CGRect qui est lui-même une propriété d'un autre objet. Dans ce cas très courant, vous devriez utiliser une variable locale temporaire:

CGRect frameRect = self.frame;
frameRect.size.width = 50;
self.frame = frameRect;

La raison en est que l'utilisation de la propriété accessor self.frame = ... est équivalente à [self setFrame:...] et que cet accesseur attend toujours un CGRect entier. La combinaison de l'accès struct de style C avec la notation de points de la propriété Objective-C ne fonctionne pas bien dans ce cas.

92
Ole Begemann

J'ai bien aimé la réponse d'Ahmed Khalaf , mais je me suis dit que vous pourriez aussi bien écrire quelques fonctions en C ... le principal avantage est qu'il sera plus facile de localiser les erreurs si vous ' re utilisant le mauvais type.

Ceci dit, j’ai écrit un fichier .h avec ces déclarations de fonction:

CGRect CGRectSetWidth(CGRect rect, CGFloat width);
CGRect CGRectSetHeight(CGRect rect, CGFloat height);
CGRect CGRectSetSize(CGRect rect, CGSize size);
CGRect CGRectSetX(CGRect rect, CGFloat x);
CGRect CGRectSetY(CGRect rect, CGFloat y);
CGRect CGRectSetOrigin(CGRect rect, CGPoint Origin);

Et un fichier .m correspondant avec ces implémentations de fonctions:

CGRect CGRectSetWidth(CGRect rect, CGFloat width) {
    return CGRectMake(rect.Origin.x, rect.Origin.y, width, rect.size.height);
}

CGRect CGRectSetHeight(CGRect rect, CGFloat height) {
    return CGRectMake(rect.Origin.x, rect.Origin.y, rect.size.width, height);
}

CGRect CGRectSetSize(CGRect rect, CGSize size) {
    return CGRectMake(rect.Origin.x, rect.Origin.y, size.width, size.height);
}

CGRect CGRectSetX(CGRect rect, CGFloat x) {
    return CGRectMake(x, rect.Origin.y, rect.size.width, rect.size.height);
}

CGRect CGRectSetY(CGRect rect, CGFloat y) {
    return CGRectMake(rect.Origin.x, y, rect.size.width, rect.size.height);
}

CGRect CGRectSetOrigin(CGRect rect, CGPoint Origin) {
    return CGRectMake(Origin.x, Origin.y, rect.size.width, rect.size.height);
}

Alors, maintenant, pour faire ce que vous voulez, vous pouvez simplement faire:

self.frame = CGRectSetWidth(self.frame, 50);

Get even Fancier (mise à jour effectuée un an plus tard)

Cela contient cependant un self.frame redondant. Pour résoudre ce problème, vous pouvez ajouter une category sur UIView avec des méthodes ressemblant à ceci:

- (void) setFrameWidth:(CGFloat)width {
    self.frame = CGRectSetWidth(self.frame, width); // You could also use a full CGRectMake() function here, if you'd rather.
}

Et maintenant, vous pouvez simplement taper:

[self setFrameWidth:50];

Ou encore mieux:

self.frameWidth = 50;

Et juste pour que vous puissiez faire quelque chose comme ça:

self.frameWidth = otherView.frameWidth; // as opposed to self.frameWidth = otherView.frame.size.width;

Vous aurez aussi besoin de l'avoir dans votre catégorie:

- (CGFloat) frameWidth {
    return self.frame.size.width;
}

Prendre plaisir.

28
ArtOfWarfare

Basé sur la solution d'ArtOfWarfare (ce qui est vraiment génial), j'ai construit la catégorie UIView sans fonctions C.

Exemples d'utilisation:

[self setFrameWidth:50];
self.frameWidth = 50;
self.frameWidth += 50;
self.frameWidth = otherView.frameWidth; // as opposed to self.frameWidth = otherView.frame.size.width;

Fichier d'en-tête UIView+easy_frame.h:

@interface UIView (easy_frame)

- (void) setFrameWidth:(CGFloat)width;
- (void) setFrameHeight:(CGFloat)height;
- (void) setFrameX:(CGFloat)x;
- (void) setFrameY:(CGFloat)y;

- (CGFloat) frameWidth;
- (CGFloat) frameHeight;
- (CGFloat) frameX;
- (CGFloat) frameY;

Fichier d'implémentation UIView+easy_frame.m:

#import "UIView+easy_frame.h"
@implementation UIView (easy_frame)

# pragma mark - Setters

- (void) setFrameWidth:(CGFloat)width
{
  self.frame = CGRectMake(self.frame.Origin.x,
                          self.frame.Origin.y,
                          width,
                          self.frame.size.height);
}

- (void) setFrameHeight:(CGFloat)height
{
  self.frame = CGRectMake(self.frame.Origin.x,
                          self.frame.Origin.y,
                          self.frame.size.width,
                          height);
}

- (void) setFrameX:(CGFloat)x
{
  self.frame = CGRectMake(x,
                          self.frame.Origin.y,
                          self.frame.size.width,
                          self.frame.size.height);
}

- (void) setFrameY:(CGFloat)y
{
  self.frame = CGRectMake(self.frame.Origin.x,
                          y,
                          self.frame.size.width,
                          self.frame.size.height);
}

# pragma mark - Getters

- (CGFloat) frameWidth
{
  return self.frame.size.width;
}

- (CGFloat) frameHeight
{
  return self.frame.size.height;
}

- (CGFloat) frameX
{
  return self.frame.Origin.x;
}

- (CGFloat) frameY
{
  return self.frame.Origin.y;
}
6
n0_quarter

Si vous vous trouvez dans l'obligation de faire ce type de modification de composant individuel, il peut être intéressant d'avoir des macros comme celles-ci, accessibles quelque part par tout votre code:

#define CGRectSetWidth(rect, w)    CGRectMake(rect.Origin.x, rect.Origin.y, w, rect.size.height)
#define ViewSetWidth(view, w)   view.frame = CGRectSetWidth(view.frame, w)

De cette façon, chaque fois que vous devez modifier la largeur seule, vous écrivez simplement

ViewSetWidth(self, 50);

au lieu de

self.frame = CGRectMake(self.frame.Origin.x, self.frame.Origin.y, self.frame.size.width, 50);
4
pxlshpr

Dans Swift, vous pouvez étendre la classe UIView pour l'application, .__, de telle sorte que vous puissiez, par exemple, déplacer la vue d'en-tête du tableau de 10 points comme ceci:

    tableView.tableHeaderView!.frameAdj(0, -20, 0, 0)

(Bien sûr, si vous utilisez AutoLayout, cela pourrait ne rien faire ... :))

En étendant UIView (affectant chaque UIView et sa sous-classe dans votre application)

extension UIView {
    func frameAdj(x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) {
            self.frame = CGRectMake(
                self.frame.Origin.x + x,
                self.frame.Origin.y + y,
                self.frame.size.width  + width,
                self.frame.size.height + height)
    }
}
2
clearlight

Sur la base de la réponse n0_quarter, voici une extension UIView écrite en Swift:

/**
*  Convenience category for manipulating UIView frames
*/
extension UIView {

    //MARK: - Getters
    func frameX() -> CGFloat {
        return frame.Origin.x
    }

    func frameY() -> CGFloat {
        return frame.Origin.y
    }

    func frameWidth() -> CGFloat {
        return frame.size.width
    }

    func frameHeight() -> CGFloat {
        return frame.size.height
    }

    //MARK: - Setters
    func setFrameX(x: CGFloat) {
        frame = CGRect(x: x, y: frame.Origin.y, width: frame.size.width, height: frame.size.height)
    }

    func setFrameY(y: CGFloat) {
        frame = CGRect(x: frame.Origin.x, y: y, width: frame.size.width, height: frame.size.height)
    }

    func setFrameWidth(width: CGFloat) {
        frame = CGRect(x: frame.Origin.x, y: frame.Origin.y, width: width, height: frame.size.height)
    }

    func setFrameHeight(height: CGFloat) {
        frame = CGRect(x: frame.Origin.x, y: frame.Origin.y, width: frame.size.width, height: height)
    }
}
1
Robert Wagstaff

appeler fréquemment la méthode setFrame n’est pas si bon, comme: setFrameX(x) setFrameY(y) setFrameWidth(width) ...

à la place, dans Swift, nous pouvons utiliser les paramètres par défaut pour définir plusieurs paramètres au cours d’un seul appel:

func setFrame(x x: CGFloat = CGFloat.NaN, y: CGFloat = CGFloat.NaN, width: CGFloat = CGFloat.NaN, height: CGFloat = CGFloat.NaN) {
    let newX = x.isNaN ? self.frame.Origin.x : x
    let newY = y.isNaN ? self.frame.Origin.y : y
    let newWidth = width.isNaN ? self.bounds.size.width : width
    let newHeight = height.isNaN ? self.bounds.size.height : height

    self.frame = CGRect(x: newX, y: newY, width: newWidth, height: newHeight)
}

puis mettre à jour les paramètres du cadre seulement besoin de:

setFrame(x: x, width: min(maxX - x, titleLabel.bounds.size.width))
setFrame(width: titleDescriptionLabel.bounds.size.width + 5, height: titleDescriptionLabel.bounds.size.height + 6)
0
Bigyelow

J'espère que je ne suis pas trop en retard pour la fête, voici ma solution à ObjC.

Pour moi, je préfère m'en tenir à une seule fonction plutôt qu'à plusieurs, et j'aime bien l'approche CSS présentée par l'affiche. Ci-dessous, la fonction que j’ajoute à ma catégorie UIView.

- (void)resetFrame:(CGRect)frame {
CGRect selfFrame = self.frame;
selfFrame.Origin = CGPointMake(frame.Origin.x>0 ? frame.Origin.x : selfFrame.Origin.x, frame.Origin.y>0 ? frame.Origin.y : selfFrame.Origin.y);
selfFrame.size = CGSizeMake(frame.size.width>0 ? frame.size.width : selfFrame.size.width, frame.size.height>0 ? frame.size.height : selfFrame.size.height);
[self setFrame:selfFrame]; }

Dans l'exemple, il examine chaque valeur dans le CGRect fourni et si la valeur est supérieure à 0, cela signifie que nous souhaitons remplacer cette valeur. Et par conséquent, vous pouvez faire CGRect (0,0,100,0) et cela supposera que nous voulons remplacer uniquement la largeur.

0
wahkiz