久久久久久久av_日韩在线中文_看一级毛片视频_日本精品二区_成人深夜福利视频_武道仙尊动漫在线观看

如何繼承 UIScrollView 并使委托屬性私有

How to subclass UIScrollView and make the delegate property private(如何繼承 UIScrollView 并使委托屬性私有)
本文介紹了如何繼承 UIScrollView 并使委托屬性私有的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!

問題描述

這是我想要實現的目標:

Here is what I want to achieve:

我想子類化 UIScrollView 以獲得額外的功能.這個子類應該能夠對滾動做出反應,所以我必須將委托屬性設置為 self 以接收如下事件:

I want to subclass an UIScrollView to have additional functionality. This subclass should be able to react on scrolling, so i have to set the delegate property to self to receive events like:

- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView { ... }

另一方面,其他類也應該能夠接收這些事件,就像它們使用基礎 UIScrollView 類一樣.

On the other hand, other classes should still be able to receive these events too, like they were using the base UIScrollView class.

所以我對如何解決這個問題有不同的想法,但所有這些都不完全讓我滿意:(

So I had different ideas how to solve that problem, but all of these are not entirely satisfying me :(

我的主要方法是..使用自己的委托屬性,如下所示:

My main approach is..using an own delegate property like this:

@interface MySubclass : UIScrollView<UIScrollViewDelegate>
@property (nonatomic, assign) id<UIScrollViewDelegate> myOwnDelegate;
@end

@implementation MySubclass
@synthesize myOwnDelegate;

- (id) initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.delegate = self;
    }
    return self;
}

// Example event
- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    // Do something custom here and after that pass the event to myDelegate
    ...
    [self.myOwnDelegate scrollViewDidEndDecelerating:(UIScrollView*)scrollView];
}
@end

這樣,當繼承的滾動視圖結束滾動時,我的子類可以做一些特殊的事情,但仍會通知事件的外部委托.到目前為止有效.但由于我想讓這個子類對其他開發人員可用,我想限制對基類委托屬性的訪問,因為它應該只由子類使用.我認為其他開發人員很可能直觀地使用了基類的委托屬性,即使我在頭文件中評論了問題.如果有人改變了委托屬性,子類將不會做它應該做的事情,我現在不能做任何事情來阻止它.這就是我不知道如何解決它的地方.

In that way my subclass can do something special when the inherited scrollview ends scrolling, but still informs the external delegate of the event. That works so far. But as I want to make this subclass available to other developers, I want to restrict access to the base class delegate property, as it should only be used by the subclass. I think it's most likely that other devs intuitively use the delegate property of the base class, even if I comment the problem in the header file. If someone alters the delegate property the subclass won't do what it's supposed to do and I can't do anything to prevent that right now. And that's the point where i don't have a clue how to solve it.

我嘗試覆蓋委托屬性以使其只讀,如下所示:

What I tried is trying to override the delegate property to make it readonly like this:

@interface MySubclass : UIScrollView<UIScrollViewDelegate>
...
@property (nonatomic, assign, readonly) id<UIScrollViewDelegate>delegate;
@end

@implementation MySubclass
@property (nonatomic, assign, readwrite) id<UIScrollViewDelegate>delegate;
@end

這將導致警告

"Attribute 'readonly' of property 'delegate' restricts attribute 'readwrite' of property inherited from 'UIScrollView'

好吧,這是個壞主意,因為我在這里顯然違反了 liskovs 替換原則.

Ok bad idea, as i'm obviously violating liskovs substitution principle here.

下一次嘗試 --> 嘗試像這樣覆蓋委托設置器:

Next try --> Trying to override the delegate setter like this:

...
- (void) setDelegate(id<UIScrollViewDelegate>)newDelegate {
    if (newDelegate != self) self.myOwnDelegate = newDelegate;
    else _delegate = newDelegate; // <--- This does not work!
}
...

正如評論的那樣,這個示例無法編譯,因為似乎找不到 _delegate ivar?!于是我查找了 UIScrollView 的頭文件,發現:

As commented, this example does not compile as it seems that the _delegate ivar wasn't found?! So i looked up the header file of UIScrollView and found this:

@package
    ...
    id           _delegate;
...

@package 指令將 _delegate ivar 的訪問權限限制為只能由框架本身訪問.因此,當我想設置 _delegate ivar 時,我必須使用綜合設置器.我看不到以任何方式覆蓋它的方法 :( 但我不敢相信沒有辦法解決這個問題,也許我只見樹木不見森林.

The @package directive restricts the access of the _delegate ivar to be accessible only by the framework itself. So when i want to set the _delegate ivar I HAVE TO use the synthesized setter. I can't see a way to override it in any way :( But i can't believe that there isn't a way around this, maybe i can't see the wood for the trees.

感謝您提供解決此問題的任何提示.

I appreciate for any hint on solving this problem.

它現在可以與@rob mayoff 的解決方案一起使用.正如我在下面評論的那樣,scrollViewDidScroll: 調用存在問題.我終于找到了,問題是什么,即使我不明白為什么會這樣:/

It works now with the solution of @rob mayoff . As i commented right below there was a problem with the scrollViewDidScroll: call. I finally did find out, what the problem is, even i don't understand why this is so :/

就在我們設置超級委托的那一刻:

Right in the moment when we set the super delegate:

- (id) initWithFrame:(CGRect)frame {
    ...
    _myDelegate = [[[MyPrivateDelegate alloc] init] autorelease];
    [super setDelegate:_myDelegate]; <-- Callback is invoked here
}

有一個回調到 _myDelegate.調試器在

there is a callback to _myDelegate. The debugger breaks at

- (BOOL) respondsToSelector:(SEL)aSelector {
    return [self.userDelegate respondsToSelector:aSelector];
}

使用scrollViewDidScroll:"選擇器作為參數.

with the "scrollViewDidScroll:" selector as argument.

這時候好笑的事情self.userDelegate還沒有設置,指向nil,所以返回值是NO!這似乎導致 scrollViewDidScroll: 方法之后不會被觸發.它看起來像是一個預檢查該方法是否被實現,如果它失敗了,這個方法根本不會被觸發,即使我們之后設置了我們的 userDelegate 屬性.我不知道為什么會這樣,因為大多數其他委托方法都沒有這個預檢查.

The funny thing at this time self.userDelegate isnt set yet and points to nil, so the return value is NO! That seems to cause that the the scrollViewDidScroll: methods won't get fired afterwards. It looks like a precheck if the method is implemented and if it fails this method won't get fired at all, even if we set our userDelegate property afterwards. I don't know why this is so, as the most other delegate methods don't have this precheck.

所以我的解決方案是,在 PrivateDelegate setDelegate 方法中調用 [super setDelegate...] 方法,因為這是我很確定我的 userDelegate 方法已設置的地方.

So my solution for this is, to invoke the [super setDelegate...] method in the PrivateDelegate setDelegate method, as this is the spot i'm pretty sure my userDelegate method is set.

所以我最終會得到這個實現片段:

So I'll end up with this implementation snippet:

MyScrollViewSubclass.m

MyScrollViewSubclass.m

- (void) setDelegate:(id<UIScrollViewDelegate>)delegate {
    self.internalDelegate.userDelegate = delegate;  
    super.delegate = self.internalDelegate;
}

- (id) initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.internalDelegate = [[[MyScrollViewPrivateDelegate alloc] init] autorelease];
        // Don't set it here anymore
    }
    return self;
}

其余代碼保持不變.我仍然對這種解決方法不太滿意,因為它需要至少調用一次 setDelegate 方法,但它目前可以滿足我的需求,雖然感覺很hacky:/

The rest of the code remains untouched. I'm still not really satisfied with this workaround, because it makes it necessary to call the setDelegate method at least once, but it works for my needs for the moment, although it feels very hacky :/

如果有人知道如何改進它,我將不勝感激.

If someone has ideas how to improve that, I'd appreciate that.

感謝@rob 你的例子!

Thanks @rob for your example!

推薦答案

MySubclass 設為自己的委托時出現問題.大概您不想為 UIScrollViewDelegate 方法的 all 運行自定義代碼,但是無論您是否有自己的實現,都必須將消息轉發給用戶提供的委托或不.所以你可以嘗試實現所有的委托方法,其中大多數只是像這樣轉發:

There is a problem with making MySubclass its own delegate. Presumably you don't want to run custom code for all of the UIScrollViewDelegate methods, but you have to forward the messages to the user-provided delegate whether you have your own implementation or not. So you could try to implement all of the delegate methods, with most of them just forwarding like this:

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    [self.myOwnDelegate scrollViewDidZoom:scrollView];
}

這里的問題是有時新版本的 iOS 會添加新的委托方法.例如,iOS 5.0 添加了 scrollViewWillEndDragging:withVelocity:targetContentOffset:.所以你的滾動視圖子類不會是面向未來的.

The problem here is that sometimes new versions of iOS add new delegate methods. For example, iOS 5.0 added scrollViewWillEndDragging:withVelocity:targetContentOffset:. So your scrollview subclass won't be future-proof.

處理此問題的最佳方法是創建一個單獨的私有對象,該對象僅充當滾動視圖的委托,并處理轉發.此專用委托對象可以將其接收到的每條消息轉發給用戶提供的委托,因為它接收委托消息.

The best way to handle this is to create a separate, private object that just acts as your scrollview's delegate, and handles forwarding. This dedicated-delegate object can forward every message it receives to the user-provided delegate, because it only receives delegate messages.

這就是你要做的.在你的頭文件中,你只需要為你的滾動視圖子類聲明接口.您不需要公開任何新方法或屬性,因此它看起來像這樣:

Here's what you do. In your header file, you only need to declare the interface for your scrollview subclass. You don't need to expose any new methods or properties, so it just looks like this:

@interface MyScrollView : UIScrollView
@end

所有真正的工作都在 .m 文件中完成.首先,我們為私有委托類定義接口.它的工作是為某些委托方法回調 MyScrollView,并將 all 消息轉發給用戶的委托.所以我們只想給它提供 UIScrollViewDelegate 的方法.我們不希望它有額外的方法來管理對用戶委托的引用,因此我們將把該引用保留為實例變量:

All the real work is done in the .m file. First, we define the interface for the private delegate class. Its job is to call back into MyScrollView for some of the delegate methods, and to forward all messages to the user's delegate. So we only want to give it methods that are part of UIScrollViewDelegate. We don't want it to have extra methods for managing a reference to the user's delegate, so we'll just keep that reference as an instance variable:

@interface MyScrollViewPrivateDelegate : NSObject <UIScrollViewDelegate> {
@public
    id<UIScrollViewDelegate> _userDelegate;
}
@end

接下來我們將實現 MyScrollView.它需要創建一個它需要擁有的 MyScrollViewPrivateDelegate 的實例.由于 UIScrollView 不擁有其委托,因此我們需要對該對象的額外強引用.

Next we'll implement MyScrollView. It needs to create an instance of MyScrollViewPrivateDelegate, which it needs to own. Since a UIScrollView doesn't own its delegate, we need an extra, strong reference to this object.

@implementation MyScrollView {
    MyScrollViewPrivateDelegate *_myDelegate;
}

- (void)initDelegate {
    _myDelegate = [[MyScrollViewPrivateDelegate alloc] init];
    [_myDelegate retain]; // remove if using ARC
    [super setDelegate:_myDelegate];
}

- (id)initWithFrame:(CGRect)frame {
    if (!(self = [super initWithFrame:frame]))
        return nil;
    [self initDelegate];
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    if (!(self = [super initWithCoder:aDecoder]))
        return nil;
    [self initDelegate];
    return self;
}

- (void)dealloc {
    // Omit this if using ARC
    [_myDelegate release];
    [super dealloc];
}

我們需要重寫 setDelegate:delegate: 來存儲和返回對用戶委托的引用:

We need to override setDelegate: and delegate: to store and return a reference to the user's delegate:

- (void)setDelegate:(id<UIScrollViewDelegate>)delegate {
    _myDelegate->_userDelegate = delegate;
    // Scroll view delegate caches whether the delegate responds to some of the delegate
    // methods, so we need to force it to re-evaluate if the delegate responds to them
    super.delegate = nil;
    super.delegate = (id)_myDelegate;
}

- (id<UIScrollViewDelegate>)delegate {
    return _myDelegate->_userDelegate;
}

我們還需要定義我們的私有委托可能需要使用的任何額外方法:

We also need to define any extra methods that our private delegate might need to use:

- (void)myScrollViewDidEndDecelerating {
    // do whatever you want here
}

@end

現在我們終于可以定義MyScrollViewPrivateDelegate的實現了.我們需要明確定義每個應該包含我們私有自定義代碼的方法.該方法需要執行我們的自定義代碼,如果用戶的代理響應了消息,將消息轉發給用戶的代理:

Now we can finally define the implementation of MyScrollViewPrivateDelegate. We need to explicitly define each method that should contain our private custom code. The method needs to execute our custom code, and forward the message to the user's delegate, if the user's delegate responds to the message:

@implementation MyScrollViewPrivateDelegate

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    [(MyScrollView *)scrollView myScrollViewDidEndDecelerating];
    if ([_userDelegate respondsToSelector:_cmd]) {
        [_userDelegate scrollViewDidEndDecelerating:scrollView];
    }
}

我們需要處理所有其他我們沒有自定義代碼的 UIScrollViewDelegate 方法,以及將在未來版本的 iOS 中添加的所有這些消息.我們必須實現兩種方法來實現這一點:

And we need to handle all of the other UIScrollViewDelegate methods that we don't have custom code for, and all of those messages that will be added in future versions of iOS. We have to implement two methods to make that happen:

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

- (void)forwardInvocation:(NSInvocation *)invocation {
    // This should only ever be called from `UIScrollView`, after it has verified
    // that `_userDelegate` responds to the selector by sending me
    // `respondsToSelector:`.  So I don't need to check again here.
    [invocation invokeWithTarget:_userDelegate];
}

@end

這篇關于如何繼承 UIScrollView 并使委托屬性私有的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!

【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!

相關文檔推薦

Swift - how to get last taken 3 photos from photo library?(Swift - 如何從照片庫中獲取最后拍攝的 3 張照片?)
Setting contentOffset programmatically triggers scrollViewDidScroll(以編程方式設置 contentOffset 觸發 scrollViewDidScroll)
why UIScrollView is leaving space from top in ios 6 and ios 7(為什么 UIScrollView 在 ios 6 和 ios 7 中從頂部留下空間)
UIScrollView pauses NSTimer while scrolling(UIScrollView 在滾動時暫停 NSTimer)
Setting up UIScrollView to swipe between 3 view controllers(設置 UIScrollView 在 3 個視圖控制器之間滑動)
UIScrollView with quot;Circularquot; scrolling(帶有“圓形的 UIScrollView滾動)
主站蜘蛛池模板: 九九热精品视频 | 国产精品久久久久久久久污网站 | 久久精品亚洲一区二区三区浴池 | 日韩成人在线视频 | 成人在线观看免费 | 91精品入口蜜桃 | 一区二区三区免费 | 久久久久久久一区二区三区 | 福利精品 | 黄色网址在线免费观看 | 午夜精品久久久久久久久久久久久 | 久久久久久久国产精品视频 | 91黄在线观看 | 中文字幕一区二区三区四区五区 | 偷拍第一页 | 欧美成人精品一区二区男人看 | h片在线观看免费 | 欧美精品电影一区 | 日韩专区中文字幕 | 国产99久久 | 在线午夜| 一级片视频免费 | 欧美一区二区在线 | 97avcc| 日本亚洲一区 | 视频一区二区在线观看 | 国产激情偷乱视频一区二区三区 | 成人在线视频观看 | 欧美午夜精品久久久久免费视 | 欧美亚洲免费 | 国产区在线免费观看 | 久久视频一区 | 国产免费视频在线 | 在线激情视频 | 91精品国产91久久久久久密臀 | 天天看天天操 | 中文字幕 亚洲一区 | 日韩视频在线一区 | 日韩图区 | 成年人视频在线免费观看 | 婷婷开心激情综合五月天 |