1. 程式人生 > >【iOS】讓我們一次性解決導航欄的所有問題

【iOS】讓我們一次性解決導航欄的所有問題

前一段時間換了工作,公司專案趕得比較緊,沒有時間更新文章,現在閒下來了,趕緊寫一篇來彌補自己的羞愧。
今天我們來重點討論導航欄返回的問題,包括各種問題的解決方案。

系統預設導航欄的返回按鈕和返回方式

在預設情況下,導航欄返回按鈕長這個樣子


導航欄預設返回按鈕

導航欄左上角的返回按鈕,其文字預設為上一個ViewController的標題,如果上一個ViewController沒有標題,則為Back(中文環境下為“返回”)。

在預設情況下,導航欄返回的點選互動和滑動互動如下


預設導航欄互動

這些東西不需要任何設定和操作,因此也沒有其他需要說明的地方。

自定義左上角的返回按鈕

絕大多數情況下,我們都需要根據產品需求自定義左上角的返回按鈕,雖然這對大多數開發者來說不是什麼難事,但依然有幾個問題值得注意。

替換左上角返回按鈕

替換返回按鈕非常簡單,只需要在ViewController中建立一個UIBarButtonItem和一張圖片,併為按鈕新增相應的點選事件即可,程式碼如下

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.

UIButton * leftBtn = [UIButton buttonWithType:UIButtonTypeSystem];
leftBtn.frame = CGRectMake(0, 0, 25,25);
[leftBtn setBackgroundImage:[UIImage imageNamed:@"nav_back"] forState:UIControlStateNormal];
[leftBtn addTarget:self action:@selector(leftBarBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:leftBtn];
}
- (void)leftBarBtnClicked:(UIButton *)btn
{
 [self.navigationController popViewControllerAnimated:YES];
}

我們來看一眼效果


替換返回按鈕
調整按鈕位置

我們可以看到,上面的按鈕是有點偏右的,那如果我們想調整按鈕的位置該怎麼做呢?設定Frame顯然是行不通的,因為導航欄的NavigationItem是個比較特殊的View,我們無法通過簡單的調整Frame來的調整左右按鈕的位置。但是在蘋果提供的UIButtonBarItem中有個叫做UIBarButtonSystemItemFixedSpace的控制元件,利用它,我們就可以輕鬆調整返回按鈕的位置。具體使用方法如下

//建立返回按鈕
UIButton * leftBtn = [UIButton buttonWithType:UIButtonTypeSystem];
leftBtn.frame = CGRectMake(0, 0, 25,25);
[leftBtn setBackgroundImage:[UIImage imageNamed:@"icon_back"] forState:UIControlStateNormal];
[leftBtn addTarget:self action:@selector(leftBarBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem * leftBarBtn = [[UIBarButtonItem alloc]initWithCustomView:leftBtn];;
//建立UIBarButtonSystemItemFixedSpace
UIBarButtonItem * spaceItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
//將寬度設為負值
spaceItem.width = -15;
//將兩個BarButtonItem都返回給NavigationItem
self.navigationItem.leftBarButtonItems = @[spaceItem,leftBarBtn];

我們來看一眼效果


調整返回按鈕位置

可以看到,我們的返回按鈕已經緊靠著螢幕邊緣。

這個方法同樣適用於調整導航欄右側的按鈕

讓滑動返回手勢生效

如果使用自定義的按鈕去替換系統預設返回按鈕,會出現滑動返回手勢失效的情況。解決方法也很簡單,只需要重新新增導航欄的interactivePopGestureRecognizerdelegate即可。
首先為ViewContoller新增UIGestureRecognizerDelegate協議

然後設定代理

self.navigationController.interactivePopGestureRecognizer.delegate = self;

至此,我們已經將返回按鈕替換為我們的自定義按鈕,並使滑動返回重新生效。接下來,我們繼續來解決互動上的問題。

全屏滑動返回

這個一個很常見的需求,網上解決方案也很多,這裡將本人常用的方法貼到這裡。僅供參考
實現全屏滑動返回僅需在導航欄給導航欄新增UIGestureRecognizerDelegate協議,並在ViewDidLoad中寫入如下程式碼

// 獲取系統自帶滑動手勢的target物件
id target = self.interactivePopGestureRecognizer.delegate;

// 建立全屏滑動手勢,呼叫系統自帶滑動手勢的target的action方法
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];

// 設定手勢代理,攔截手勢觸發
pan.delegate = self;

// 給導航控制器的view新增全屏滑動手勢
[self.view addGestureRecognizer:pan];

// 禁止使用系統自帶的滑動手勢
self.interactivePopGestureRecognizer.enabled = NO;

我們來看一眼效果(注意滑鼠位置)


全屏滑動返回.gif

成功

這種方法的原理其實很簡單,其實就是自定義一個全屏滑動手勢,並將滑動事件設定為系統滑動事件,然後禁用系統滑動手勢即可。handleNavigationTransition就是系統滑動的方法,雖然系統並未提供介面,但是我們我們可以通過runtime找到這個方法,因此直接呼叫即可。兩位,不必擔心什麼私有API之類的問題,蘋果如果按照方法名去判斷是否使用私有API,那得誤傷多少App。

NavigationBar切換動畫的“終極解決方案”

本部分文字程式碼都較多,不想看這麼多廢話的同學請直接翻到末尾,文末附有下載地址,匯入專案後,繼承即可生效。

在改變了導航欄樣式,實現了全屏滑動返回之後,我們有了一個看起來還不錯的導航欄。但是我們滑動時的切換依然是系統自帶的動畫,如果遇到前一個介面的NavigationBar為透明或前後兩個Bar顏色不一樣,這種漸變式的動畫看起來就會不太友好,尤其當前後兩個介面其中一個介面的NavigationBar為透明或隱藏時,其效果更是慘不忍睹。

這個問題,其實很多App,比如天貓、美團等都通過一種“整體返回”的效果來解決這個問題。效果如下:


整體滑動返回

這種解決方案等於將兩個NavigationBar獨立開來,因此可以相對完美的解決導航欄滑動切換中的種種Bug。
接下來,我們來看看如何實現這種效果。

基本原理

以我個人的認知,實現這個效果有三種基本思路:

  1. 使用UINavigationController自帶的setNavigationBarHidden: animated:方法來實現,每次push或pop時,在當前控制器的viewWillDisappear:中設定隱藏,在要跳轉的控制器的viewWillAppear:中設定導航欄顯示。
  2. 在每次Push前對當前頁面進行截圖並儲存到陣列,Pop時取陣列最後一個元素顯示,滑動結束後呼叫系統Pop方法並刪除最後一張截圖。
  3. 使用iOS 7之後開放的,UIViewControllerAnimatedTransitioning協議,來實現自定義導航欄轉場動畫及互動。

以上三種方法,方法一十分繁瑣,而且會有很多莫名其妙的BUG,直接pass。

在iOS的互動中,push一般通過按鈕的點選事件或View的tap事件觸發,而pop則可能通過事件觸發,也可能通過右滑手勢觸發。因此,我們將這個我們要實現的動畫效果分為互動效果和無互動效果兩種,下面我們將使用方法2和方法3提供的思路,分別實現這兩種效果,這樣就能較為完美的解決Push和Pop的動畫問題。

實現互動動畫效果

準備需要使用的陣列及手勢
#define ScreenWidth [UIScreen mainScreen].bounds.size.width
#define ScreenHeight [UIScreen mainScreen].bounds.size.height
@interface LTNavigationController ()<UIGestureRecognizerDelegate>
@property(strong,nonatomic)UIImageView * screenshotImgView;
@property(strong,nonatomic)UIView * coverView;
@property(strong,nonatomic)NSMutableArray * screenshotImgs;
@property(strong,nonatomic)UIPanGestureRecognizer *panGestureRec;
@end

@implementation LTNavigationController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.

// 1,建立Pan手勢識別器,並繫結監聽方法
_panGestureRec = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureRec:)];
_panGestureRec.edges = UIRectEdgeLeft;
// 為導航控制器的view新增Pan手勢識別器
[self.view addGestureRecognizer:_panGestureRec];

// 2.建立截圖的ImageView
_screenshotImgView = [[UIImageView alloc] init];
// app的frame是包括了狀態列高度的frame
_screenshotImgView.frame = CGRectMake(0, 0, ScreenWidth, ScreenHeight);


// 3.建立截圖上面的黑色半透明遮罩
_coverView = [[UIView alloc] init];
// 遮罩的frame就是截圖的frame
_coverView.frame = _screenshotImgView.frame;
// 遮罩為黑色
_coverView.backgroundColor = [UIColor blackColor];

// 4.存放所有的截圖陣列初始化
_screenshotImgs = [NSMutableArray array];
}
實現手勢的相應事件
// 響應手勢的方法
- (void)panGestureRec:(UIPanGestureRecognizer *)panGestureRec
{

// 如果當前顯示的控制器已經是根控制器了,不需要做任何切換動畫,直接返回
if(self.visibleViewController == self.viewControllers[0]) return;
// 判斷pan手勢的各個階段
switch (panGestureRec.state) {
    case UIGestureRecognizerStateBegan:
        // 開始拖拽階段
        [self dragBegin];
        break;

    case UIGestureRecognizerStateEnded:
        // 結束拖拽階段
        [self dragEnd];
        break;

    default:
        // 正在拖拽階段
        [self dragging:panGestureRec];
        break;
}
}

#pragma mark 開始拖動,新增圖片和遮罩
- (void)dragBegin
{
// 重點,每次開始Pan手勢時,都要新增截圖imageview 和 遮蓋cover到window中
[self.view.window insertSubview:_screenshotImgView atIndex:0];
[self.view.window insertSubview:_coverView aboveSubview:_screenshotImgView];

// 並且,讓imgView顯示截圖陣列中的最後(最新)一張截圖
_screenshotImgView.image = [_screenshotImgs lastObject];
//_screenshotImgView.transform = CGAffineTransformMakeTranslation(ScreenWidth, 0);
}

// 預設的將要變透明的遮罩的初始透明度(全黑)
#define kDefaultAlpha 0.6

// 當拖動的距離,佔了螢幕的總寬高的3/4時, 就讓imageview完全顯示,遮蓋完全消失
#define kTargetTranslateScale 0.75
#pragma mark 正在拖動,動畫效果的精髓,進行位移和透明度變化
- (void)dragging:(UIPanGestureRecognizer *)pan
{

// 得到手指拖動的位移
CGFloat offsetX = [pan translationInView:self.view].x;

// 讓整個view都平移     // 挪動整個導航view
if (offsetX > 0) {
    self.view.transform = CGAffineTransformMakeTranslation(offsetX, 0);
  }


// 計算目前手指拖動位移佔螢幕總的寬高的比例,當這個比例達到3/4時, 就讓imageview完全顯示,遮蓋完全消失
double currentTranslateScaleX = offsetX/self.view.frame.size.width;

if (offsetX < ScreenWidth) {

    _screenshotImgView.transform = CGAffineTransformMakeTranslation((offsetX - ScreenWidth) * 0.6, 0);
}

// 讓遮蓋透明度改變,直到減為0,讓遮罩完全透明,預設的比例-(當前平衡比例/目標平衡比例)*預設的比例
double alpha = kDefaultAlpha - (currentTranslateScaleX/kTargetTranslateScale) * kDefaultAlpha;
_coverView.alpha = alpha;
}

#pragma mark 結束拖動,判斷結束時拖動的距離作相應的處理,並將圖片和遮罩從父控制元件上移除
- (void)dragEnd
{
// 取出挪動的距離
CGFloat translateX = self.view.transform.tx;
// 取出寬度
CGFloat width = self.view.frame.size.width;

if (translateX <= 40) {
    // 如果手指移動的距離還不到螢幕的一半,往左邊挪 (彈回)
    [UIView animateWithDuration:0.3 animations:^{
        // 重要~~讓被右移的view彈回歸位,只要清空transform即可辦到
        self.view.transform = CGAffineTransformIdentity;
        // 讓imageView大小恢復預設的translation
        _screenshotImgView.transform = CGAffineTransformMakeTranslation(-ScreenWidth, 0);
        // 讓遮蓋的透明度恢復預設的alpha 1.0
        _coverView.alpha = kDefaultAlpha;
    } completion:^(BOOL finished) {
        // 重要,動畫完成之後,每次都要記得 移除兩個view,下次開始拖動時,再新增進來
        [_screenshotImgView removeFromSuperview];
        [_coverView removeFromSuperview];
    }];
} else {
    // 如果手指移動的距離還超過了螢幕的一半,往右邊挪
    [UIView animateWithDuration:0.3 animations:^{
        // 讓被右移的view完全挪到螢幕的最右邊,結束之後,還要記得清空view的transform
        self.view.transform = CGAffineTransformMakeTranslation(width, 0);
        // 讓imageView位移還原
        _screenshotImgView.transform = CGAffineTransformMakeTranslation(0, 0);
        // 讓遮蓋alpha變為0,變得完全透明
        _coverView.alpha = 0;
    } completion:^(BOOL finished) {
        // 重要~~讓被右移的view完全挪到螢幕的最右邊,結束之後,還要記得清空view的transform,不然下次再次開始drag時會出問題,因為view的transform沒有歸零
        self.view.transform = CGAffineTransformIdentity;
        // 移除兩個view,下次開始拖動時,再加回來
        [_screenshotImgView removeFromSuperview];
        [_coverView removeFromSuperview];

        // 執行正常的Pop操作:移除棧頂控制器,讓真正的前一個控制器成為導航控制器的棧頂控制器
        [self popViewControllerAnimated:NO];
    }];
}

}

實現截圖儲存功能,並在Push前截圖
- (void)screenShot
{
// 將要被截圖的view,即視窗的根控制器的view
UIViewController *beyondVC = self.view.window.rootViewController;
// 背景圖片 總的大小
CGSize size = beyondVC.view.frame.size;
// 開啟上下文,使用引數之後,截出來的是原圖(YES  0.0 質量高)
UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);
// 要裁剪的矩形範圍
CGRect rect = CGRectMake(0, 0, ScreenWidth, ScreenHeight);
//注:iOS7以後renderInContext:由drawViewHierarchyInRect:afterScreenUpdates:替代
[beyondVC.view drawViewHierarchyInRect:rect  afterScreenUpdates:NO];
// 從上下文中,取出UIImage
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
// 新增擷取好的圖片到圖片陣列
if (snapshot) {
    [_screenshotImgs addObject:snapshot];
}
// 千萬記得,結束上下文(移除棧頂的基於當前點陣圖的圖形上下文)
UIGraphicsEndImageContext();
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
  //有在導航控制器裡面有子控制器的時候才需要截圖
if (self.viewControllers.count >= 1) {
    // 呼叫自定義方法,使用上下文截圖
    [self screenShot];
}
// 截圖完畢之後,才呼叫父類的push方法
[super pushViewController:viewController animated:YES];
}
重寫常用的pop方法

在一開始基本原理地方,我們說過pop時要刪除最後一張截圖,用來保證陣列中的最後一張截圖是上一個控制器,但是很多情況下我們可能呼叫的是導航欄的popToViewController: animated:方法或popToRootViewControllerAnimated:來返回,這種情況下,我們刪除的可能就不是一張截圖,因此我們需要分別重寫這些Pop方法,去確定我們要刪除多少張圖片,程式碼如下

- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
   [_screenshotImgs removeLastObject];
   return [super popViewControllerAnimated:animated];
}
- (NSArray<UIViewController *> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
{
for (NSInteger i = self.viewControllers.count - 1; i > 0; i--) {
    if (viewController == self.viewControllers[i]) {
        break;
    }
    [_screenshotImgs removeLastObject];
}

return [super popToViewController:viewController animated:animated];
}
- (NSArray<UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated
{
[_screenshotImgs removeAllObjects];
return [super popToRootViewControllerAnimated:animated];
}
※在指定的控制器遮蔽手勢

在上面程式碼中,我們使用的是側滑手勢,並將相應區域設定為螢幕左側。
之所以不用全屏滑動,是因為全屏滑動手勢在有些時候會和其他手勢衝突,如果衝突的是我們自定義的手勢,自然好解決,但如果是系統手勢,如TableView的左滑選單操作,這個事情就很蛋疼的。
但是如果必須要做全屏滑動手勢的話,我們可以對程式碼稍作修改,某些控制器中遮蔽手勢。

首先給導航欄新增禁用名單陣列並配置

...
@property(nonatomic,copy)NSArray * forbiddenArray;
...
- (void)viewDidLoad {
[super viewDidLoad];
//原來程式碼
...
  //將手勢禁用,之後在Push時根據條件開啟
 self.panGestureRec.enabled = enable
//將需要禁用手勢的控制器的類名加到這個陣列
self.forbiddenArray = @[@"SCViewController",@"ManageAddressViewController"];
}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{

//在指定控制器中禁用手勢  解決滑動返回手勢和某些手勢衝突問題
BOOL enable = YES;
for (NSString * string in self.forbiddenArray) {
    NSString * className = NSStringFromClass([viewController class]);
    if ([string isEqualToString:className]) {
        enable = NO;
    }
}
self.panGestureRec.enabled = enable;

//原有程式碼
...
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
NSInteger count = self.viewControllers.count;
NSString * className = nil;
if (count >= 2) {
    className = NSStringFromClass([self.viewControllers[count -2] class]);
}

BOOL enable = YES;
for (NSString * string in self.forbiddenArray) {
    if ([string isEqualToString:className]) {
        enable = NO;
    }
}
self.panGestureRec.enabled = enable;
//原有程式碼
...

return [super popViewControllerAnimated:animated];
}

相關推薦

iOS我們一次性解決導航所有問題

前一段時間換了工作,公司專案趕得比較緊,沒有時間更新文章,現在閒下來了,趕緊寫一篇來彌補自己的羞愧。 今天我們來重點討論導航欄返回的問題,包括各種問題的解決方案。 系統預設導航欄的返回按鈕和返回方式 在預設情況下,導航欄返回按鈕長這個樣子 導航欄預設返回按鈕

ios 10 開發-我們一次性解決導航所有問題

前言 今天我們來重點討論導航欄返回的問題,包括各種問題的解決方案。 系統預設導航欄的返回按鈕和返回方式 在預設情況下,導航欄返回按鈕長這個樣子 導航欄預設返回按鈕 導航欄左上角的返回按鈕,其文字預設為上一個ViewController的標題,如果上一個ViewContr

iOSNSLog列印字典顯示得更好看(解決中文亂碼並顯示成JSON格式)

前言 文章的初衷很簡單,是為了能夠正常顯示打印出字典裡面的中文。因為預設情況下,直接列印字典的話,在Xcode控制檯上,中文會是亂碼的,需要Unicode轉碼才能看到中文。 比如列印下面的一個字典 NSDictionary *dict = @{

SqlSugar ORM 入門篇2 查詢 我們實現零SQL

SqlSugar在查詢的功能是非常強大的,多表查詢、分頁查詢 、 一對一查詢、二級快取、一對多查、WhenCase等複雜函式、Mapper功能、和拉姆達自定義擴充套件等,用好了是可以做到真正零SQL的一款ORM。   首先將SqlSugar更新到4.8版本,下面我就來一一講解每種查

SqlSugar入門篇2 查詢 我們實現零SQL

SqlSugar在查詢的功能是非常強大的,多表查詢、分頁查詢 、 一對一查詢、一對多查、WhenCase等複雜函式、Mapper功能、和拉姆達自定義擴充套件等,用好了是可以做到真正零SQL的一款ORM。 首先將SqlSugar更新到4.8版本,下面我就來一一講解每種查詢的寫法 建立DbContex

實戰乾貨透明狀態列和導航的終極解決方案

本文主要分享了透明狀態列和導航欄的終極解決方案。以後這類問題,一文搞定 這個技術痛點遇到過的都懂~本文幫你徹底解決這個痛點~還不快收藏   背景   在我做 Android 開發之前,我就發現有些 App 的狀態列和導航欄有透明效果,或者是沉浸式效果,比如說酷

洛谷P2420我們異或吧DFS

題目大意: 思路: 首先,這到題是我在想刷LCALCALCA的時候遇到的,結果這道標籤是LCALCALCA的題目我卻想不到為什們要用LCALCALCA,一個DFSDFSDFS就可以過。 其實要求兩個

朱智文的專欄我們一起放逐我們的青春

專欄達人 授予成功建立個人部落格專欄

AndroidHeaderView也參與回收機制,自我感覺是優雅的為 RecyclerView 新增 HeaderView (FooterView)的解決方案

本文站在巨人的肩膀上 自我感覺又進了一步而成。基於翔神的大作基礎之上寫的一個為RecyclerView新增HeaderView FooterView 的另一種解決方案, 翔神連結文首鎮樓:http://blog.csdn.net/lmj623565791/article/de

IOS無法識別類別拓展方法unrecognized selector sent to instance的解決方法

有時在開發中會發現無法識別拓展類別的新增方法,總是識別原檔案的方法,而原檔案中是沒有新添方法的,於是在動態呼叫拓展方法時丟出了unrecognized selector sent to instance的錯誤。 解決方法是在Xcode的Build Settings下Othe

javascript面向物件之路我們一起來坦克大戰吧01

提問 不知道大家發現沒有,執行時候瀏覽器或者電腦會變得很卡哦。根據我們之前的學習,你知道是什麼原因導致的嗎? 若是各位有興趣,請你回答卡的原因,並提出優化方案。  前言 PS 各位要看效果還是使用ff或者google吧,ie7以下好像有問題。 最近大家都在坦克大戰,

iOS高德地圖MAMapKit的使用:地圖顯示、新增大頭針、導航、定位功能介紹

4、 引入高德地圖依賴系統庫檔案: 說明: 1.備註中,2D表示使用2D柵格地圖需要的系統檔案,3D表示使用3D向量地圖需要的系統檔案、Search表示使用搜索庫需要的系統檔案,3D(V3.X.X)表示3D向量地圖V3.0.0以後版本需要新增的庫。 2.SystemConfiguration.f

IOS異常捕獲 拒絕閃退 應用從容的崩潰 UncaughtExceptionHandler

mode tis ring enc 原因 include equal ipad ani 盡管大家都不願意看到程序崩潰,但可能崩潰是每一個應用必須面對的現實。既然崩潰已經發生。無法

JavaScript事件支持先發布後訂閱

class 問題 想要 png trigger 很快 9.png area ++ 之前寫過一個的事件管理器,就是普通的先訂閱後發布模式。但實際場景中我們需要做到後訂閱的也能收到發布的消息。比如我們關註微信公眾號,還是能看到歷史消息的。類似於qq離線消息,我先發給你,你登錄了

拿來主義我們談WEB緩存的時候,我們在談些什麽?

1.0 服務器端 過程 用戶 重定向 new nac java style 第一部分 Web緩存是什麽 場景1:測試妹子測功能時會說為什麽我的瀏覽器的顯示亂七八糟,我的界面怎麽跟別人瀏覽器上不一致?旁邊的人會提醒說:清下緩存試試。 場景2:開發改了代碼,上了環境,發現不

iOS代理傳值與塊代碼傳值

ring 方法 nslog 設置代理 轉載 adf delegate alloc 代理傳值 主線程與子線程常常須要進行數據的傳遞。不同的類之間,不同的控制器之間都須要。 並且常常須要監聽一個動作的完畢。而後才去做對應事件。(代理是一對一的關系)。 一、代理傳值 代理

iOSUICollectionView自己定義Layout之蜂窩布局

with top http reserve src 布局 step object .com 網上的UICollectionView的Layout布局,其cell的形狀多為矩形和圓形。 本篇博文將正六邊形作為cell的基本形狀,為您展現獨特的蜂窩布局效果及實現源代碼。 幫

iOSUIDynamicAnimator動畫

set translate logs enc ica cgpoint isp mat .cn 創建動畫 1 UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:sel

Git常見錯誤提示解決辦法和常用方法

log image 解決 遠程 添加 logs 遠程倉庫 錯誤 ima 1.添加遠程倉庫時提示fatal: remote origin already exists. 先刪除遠程倉庫,再添加遠程倉庫 最後再push 2.修改本地文件(比如README.md)後,更新到g

你不再害怕指針——C指針詳解(經典,非常詳細)

有一個 情況 value 第一個字符 接下來 意思 strcpy abcdefg 數值 前言:復雜類型說明 要了解指針,多多少少會出現一些比較復雜的類型,所以我先介紹一下如何完全理解一個復雜類型,要理解復雜類型其實很簡單,一個類型裏會出現很多運算符,他們也像普通的表