Как инициализировать класс со свойством, которое ограничено универсальным типом и протоколом в Swift

#ios #swift #class #generics #protocols

#iOS #swift #класс #общие #протоколы

Вопрос:

Я инициализирую свой Glide класс с помощью a UIViewController , который соответствует протоколу Glideable , например:

 class Glide : NSObject, UIGestureRecognizerDelegate, UIScrollViewDelegate {
    private var card: (Glideable amp; UIViewController)?
    init(parentViewController: UIViewController, configuration: GlideConfiguration, card: Glideable amp; UIViewController) {}
}
 

Поэтому, когда вы создаете экземпляр этого класса, вы передаете a UIViewController , который также соответствует Glideable протоколу. Теперь я хочу немного изменить это, чтобы, если у вас есть UIViewController завернутый внутри UINavigationController такой:

 let navCardView = UINavigationController(rootViewController: CardViewController())
 

затем я инициализирую свой Glide класс, navCardView который больше не является просто a UIViewController , а a UINavigationController , но я также хочу убедиться, что CardViewController он соответствует Glideable протоколу.

Любая помощь в реализации такой архитектуры с использованием Generics будет полезна. Кажется, я не могу понять, как я могу сделать общий тип ограниченным протоколом.

Любая идея будет оценена по достоинству. Спасибо.

Комментарии:

1. Неясно, что здесь означает «инициализировать мой класс Glide с помощью navCardView». navCardView не соответствует Glideable, поэтому его нельзя назначить card (и любой код, который работал card , не будет применяться). Вы имеете в виду, что хотите передать navCardView в Glide.init и заставить Glide.init извлечь первый ViewController из его стека, сохранить его как card , а затем игнорировать остальную часть navCardView? Или вы имеете в виду, что хотите согласовать UINavigationController с Glideable? Или вы имеете в виду что-то другое? (Если последнее, важно, как выглядит протокол.)

2. Вы не можете изменить то, что ожидает существующий init in UINavigationController . Вы могли бы обернуть UINavigationController создание экземпляра внутри функции, которая только принимает Glideable , или вы могли бы создать подкласс UINavigationController только для работы Glideable … Но это действительно зависит от того, как вы его используете. Итак, вам нужно рассказать немного больше о том, какова ваша конечная цель.

3. Кстати, если Glideable всегда используется совместно с UIViewController, лучший способ описать это на уровне протокола protocol Glideable: UIViewController

Ответ №1:

Я не уверен, что именно вы пытаетесь сделать, но в целом вы должны использовать enum для представления разнородных типов в swift. Вот как вы можете использовать перечисление для обработки двух случаев: контроллер навигации и отсутствие контроллера навигации, а также сделать класс универсальным для определенного типа контроллера представления:

 import SwiftUI
import PlaygroundSupport
import Combine

protocol Glideable { }

typealias GlideableViewController = Glideable amp; UIViewController

final class Glide<SomeGlidableViewController: GlideableViewController>: NSObject, Glideable {
  private enum Wrapped {
    case viewController(SomeGlidableViewController), navigationContoller(UINavigationController, SomeGlidableViewController)
    var glideViewController: SomeGlidableViewController {
      switch self {
      case let .viewController(viewController): return viewController
      case let .navigationContoller(_, viewController): return viewController
      }
    }
    var navigationController: UINavigationController? {
      guard case let .navigationContoller(navigationContoller, _) = self else { return nil }
      return navigationContoller
    }
  }
  private let wrappedViewController: Wrapped
  init(glideableViewController: SomeGlidableViewController) {
    wrappedViewController = .viewController(glideableViewController)
    super.init()
    commonInit()
  }
  init?(navigationController: UINavigationController) {
    guard let glideableViewController = navigationController.topViewController as? SomeGlidableViewController else {
      return nil
    }
    wrappedViewController = .navigationContoller(navigationController, glideableViewController)
    super.init()
    commonInit()
  }
  private func commonInit() {
    print("This class wraps a VC of type: (SomeGlidableViewController.self)")
    print("The wrapped VC is (wrappedViewController.glideViewController)")
    if let navigationController = wrappedViewController.navigationController {
      print("It is wrapped in a navigation controller")
    } else {
      print("It is not wrapped in a navigation controller")
    }
  }
}
 

Комментарии:

1. Большое спасибо! Код помог мне прояснить некоторые из моих концепций относительно такой ситуации, и теперь я могу справиться с обоими случаями. Большое спасибо! 🙂