2009-03-14 19 views
26

J'ai un UIViewController personnalisé et un UIView personnalisé. Je voudrais substituer la propriété viewcontroller.view pour retourner MyCustomUIView.Comment remplacer la propriété "view" dans UIViewController?

En ce moment, j'ai:

@interface MyViewController : UIViewController {  
    IBOutlet MyView* view; 
} 

@property (nonatomic, retain) IBOutlet MyView* view; 

Cette compile, mais je reçois un avertissement: la propriété type « vue » ne correspond pas super classe de type propriété « UIViewController ».

Comment atténuer cet avertissement?

+2

Je pense que vous devriez plutôt utiliser '@ dynamic'. S'il vous plaît lire [cette question] (http://stackoverflow.com/questions/1160498/synthesize-vs-dynamic-what-are-the-differences), les réponses m'ont vraiment été utiles :) – Ondrej

+3

Il y a un très bel article appelé [Remplacement de la propriété View de UIViewController, Fait à droite] (http://travisjeffery.com/b/2012/12/overriding-uiviewcontrollers-view-property-done-right/). – lambdas

Répondre

18

La réponse courte est que ce n'est pas le cas. La raison en est que les propriétés ne sont en réalité que des méthodes, et si vous essayez de changer le type de retour, vous obtenez ceci:

  • (UIView *) voir;
  • (vue MyView *);

Objectif-C ne pas permettre la covariance de type de retour.

Ce que vous pouvez faire est d'ajouter une nouvelle propriété "myView", et le faire simplement classer la propriété "view". Cela permettra d'atténuer les typecasts dans tout votre code. Affectez juste votre sous-classe d'affichage à la propriété d'affichage, et tout devrait fonctionner correctement.

+0

+1 pour donner exactement les pièces manquantes de ce que j'ai répondu :) –

4

La propriété/méthode d'affichage de UIViewController renvoie automatiquement votre vue. Je pense que vous aurez juste à jeter le résultat à MyView (ou tout simplement utiliser le type id):

MyView *myView = (MyView*)controller.view; 
id myView = controller.view; 

Je pense que le code que vous avez posté ci-dessus vous causer des ennuis. Vous ne voulez probablement pas créer vous-même un membre de vue (car alors le contrôleur stockera 2 vues, et n'utilisera peut-être pas la bonne en interne) ou surcharger la propriété view (car UIViewController a une gestion spéciale pour cela). (Voir this question pour plus d'informations sur ce dernier.)

4

Désolé, mais votre réponse est incorrecte.

L'implémentation correcte de ceci serait d'ajouter un @property au fichier d'en-tête avec votre type de retour. Ensuite, au lieu de @synthesize vous ajoutez le getter et setter manuellement et retourner un type de fonte de [super vue]

par exemple ma classe est un PlayerViewController

PlayerViewController.h

@property (strong, nonatomic) IBOutlet PlayerView *view; 

PlayerViewController. m

- (PlayerView *)view{ 
    return (PlayerView*)[super view]; 
} 
- (void)setView:(PlayerView *)view{ 
    [super setView:view]; 
} 

l'important est de placer la bonne classe dans la vue. La mise en place d'un UIView à l'emplacement d'un PlayerView fonctionnerait probablement dans l'Interface Builder, mais ne fonctionnerait pas correctement dans le code.

J'utilise actuellement cette implémentation.

+0

Je sais que cette question était ancienne. Mais j'ai trouvé cela et puis est venu avec ma propre réponse. J'espère que cela aidera les futurs utilisateurs. –

+0

Pensez-vous que la propriété devrait être forte? Je veux dire, 'UIView' a déjà une propriété forte' view' qui a la même valeur. Le modificateur readonly n'est-il pas plus approprié? – lambdas

+0

+1: Je pensais que c'était possible. Merci de le confirmer. @ lpaul7 - Non, 'readonly' n'a pas de sens parce que vous ne pouvez pas définir la propriété' view', d'où la signification de 'readonly'. Vous devez avoir cette propriété comme 'strong' pour remplacer la propriété' view' de la superclasse (qui a une déclaration de propriété de 'strong'). –

23

@ lpaul7 déjà posté un lien vers le blog de Travis Jeffery comme commentaire, mais il est tellement plus correct que toutes les autres réponses qu'il a vraiment besoin du code pour être une réponse:

ViewController.h:

@interface ViewController : UIViewController 

@property (strong, nonatomic) UIScrollView *view; 

@end 

ViewController.m:

@implementation ViewController 

@dynamic view; 

- (void)loadView { 
    self.view = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]]; 
} 

@end 

Si vous utilisez un xib, au lieu de surcharger loadView, changez le type de vue racine du contrôleur de vue et ajoutez IBOutlet à la propriété.

Voir Overriding UIViewController's View Property, Done Right pour plus de détails.

+0

En utilisant cette approche, je reçois l'avertissement de compilateur suivant: "type de propriété 'MyScrollView *' est incompatible avec le type 'UIView *' hérité de 'UIViewController'" – Koen

+0

@Koen Votre MyScrollView est-elle une sous-classe de UIView? Recevez-vous l'avertissement du compilateur si vous utilisez UIScrollView au lieu de MyScrollView? – JosephH

+0

MyScrollView est une sous-classe de 'UIScrollView'. J'ai fini par faire le scrollView juste un conteneur pour MyCustomView où je fais tout le dessin. Par opposition à faire tout le dessin de MyScrollView. – Koen

0

Si quelqu'un est sur Swift 2.0+, vous pouvez utiliser des extensions de protocole pour une mise en œuvre réutilisable:

// Classes conforming to this protocol... 
protocol CustomViewProvider { 

    typealias ViewType 
} 

// ... Will get customView accessor for free 
extension CustomViewProvider where Self: UIViewController, Self.ViewType: UIView { 

    var customView: Self.ViewType { 
     return view as! Self.ViewType 
    } 
} 


// Example: 
extension HomeViewController: CustomViewProvider { 
    typealias ViewType = HomeView 
} 

// Now, we can do something like 
let customView: HomeView = HomeViewController().customView