博客園第三方客戶端-i博客園正式發佈App Store

来源:http://www.cnblogs.com/polobymulberry/archive/2016/02/18/5189378.html
-Advertisement-
Play Games

博客園第三方客戶端-i博客園正式發佈App Store 1. 前言 算來從15年8月到現在自學iOS已經快7個月了,雖然中間也是斷斷續續的,不過竟然堅持下來了。年後要找實習啦,於是萌生了一個想法 —— 寫一個app練練手。這次我沒弄後臺了,直接使用了博客園的open api(嘿嘿)。之前也做過一個a


博客園第三方客戶端-i博客園正式發佈App Store

1. 前言


算來從15年8月到現在自學iOS已經快7個月了,雖然中間也是斷斷續續的,不過竟然堅持下來了。年後要找實習啦,於是萌生了一個想法 —— 寫一個app練練手。這次我沒弄後臺了,直接使用了博客園的open api(嘿嘿)。之前也做過一個app,叫做魔界-魔術,前後端都是我弄的,不過後端使用的是Bmob後端雲(一個Baas服務),但是作為第一個app,代碼上感覺很混亂,而且基本上都是用的第三方控制項。這次的i博客園是我完全獨立開發的(包括UI設計),整體使用的是MVC模式,並且儘量不去使用別人第三方控制項(雖然還是用了。後面會提到具體使用)。

先放出幾張app的gif預覽圖片:

1 2 3

Appstore地址:

大家可以在AppStore搜索i博客園。或者掃描下麵二維碼:

2016-02-18-1009004908

 

2. 使用的資料和工具


  • 博客園官方open web api網址:
  1. http://wcf.open.cnblogs.com/news/help (新聞)
  2. http://wcf.open.cnblogs.com/blog/help (博客)
  • 使用到的第三方控制項
    • AFNetworking
    • SDWebImage
    • HMSegmentedControl(Segmented Control)
    • RESideMenu (側滑控制器視圖)
    • MJRefresh
    • Masonry (AutoLayout)
    • UITableView+FDTemplateLayoutCell (動態計算UITableViewCell的高度)
    • XMLDictionary (解析XML文件,因為博客園web api傳回來的是xml數據)
  • UI資源和工具

3. 解決的問題


問題一:實現引導頁(不是啟動頁)上的RippleButton(有水波漣漪動畫的按鈕,第一張gif圖片上的那個粉紅色按鈕)

解決思路:

1. 使用UIBesierPath構建一個圓形的path

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:pathFrame cornerRadius:self.layer.cornerRadius];

2. 將上面的path賦值給circleShape(CAShapeLayer對象)的path屬性,同時添加該circleShape到RippleButton(UIView類型)上

CAShapeLayer *circleShape = [CAShapeLayer layer];
circleShape.path = path.CGPath;
[self.layer addSublayer:circleShape];

3. 這時,就可以使用Core Animation來操作RippleButton的layer了,細節我就不詳細說了,無非是通過動畫控制圓圈的scale和alpha

CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.5, 2.5, 1)];

CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
alphaAnimation.fromValue = @1;
alphaAnimation.toValue = @0;

CAAnimationGroup *animation = [CAAnimationGroup animation];
animation.animations = @[scaleAnimation, alphaAnimation];
animation.duration = 1.0f;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[circleShape addAnimation:animation forKey:nil];

4. 但是如果僅僅添加一個circleShape,那麼不會有多個水波散開的效果。於是我又將上述123步代碼封裝成createRippleEffect函數,並添加到定時器中

- (void)setupRippleTimer
{
    __weak __typeof__(self) weakSelf = self;
    NSTimeInterval repeatInterval = self.repeatInterval;
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, repeatInterval * NSEC_PER_SEC, 0);
    
    __block NSInteger count = 0;
    dispatch_source_set_event_handler(self.timer, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            count ++;
            // 水波紋重覆次數,預設-1,表示永久
            if (self.repeatCount != -1 && count > weakSelf.repeatCount) {
                [weakSelf stopRippleEffect];
                return;
            }
            [weakSelf createRippleEffect];
        });
    });
}

問題二:48小時閱讀和十日推薦中使用了UICollectionView,自定義了UICollectionViewLayout,實現輪盤旋轉的效果(部分代碼參考了AWCollectionViewDialLayout

解決思路:

1. 首先得知道自定義UICollectionViewLayout的具體流程

實現自定義的UICollectionViewLayout的具體流程請參考這篇文章,很詳細!

2. 整個自定義UICollectionViewLayout實現過程中,最核心的要數layoutAttributesForElementsInRect這個函數了

2.1 首先根據rect的y值來計算出哪幾個cell在當前rect中:

// 在rect這個區域內有幾個cell,返回每個cell的屬性
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *layoutAttributes = [NSMutableArray array];
    
    CGFloat minY = CGRectGetMinY(rect);
    CGFloat maxY = CGRectGetMaxY(rect);
    // 獲取到rect這個區域的cells的firstIndex和lastIndex,這兩個沒啥用,主要是為了獲取activeIndex
    NSInteger firstIndex = floorf(minY / self.itemHeight);
    NSInteger lastIndex = floorf(maxY / self.itemHeight);
    NSInteger activeIndex = (firstIndex + lastIndex) / 2; // 中間那個cell設為active
    // maxVisiableOnScreeen表示當前屏幕最多有多少cell
    // angularSpacing表示每隔多少度算一個cell,因為這裡是輪盤,每個cell其實看做一個扇形
    NSInteger maxVisiableOnScreeen = 180 / self.angularSpacing + 2;
    
    // firstItem和lastItem就表示哪幾個cell處於當前rect
    NSInteger firstItem = fmax(0, activeIndex - (NSInteger)maxVisiableOnScreeen/2);
    NSInteger lastItem = fmin(self.cellCount, activeIndex + (NSInteger)maxVisiableOnScreeen/2);
    if (lastItem == self.cellCount) {
        firstItem = fmax(0, self.cellCount - (NSInteger)maxVisiableOnScreeen);
    }
    // 計算rect中每個cell的UICollectionViewLayoutAttributes
    for (NSInteger i = firstItem; i < lastItem; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *attributes= [self layoutAttributesForItemAtIndexPath:indexPath];
        [layoutAttributes addObject:attributes];
    }
    
    return layoutAttributes;
}

2.2 計算每個cell的UICollectionViewLayoutAttributes

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    // 預設offset為0
    CGFloat newIndex = (indexPath.item + self.offset);
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    attributes.size = self.cellSize;
    
    CGFloat scaleFactor, deltaX;
    CGAffineTransform translationT;
    CGAffineTransform rotationT;
    
    switch (self.wheetAlignmentType) {
        case WheetAlignmentTypeLeft:
            scaleFactor = fmax(0.6, 1 - fabs(newIndex * 0.25));
            deltaX = self.cellSize.width / 2;
            attributes.center = CGPointMake(-self.radius + self.xOffset, self.collectionView.height/2+self.collectionView.contentOffset.y);
            rotationT = CGAffineTransformMakeRotation(self.angularSpacing * newIndex * M_PI / 180);
            translationT = CGAffineTransformMakeTranslation(self.radius + deltaX * scaleFactor, 0);
            break;
        case WheetAlignmentTypeRight:
            scaleFactor = fmax(0.6, 1 - fabs(newIndex * 0.25));
            deltaX = self.cellSize.width / 2;
            attributes.center = CGPointMake(self.radius - self.xOffset  + ICDeviceWidth, self.collectionView.height/2+self.collectionView.contentOffset.y);
            rotationT = CGAffineTransformMakeRotation(-self.angularSpacing * newIndex * M_PI / 180);
            translationT = CGAffineTransformMakeTranslation(- self.radius - deltaX * scaleFactor, 0);
            break;
        case WheetAlignmentTypeCenter:
            // 待實現
            break;
        default:
            break;
    }
    
    CGAffineTransform scaleT = CGAffineTransformMakeScale(scaleFactor, scaleFactor);
    attributes.alpha = scaleFactor; // alpha和scaleFactor一致
    // 先scale縮小,在translation到對應位置(因為是扇形,每個cell的x值和對應位置有關),最後rotation(形成弧形)
    attributes.transform = CGAffineTransformConcat(scaleT, CGAffineTransformConcat(translationT, rotationT));
    attributes.zIndex = indexPath.item;
    
    return attributes;
}

問題三:實現帶動畫的TabBarItem

解決思路:

不詳細說了,我將代碼提交到了Github - animated-tab-bar-Objective-CPJXAnimatedTabBarController is a Objective-C version of RAMAnimatedTabBarController(https://github.com/Ramotion/animated-tab-bar))。

主要就是自定義UITabBarItem,以及自定義UITabBarItem的AutoLayout構建。代碼封裝的很好,尤其動畫實現部分,結構很清晰,符合OOP思想。

問題四:博客園使用的xml形式的open web api。解析困難。

解決思路:

這裡我還是使用MVC思路,使用AFNetworking獲取到XML數據,再使用XMLDictionary來解析XML數據(本質是NSXMLParserDelegate),並將其轉化為Model(需要自己實現)。

關於NSXMLParserDelegate可以參考這篇文章 - iOS開發之解析XML文件

問題五:設計部分,不是很擅長,每個頁面的佈局都需要想很久,儘量做得簡潔,有科技風。

解決思路:

基本上就是多看別人app設計,模仿,或者自己想啊想。也是第一次用Sketch,話說還挺好用的。

4. 存在問題和TODO


  • 分享到微信微博等等,準備使用友盟。
  • 涉及到UIWebView界面的排版,很醜。不是很懂CSS、JS、HTML5。之前為了一個圖片適配搞了半天,其實只要在<head>中加上"img{max-width:100%%;height:auto;}"就行。懇請大家指點一下我。
  • 使用自定義TabBarItem後,隱藏TabBar很麻煩。
  • 離線閱讀
  • ……

5. 後記


自己親手去寫代碼確實感覺上是不一樣,很多細節問題,雖然不難,但是很有挑戰性。代碼目前很挫,後面修改規範點,準備放到Github上。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 是CApropertyAnimation的子類,跟CABasicAnimation的區別是:CABasicAnimation只能從一個數值(fromValue)變到另一個數值(toValue),而CAKeyframeAnimation會使用一個NSArray保存這些數值,就可以實現CALayer的某
  • 什麼是JRebel for Android? 一款Android studio插件——允許你修改正在運行中的應用程式,而且不必重新部署或重啟。支持所有運行Android 4.0及以上版本的手機和平板。由ZeroTurnaround開發。這是一款收費插件,每年49美元,免費試用21天。 通常在開發過程
  • Ios中的定位功能是通過 Core Location框架實現的。它和地圖開發框架是相互獨立的。在Core Location中主要實現了定位和地理編碼的功能! 下麵我們就來介紹一下它的屬性,方法和代理方法! 屬性: desiredAccuracy:定位精度,是一個枚舉類型 //kCLLocationA
  • package com.example.yanlei.picture; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.Menu; import androi
  • ZXing的二維碼功能的提取lib下載地址:https://github.com/xuyisheng/ZXingLib 1.掃描二維碼: 我們掃描就是要用到這個CaptureActivity類,直接把上面下載地址裡面下載了裡面的libzxing作為Module,如下圖: 首先加上許可權: <!-- 相
  • 抖動效果在開發中比較少用到,不過有時使用了確有個很好的裝逼效果,用的時候就例如一些用戶錯誤操作之類的 效果如下,不過gif看到的效果沒實際的好看 上代碼 1 - (void)shakeAnimationForView:(UIView *) view 2 3 { 4 // 獲取到當前的View 5 6
  • 第十一章 Android的線程和線程池 從用途上來說,線程分為子線程和主線程,主線程主要處理和界面相關的事情,而子線程往往用於執行耗時的操作。AsyncTask,IntentService,HandlerThread都可以扮演線程的角色。 AsyncTask封裝了線程池和Handler,主要是為了方
  • 分類:C#、Android、VS2015; 創建日期:2016-02-18 1、主界面運行截圖 2、MainActivity.cs文件中對應的代碼 chItems.Add(new Chapter() { ChapterName = "第10章 擴展組件庫和其他視圖", ChapterItems = ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...