2014-11-16 6 views
8

Ich untergliedere UIScrollView, um einige Funktionen wie Doppeltippen zum Zoomen und eine Bildeigenschaft für Galeriezwecke hinzuzufügen. Aber um das Bild zu machen, muss meine Unterklasse ein eigener Delegat sein und das viewForZoomingInScrollView implementieren.In Swift, wie habe ich eine UIScrollView-Unterklasse, die einen internen und externen Delegaten hat?

Aber wenn jemand meine Scroll-View-Unterklasse verwendet, möchten sie vielleicht Delegiertenbenachrichtigungen erhalten, um scrollViewDidScroll zu sehen oder was Sie haben.

In Swift, wie bekomme ich beides?

+0

Wissen Sie, wie man dieses Problem zu lösen in ObjC und wollen Portierung auf Swift genannt? – e1985

+0

Wenn nicht, versuchen Sie dies mit der Lösung in dieser Antwort http://stackoverflow.com/a/9986842/637641 – e1985

+0

Ich kann nicht eine Swift-Implementierung herausfinden. –

Antwort

12

Hier ist eine Swift-Version dieses Muster:

Obwohl forwardInvocation: in Swift deaktiviert ist, können wir noch forwardingTargetForSelector:

class MyScrollView: UIScrollView { 

    class _DelegateProxy: NSObject, UIScrollViewDelegate { 
     weak var _userDelegate: UIScrollViewDelegate? 

     override func respondsToSelector(aSelector: Selector) -> Bool { 
      return super.respondsToSelector(aSelector) || _userDelegate?.respondsToSelector(aSelector) == true 
     } 

     override func forwardingTargetForSelector(aSelector: Selector) -> AnyObject? { 
      if _userDelegate?.respondsToSelector(aSelector) == true { 
       return _userDelegate 
      } 
      else { 
       return super.forwardingTargetForSelector(aSelector) 
      } 
     } 

     func viewForZoomingInScrollView(scrollView: MyScrollView) -> UIView? { 
      return scrollView.viewForZooming() 
     } 

     // Just a demo. You don't need this. 
     func scrollViewDidScroll(scrollView: MyScrollView) { 
      scrollView.didScroll() 
      _userDelegate?.scrollViewDidScroll?(scrollView) 
     } 
    } 

    private var _delegateProxy = _DelegateProxy() 

    required init(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     super.delegate = _delegateProxy 
    } 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
     super.delegate = _delegateProxy 
    } 

    override var delegate:UIScrollViewDelegate? { 
     get { 
      return _delegateProxy._userDelegate 
     } 
     set { 
      self._delegateProxy._userDelegate = newValue; 
      /* It seems, we don't need this anymore. 
      super.delegate = nil 
      super.delegate = _delegateProxy 
      */ 
     } 
    } 

    func viewForZooming() -> UIView? { 
     println("self viewForZooming") 
     return self.subviews.first as? UIView // whatever 
    } 

    func didScroll() { 
     println("self didScroll") 
    } 
} 
+0

Schöner Fang. Ich erinnere mich, das gleiche zu versuchen, aber einen seltsamen Fehler (erinnere mich nicht, welche) überschreiben responseToSelector. – e1985

+0

In Swift 1.2 werden Klammern wie folgt benötigt: 'super.respondsToSelector (aSelector) || (_userDelegate?. ResponsesToSelector (aSelector) == true) ' – akira108

+0

Sehr gute Antwort für die ordnungsgemäße Handhabung der Delegierungsweiterleitung. Habe mich vollständig für meine benutzerdefinierte Implementierung eines UITableView gerettet! :) – gigo

3

Ich weiß nichts über eine 100% Swift Lösung dafür. Wenn Sie this ObjC answer auf dasselbe Problem anwenden und versuchen, es nach Swift zu portieren, stellt sich heraus, dass dies nicht möglich ist, da NSInvocation in Swift nicht verfügbar ist.

Was wir tun können, ist die vorgeschlagene MyScrollViewPrivateDelegate in ObjC zu implementieren (nicht vergessen es in dem Überbrückungs Header-Datei zu importieren) und die Scroll-Ansicht Unterklasse in Swift wie folgt aus:

MyScrollView.swift

import UIKit 

class MyScrollView: UIScrollView { 

    private let myDelegate = MyScrollViewPrivateDelegate() 

    required init(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
     super.delegate = myDelegate 
    } 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
     super.delegate = myDelegate 
    } 

    override var delegate: UIScrollViewDelegate? { 
     set { 
      myDelegate.userDelegate = newValue 
      super.delegate = nil 
      super.delegate = myDelegate 
     } 

     get { 
      return myDelegate.userDelegate 
     } 
    } 

    func viewForZooming() -> UIView { 
     return UIView()// return whatever you want here... 
    } 
} 

MyScrollViewPrivateDelegate.h

#import <UIKit/UIKit.h> 

@interface MyScrollViewPrivateDelegate : NSObject <UIScrollViewDelegate> 

@property (weak, nonatomic) id<UIScrollViewDelegate> userDelegate; 

@end 

MyScrollViewPrivateDelegate.m

#import "MyScrollViewPrivateDelegate.h" 
#import "YOUR_MODULE-Swift.h" 

@implementation MyScrollViewPrivateDelegate 

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView 
{ 
    // you could check if the user delegate responds to viewForZoomingInScrollView and call it instead... 
    return [(MyScrollView *)scrollView viewForZooming]; 
} 

- (BOOL)respondsToSelector:(SEL)selector 
{ 
    return [_userDelegate respondsToSelector:selector] || [super respondsToSelector:selector]; 
} 

- (void)forwardInvocation:(NSInvocation *)invocation 
{ 
    [invocation invokeWithTarget:_userDelegate]; 
} 

@end 
0

UIScrollView bereits definiert eine delegate Eigenschaft, die jede UIScrollView Unterklasse gewonnen Es ist nicht möglich, eine Eigenschaft mit dem Namen delegate zu definieren.

Unten CustomScrollViewhat eine UIScrollView Instanz statt Subklassifizieren UIScrollView. Dies ermöglicht CustomScrollView, eine delegate Eigenschaft zu definieren, ohne mit der Superklasse in Konflikt zu geraten.

protocol CustomScrollViewDelegate: class { 
    func scrollViewDidScroll() 
} 

class CustomScrollView: UIView, UIScrollViewDelegate { 
    var scrollView: UIScrollView = UIScrollView() 
    weak var delegate: CustomScrollViewDelegate? // External delegate 

    override init (frame : CGRect) { 
     super.init(frame : frame) 
     setup() 
    } 

    required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
    } 

    func setup() { 
     scrollView.delegate = self // Internal delegate 
     addSubview(scrollView) 
    } 

    func scrollViewDidScroll(_ scrollView: UIScrollView) { 
     delegate?.scrollViewDidScroll() // External delegation 
    } 
} 
+0

Das beantwortet die Frage nicht, wie Aufrufe an diesen neuen Delegaten weitergeleitet werden, der in UIView hinzugefügt wird. – adev

+0

@adev - Antwort jetzt mit Code aktualisiert. – dxb

1

Hier ist eine einfache Arbeits Spielplatz Version in Swift 3, die rein als Beobachter fungiert und nicht nur als Abfangjäger wie die anderen Antworten hier.

Die Unterscheidung ist, dass der ursprüngliche Bildlauf-Delegat alle seine Delegate-Methoden wie normal aufgerufen haben sollte im Gegensatz zu ihnen von einem anderen Delegaten entführt werden.

(Sie können Kopieren/Einfügen dieser in einen Spielplatz und führen Sie es zu testen)

import UIKit 

final class ScrollViewObserver: NSObject, UIScrollViewDelegate { 

    // MARK: - Instantiation 

    init(scrollView: UIScrollView) { 
     super.init() 

     self.scrollView = scrollView 
     self.originalScrollDelegate = scrollView.delegate 
     scrollView.delegate = self 
    } 

    deinit { 
     self.remove() 
    } 

    // MARK: - API 

    /// Removes ourselves as an observer, resetting the scroll view's original delegate 
    func remove() { 
     self.scrollView?.delegate = self.originalScrollDelegate 
    } 

    // MARK: - Private Properties 

    fileprivate weak var scrollView: UIScrollView? 
    fileprivate weak var originalScrollDelegate: UIScrollViewDelegate? 

    // MARK: - Forwarding Delegates 

    /// Note: we forward all delegate calls here since Swift does not support forwardInvocation: or NSProxy 

    func scrollViewDidScroll(_ scrollView: UIScrollView) { 
     // Run any custom logic or send any notifications here 
     print("proxy did scroll") 

     // Then, forward the call to the original delegate 
     self.originalScrollDelegate?.scrollViewDidScroll?(scrollView) 
    } 

    func scrollViewDidZoom(_ scrollView: UIScrollView) { 
     self.originalScrollDelegate?.scrollViewDidZoom?(scrollView) 
    } 

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { 
     self.originalScrollDelegate?.scrollViewWillBeginDragging?(scrollView) 
    } 

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { 
     self.originalScrollDelegate?.scrollViewWillEndDragging?(scrollView, withVelocity: velocity, targetContentOffset: targetContentOffset) 
    } 

    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { 
     self.originalScrollDelegate?.scrollViewDidEndDragging?(scrollView, willDecelerate: decelerate) 
    } 

    func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { 
     self.originalScrollDelegate?.scrollViewWillBeginDecelerating?(scrollView) 
    } 

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 
     self.originalScrollDelegate?.scrollViewDidEndDecelerating?(scrollView) 
    } 

    func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 
     self.originalScrollDelegate?.scrollViewDidEndScrollingAnimation?(scrollView) 
    } 

    func viewForZooming(in scrollView: UIScrollView) -> UIView? { 
     return self.originalScrollDelegate?.viewForZooming?(in: scrollView) 
    } 

    func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) { 
     self.originalScrollDelegate?.scrollViewWillBeginZooming?(scrollView, with: view) 
    } 

    func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) { 
     self.originalScrollDelegate?.scrollViewDidEndZooming?(scrollView, with: view, atScale: scale) 
    } 

    func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { 
     return self.originalScrollDelegate?.scrollViewShouldScrollToTop?(scrollView) == true 
    } 

    func scrollViewDidScrollToTop(_ scrollView: UIScrollView) { 
     self.originalScrollDelegate?.scrollViewDidScrollToTop?(scrollView) 
    } 

} 

final class TestView: UIView, UIScrollViewDelegate { 

    let scrollView = UIScrollView() 
    fileprivate(set) var scrollObserver: ScrollViewObserver? 

    required init() { 
     super.init(frame: .zero) 

     self.scrollView.delegate = self 
     self.scrollObserver = ScrollViewObserver(scrollView: self.scrollView) 
    } 

    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } 

    public func scrollViewDidScroll(_ scrollView: UIScrollView) { 
     print("view's original did scroll delegate method called") 
    } 

} 

let testView = TestView() 
testView.scrollView.setContentOffset(CGPoint(x: 0, y: 100), animated: true) 
testView.scrollObserver?.remove() 
print("removed the observer") 
testView.scrollView.setContentOffset(CGPoint(x: 0, y: 200), animated: true) 
testView.scrollView.setContentOffset(CGPoint(x: 0, y: 300), animated: true) 

Dieser druckt

Proxy

Ansicht des ursprünglichen tat scroll Delegatmethode

genannt hat blättern

entfernt den Beobachter

Scroll-Delegat ursprünglich tat Methode Ansicht

Ansicht Original tat scroll Delegatmethode

genannt
Verwandte Themen