你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 一個 Excel 窗口凍結效果的實現

一個 Excel 窗口凍結效果的實現

編輯:IOS開發基礎

作者:@靛青K 授權本站轉載。作者特地為本站將文章進行了潤色,在此致謝。

最近做了一個輪子 DQKFreezeWindowView ,這裡我們一起探討一下這個輪子中凍結效果的簡單實現思路,也就是我的思考過程。

不廢話,直接開始~

初步分析

既然是一個凍結的效果,那至少要用一個UIScrollView來做主要顯示的部分。還是先來看幾張已經實現了的:

pic_001.png

pic_002.png

pic_003.png

在三者的使用當中,個人認為體驗最好就是 MS 的 Excel 了,流暢、各方向都可以滑動。這裡先用 Reveal 查看一下三者的布局,使用的 View 都是什麼。

pic_004.png

可以看到某課程表 App 是選擇了一個 UIScrollView 加兩個 Bar (UIView)實現該功能。簡單實用,顯示少量視圖尚可。

pic_005.png

為了顯示更多數據/視圖, Excel 就采用了三個UIScrollView。

這裡有一些奇怪的現象引起了我的注意,為什麼三者左邊都不支持滑動?明明邊欄視圖都是在UIScrollView裡,怎麼想都是一個不合理的情況。所以我們的目標是這樣的幾個功能實現:

  • 主要視圖支持多方位滾動

  • 邊欄視圖一樣支持滾動

  • bounces效果實現

  • 像 Excel 一樣支持更多數據顯示

既然需要實現邊緣的滑動,就要三個UIScrollView。為什麼一個不行,因為當你滑動的時候,頂欄和側欄最好是留在邊緣的。

注意:

這裡我考慮過使用UITableView或者UICollectionView實現,因為它已經很好的解決了delegate和dataSource等眾多問題。 XCMultiSortTableView 這個開源項目的方案是UITableView裡面的UITableViewCell套一個UITableView,很好的實現邊緣滾動問題。贊!但是並不能滿足咱的要求,像 Excel 那樣任意方向滾動。 博主沒有想到用UITableView或者UICollectionView實現的方案所依如果你在這方面有什麼好方案,快來和我交流。

關於以上幾款 App 的研究有興趣可以學習逆向相關問題 class-dump 和 IDA ,這裡不再贅述。

開始實現

我們需要三個UIScrollView,首要問題就是實現同步滾動,也是整個問題的關鍵部分:

為了方便定位視圖位置,先來自定義一個UIView,聲明三個UIScrollView類,mainScrollView 、 sectionScrollView 、 rowScrollView。將該UIVIew添加到一個UIViewController中,三者在UIView位置如下圖:

pic_006.jpg

為什麼這樣起名字,因為像日歷、課程表之類都是豎著一排在一天,(好吧、其實是想不到合適的了,如果你想到更好的,快來告訴我)。

開始實現同步滾動問題:

  • 普通情況的滾動

    這個簡單,使用- (void)scrollViewDidScroll:(nonnull UIScrollView *)scrollView加上- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;即可。比如 mainScrollView x 方向滾動多少, sectionScrollView x 方向滾動多少。

  • 滾動到邊緣情況

    這個稍微復雜一些,這裡先談一個觸摸邊緣滾動的情況,並且沒有bounces,這種情況不涉及滑動越過邊界問題。滾動 SectionScrollView 時, RowScrollView 不動,反之亦然。當前代碼如下:

- (void)scrollViewDidScroll:(nonnull UIScrollView *)scrollView
{
    if ([scrollView isEqual:self.mainScrollView]) { //滾動 mainScrollView
        self.sectionScrollView.delegate = nil;
        self.rowScrollView.delegate = nil;
        [self.sectionScrollView setContentOffset:CGPointMake(self.mainScrollView.contentOffset.x, 0)];
        [self.rowScrollView setContentOffset:CGPointMake(0, self.mainScrollView.contentOffset.y)];
         self.sectionScrollView.delegate = self;
         self.rowScrollView.delegate = self;
    } else if ([scrollView isEqual:self.sectionScrollView]) {  // 滾動 sectionScrollView
        self.mainScrollView.delegate = nil;
        self.rowScrollView.delegate = nil;
        [self.mainScrollView setContentOffset:CGPointMake(self.sectionScrollView.contentOffset.x, self.mainScrollView.contentOffset.y)];
         self.mainScrollView.delegate = self;
         self.rowScrollView.delegate = self;
    } else if ([scrollView isEqual:self.rowScrollView]) {  // 滾動 rowScrollView
        self.mainScrollView.delegate = nil;
        self.sectionScrollView.delegate = nil;
        [self.mainScrollView setContentOffset:CGPointMake(self.mainScrollView.contentOffset.x, self.rowScrollView.contentOffset.y)];
         self.mainScrollView.delegate = self;
         self.sectionScrollView.delegate = self;
    }
}

诶?為什麼我們要設置其他 scrollView 的delegate = nil。如果不設置,當視圖滾動時,再次滑動會出現視圖卡頓、移動混亂情況。

為什麼?

因為要實現三個 scrollView 都支持滾動,那就都要設置其delegate屬性,那麼滾動時,三個視圖都會委托這個函數來執行,如果不分開他們的滾動情況討論,並在情況裡面設置其他 scrollView delegate 為 nil。

其實到這裡需要的功能已經實現了。但是我在使用的時候發現一個 Bug ,比如,我們現在滑動的是 mainScrollView ,視圖停止滾動之前,去滑動 sectionScrollView 。诶!!!視圖一下子就錯位了,而且再滑動 mainScrollView 也不會同步了。這裡我的理解是,- (void)scrollViewDidScroll:(nonnull UIScrollView *)scrollView,這個委托方法是在視圖只要有滾動就會一直執行,那麼其對應的delegate也是不停的在nil和self之間不停的切換,滾動 mainScrollView 時,又去滾動另一個 scrollView ,將會執行self.mainScrollView.delegate = nil;, 同時執行另一個- (void)scrollViewDidScroll:(nonnull UIScrollView *)scrollView,出現錯位,如果不去滑動其他 scrollView ,self.mainScrollView.delegate就永遠是nil了,沒有了委托對象,自然不會繼續同步。

兩步解決問題,以滾動 mainScrollView 舉例,第一步,在情況開始添加:

[self.sectionScrollView setContentOffset:self.sectionScrollView.contentOffset animated:NO];
[self.rowScrollView setContentOffset:self.rowScrollView.contentOffset animated:NO];

為什麼這樣做?理由很簡單,立刻停止其他兩個 scrollView 的滾動,其他情況的計算也就立即停止了。只計算一個滾動 mainScrollView 情況。

第二步,在委托方法最後重新設置回三者的delegate對象。這樣一定不會在某次執行完出現nil情況了。

編譯運行,Gut~隨意滑動,三個位置任意滑動,隨時切換滑動對象。

那麼bounces效果怎麼辦?沒有這個效果,滾動到邊緣就會出現立即停止的不自然感覺。這裡想過幾種方案,不好的就不在這裡談了,浪費篇章,只提現在想到的最佳方案。先來考慮 Excel 的效果實現,你這麼聰明,其實早就發現根本不需要改代碼,對,沒錯。那麼現在只需要考慮DQKFreezeWindowViewBounceStyleAll情況,滾動到邊緣時,看起來只是一個UIScrollView。這裡需要考慮的就是 sectionScrollView 向下和 rowScrollView 向右移動的問題。當然這裡也有多種方案:

  • 方案一:增加 View 的frame.size大小,這樣視圖就不會丟失了;

  • 方案二:改變 View 的位置。

采取方案二,方案一有視圖重疊問題,不好不好。在原基礎上增加代碼,因為對於 sectionScrollView 視圖水平滾動(contentOffset)已經完成,那麼視圖位置只需要垂直移動即可。還是滾動 mainScrollView 情況,直接貼代碼:

if (self.bounceStyle == DQKFreezeWindowViewBounceStyleAll) {
    if (self.mainScrollView.contentOffset.y <= 0) {
        [self.sectionScrollView setFrame:CGRectMake(self.sectionScrollView.frame.origin.x, - self.mainScrollView.contentOffset.y, self.sectionScrollView.frame.size.width, self.sectionScrollView.frame.size.height)];
    }
    if (self.mainScrollView.contentOffset.x <= 0) {
        [self.rowScrollView setFrame:CGRectMake(- self.mainScrollView.contentOffset.x, self.rowScrollView.frame.origin.y, self.rowScrollView.frame.size.width, self.rowScrollView.frame.size.height)];
    }
}

最初的方案是考慮四個情況的(>>,><,<>,<<),同時還進行各種計算,現在發現,沒有必要,sectionScrollViewy 坐標是 0 。那麼滾動多少,移動多少就可以了。(取相反數,為什麼?)

你應該注意到我在相關實現文件裡還加了一個 signView ,也就是左上角的小視圖。至於這個如何跟隨著移動,請參考源文件或者留著你來思考,類似 sectionScrollViewrowScrollView

視圖滾動問題解決!

效果已經實現,接下來的問題就是:

  • 數據/視圖加載問題(也就是 Cell 的重用機制問題)

    為了支持更好的使用內存,支持更多的數據滾動顯示,那麼如何計算那些視圖在什麼時候移除,又在什麼時候加載,什麼時候釋放成了一個關鍵的難題。

  • 類似 UITableView 的 delegate 和 dateSource 的實現。

    這裡看過一些 GitHub 項目,發現其最終還是基於 UITableView 來實現的,失望ing

這兩個問題,筆者實現的並不好,甚至很爛,同時限於篇幅,不再贅述這些問題。如果你在這裡有什麼好的經驗或者見解,希望來與我分享,非常感激。當然,如果你對我對這兩個問題的解決方案感興趣,可以查看源碼,隨時私信我。歡迎。我的微博: @靛青K

最後寫兩個不相關的兩件事以及一個相關的

  • 最近很想有個實習的機會來鍛煉,如果你願意幫我,非常感激,坐標北京;

  • 如果你覺得我們水平差不多,希望可以一起學習(自己孤零零的泡圖書館很辛苦),坐標北京;

  • 接下來筆者可能嘗試編寫一個 tweak 使 Excel 支持邊緣滾動,如果在這方面你有什麼經驗,歡迎來與我探討。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved