iOS的各種奇淫怪技, 不定時(shí)更新

本文僅記錄日常iOS開(kāi)發(fā)的一些技巧, 不定時(shí)更新, 最新的置頂, 歡迎關(guān)注打賞訂閱

5.記錄百度地圖的一個(gè)bug:

百度地圖刷新頁(yè)面標(biāo)注點(diǎn)的時(shí)候, 首先需要?jiǎng)h除所有標(biāo)注,如下

//刪除所有標(biāo)注
    NSArray *arrayAnmation=[[NSArray alloc] initWithArray:_mapView.annotations];
    [_mapView removeAnnotations:arrayAnmation];

然后添加新的標(biāo)注:

 [_mapView addAnnotation:point];

添加完標(biāo)注之后, 就會(huì)響應(yīng)下面的回調(diào):

- (BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id<BMKAnnotation>)annotation{

      BMKAnnotationView* view = nil;
      view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"start_node"];
}

問(wèn)題就出在上面的view, 調(diào)用復(fù)用之后, 心想已經(jīng)刪除所有標(biāo)注了, 調(diào)用[mapview dequeueReusableAnnotationViewWithIdentifier:@"start_node"];后應(yīng)該返回nil, 但在添加第一個(gè)標(biāo)注點(diǎn)的時(shí)候, 發(fā)現(xiàn)會(huì)有返回值, 而不是空. 除了第一個(gè)標(biāo)注點(diǎn)之外, 卻不會(huì)有返回值, 于是加入下面的代碼:

- (BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id<BMKAnnotation>)annotation{

      BMKAnnotationView* view = nil;
      view = [mapview dequeueReusableAnnotationViewWithIdentifier:@"start_node"];
if (view == nil) {
                
                view = [[BMKAnnotationView alloc]initWithAnnotation:routeAnnotation reuseIdentifier:@"start_node"];
}else{
              view = nil;
              view = [[BMKAnnotationView alloc]initWithAnnotation:routeAnnotation reuseIdentifier:@"start_node"];
}
return view;
}

總結(jié): 刪除標(biāo)注點(diǎn)后, 內(nèi)存還會(huì)有一份復(fù)用的view保存著, UITableView的復(fù)用機(jī)制應(yīng)該也是這樣的. 所以即使刪除了所有復(fù)用視圖, 還是要判空一下.

4.有些場(chǎng)景下, 我們使用Masonry進(jìn)行自動(dòng)布局時(shí), 會(huì)獲取不到frame或者bounds

例如當(dāng)想設(shè)置圖片只有兩個(gè)圓角的時(shí)候, 或者設(shè)置控件需要取得frame的時(shí)候, 會(huì)發(fā)現(xiàn)frame或者bounds都為0, 即這樣設(shè)置圓角時(shí):

_thumbView = [[UIImageView alloc] init];
    [self.contentView addSubview:_thumbView];
    [_thumbView mas_makeConstraints:^(MASConstraintMaker *make) {
       
        make.left.equalTo(_bubbleView.mas_left);
        make.size.mas_equalTo(CGSizeMake(kGridViewHeight, kGridViewHeight ));
        make.top.equalTo(_bubbleView.mas_top);
    }];
    
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:_thumbView.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomLeft cornerRadii:CGSizeMake(6, 6)];

    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = _thumbView.bounds;
    maskLayer.path = maskPath.CGPath;
    _thumbView.layer.mask = maskLayer;

圖片直接就不顯示了, 打個(gè)斷點(diǎn)在maskLayer.frame = _thumbView.bounds;的前面, 會(huì)發(fā)現(xiàn)_thumbView.bounds全部為0.于是搜尋各種方法, 了解到了下面這些方法:

setNeedsLayout:告知頁(yè)面需要更新,但是不會(huì)立刻開(kāi)始更新。執(zhí)行后會(huì)立刻調(diào)用layoutSubviews。
layoutIfNeeded:告知頁(yè)面布局立刻更新。所以一般都會(huì)和setNeedsLayout一起使用。如果希望立刻生成新的frame需要調(diào)用此方法,利用這點(diǎn)一般布局動(dòng)畫(huà)可以在更新布局后直接使用這個(gè)方法讓動(dòng)畫(huà)生效。
layoutSubviews:系統(tǒng)重寫(xiě)布局
setNeedsUpdateConstraints:告知需要更新約束,但是不會(huì)立刻開(kāi)始
updateConstraintsIfNeeded:告知立刻更新約束
updateConstraints:系統(tǒng)更新約束

而我們就可以在layoutIfNeeded里面獲取到空間的frame, 直接貼代碼:

- (void)layoutIfNeeded{

    [super layoutIfNeeded];
    
     UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:_thumbView.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomLeft cornerRadii:CGSizeMake(6, 6)];
     
     CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
     maskLayer.frame = _thumbView.bounds;//這里就可以獲取到我們想要的frame啦
     maskLayer.path = maskPath.CGPath;
     _thumbView.layer.mask = maskLayer;
}

效果如圖:


END

3.實(shí)現(xiàn)UITableView頭部彈彈的動(dòng)畫(huà)效果,

先上效果圖

彈彈的效果.gif

其實(shí)就是檢測(cè)UIScrollView(當(dāng)然了,UITableView也是UIScrollView)滾動(dòng)到某個(gè)位置, 如果小于某個(gè)位置回彈, 大于某個(gè)位置展開(kāi), 當(dāng)然也可以換作自己的動(dòng)畫(huà)邏輯, 然后讓UIScrollView繼續(xù)平滑滾動(dòng), 滾動(dòng)到固定的位置, 使其定住. 注意這個(gè)平滑的要求, 有種方案就是設(shè)置UIScrollView的contentoff, 但這種方案是一下子滾動(dòng)到指定位置, 而不是平滑. 于是我們搜尋一下UIScrollView的代理方法:

@protocol UIScrollViewDelegate<NSObject>

@optional

- (void)scrollViewDidScroll:(UIScrollView *)scrollView;                                               // any offset changes
- (void)scrollViewDidZoom:(UIScrollView *)scrollView NS_AVAILABLE_IOS(3_2); // any zoom scale changes

// called on start of dragging (may require some time and or distance to move)
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
// called on finger up if the user dragged. velocity is in points/millisecond. targetContentOffset may be changed to adjust where the scroll view comes to rest
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset NS_AVAILABLE_IOS(5_0);
// called on finger up if the user dragged. decelerate is true if it will continue moving afterwards
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView;   // called on finger up as we are moving
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;      // called when scroll view grinds to a halt

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView; // called when setContentOffset/scrollRectVisible:animated: finishes. not called if not animating

- (nullable UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;     // return a view that will be scaled. if delegate returns nil, nothing happens
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view NS_AVAILABLE_IOS(3_2); // called before the scroll view begins zooming its content
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale; // scale between minimum and maximum. called after any 'bounce' animations

- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView;   // return a yes if you want to scroll to the top. if not defined, assumes YES
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView;      // called when scrolling animation finished. may be called immediately if already at top

@end

里面有個(gè)方法我們比較感興趣, 但平日里比較少用, 就是這個(gè):

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset;
// called on finger up if the user dragged. decelerate is true if it will continue moving afterwards

注釋里告訴我們, 滾動(dòng)完之后, 可以繼續(xù)移動(dòng). 而targetContentOffset就是我們想要的目標(biāo)偏移量, 里面的(inout CGPoint *)回調(diào)也比較奇怪, 字面意思大概就是可以當(dāng)作輸入輸出, 應(yīng)該是當(dāng)作一個(gè)指針當(dāng)作回傳, 告訴UIScrollView將要移動(dòng)到哪, 我們可以在targetContentOffset設(shè)置輸入量, 如下:

targetContentOffset->x = 0;
targetContentOffset->y = -104;

當(dāng)然這種設(shè)置并不完美, 在一些奇葩的拖動(dòng)會(huì)有鬼畜的效果, 于是我們對(duì)速度加入了約束:

//也就是速度為0 之后再做滾動(dòng)
 if (velocity.y == 0) {
        
        if ((-newY) > 100 && (-(newY) + 64) < self.headerView.sectionHeaderViewOrinY) {
            
            targetContentOffset->x = 0;
            targetContentOffset->y = -104;
            
        }
     
    }

于是就可以作出開(kāi)頭顯示的動(dòng)畫(huà)了.END

2.實(shí)現(xiàn)UITextField回刪按鈕的回調(diào)檢測(cè)

UITextField的代理事件只有下面幾種:

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField; 
- (void)textFieldDidBeginEditing:(UITextField *)textField;   
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField; 
- (void)textFieldDidEndEditing:(UITextField *)textField;  
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;   
- (BOOL)textFieldShouldClear:(UITextField *)textField;  
- (BOOL)textFieldShouldReturn:(UITextField *)textField;  

想要檢測(cè)回刪按鈕的事件時(shí), 發(fā)現(xiàn)沒(méi)有回調(diào)事件,看來(lái)需要我們自己來(lái)實(shí)現(xiàn)UITextField回刪按鈕的回調(diào).
首先點(diǎn)進(jìn)UITextField的實(shí)現(xiàn)

NS_CLASS_AVAILABLE_IOS(2_0) @interface UITextField : UIControl <UITextInput, NSCoding, UIContentSizeCategoryAdjusting>

實(shí)現(xiàn)了UITextInput的協(xié)議, 再點(diǎn)進(jìn)UITextInput的協(xié)議看看

protocol UITextInput <UIKeyInput>
@required

/* Methods for manipulating text. */
- (nullable NSString *)textInRange:(UITextRange *)range;
- (void)replaceRange:(UITextRange *)range withText:(NSString *)text;

里面的幾個(gè)方法沒(méi)有我們感興趣的, 繼續(xù)點(diǎn)進(jìn)UIKeyInput,可以看到

@protocol UIKeyInput <UITextInputTraits>

#if UIKIT_DEFINE_AS_PROPERTIES
@property(nonatomic, readonly) BOOL hasText;
#else
- (BOOL)hasText;
#endif
- (void)insertText:(NSString *)text;
- (void)deleteBackward;
@end

好了, 我們找到了- (void)deleteBackward;就是隱藏的刪除回調(diào)方法, 只是沒(méi)寫(xiě)在UITextField的回調(diào)方法里面而已. 因此我們可以寫(xiě)一個(gè)繼承UITextField的自定義類(lèi), 如下:

#import <UIKit/UIKit.h>

@protocol KeyboardInputDelegate <NSObject>

@optional
//我們自己定義的一個(gè)回刪按鈕的回調(diào)方法, 順便傳回UITextField
- (void)deleteBackword:(UITextField *)field;

@end

@interface DeleteDetectionTextField : UITextField

@property (nonatomic, weak) id<KeyboardInputDelegate> KeyboardInputDelegate;

@end

然后重寫(xiě)一下- (void)deleteBackward;方法

//這個(gè)就是被隱藏的回刪方法, 咱們覆寫(xiě)一下
- (void)deleteBackward{

    [super deleteBackward];
    
    if (self.KeyboardInputDelegate && [self.KeyboardInputDelegate respondsToSelector:@selector(deleteBackword:)]) {
//這句話會(huì)不會(huì)引發(fā)循環(huán)引用?
        [self.KeyboardInputDelegate deleteBackword:self];
    }
}

然后我們調(diào)用自定義的DeleteDetectionTextField使用時(shí), 記得設(shè)置一下代理:

 inputField.delegate = self;
 inputField.KeyboardInputDelegate = self;

實(shí)現(xiàn)回調(diào):

#pragma mark - 回刪按鈕的回調(diào)
- (void)deleteBackword:(UITextField *)field{
    //這里就可以干羞羞的事情
}

完成

1.xib拖的控件不能用代碼作動(dòng)畫(huà),用代碼作動(dòng)畫(huà)會(huì)無(wú)端出現(xiàn)很多坑. 因此xib比較適合靜態(tài)視圖的搭建,一些動(dòng)畫(huà)視圖還是使用純代碼比較合適些

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,346評(píng)論 4 61
  • 一覺(jué)醒來(lái),我竟然變成了一只小綿羊 我叫高陽(yáng),我很早就發(fā)育了合適的身高,也長(zhǎng)了合適的臉,所以,初中時(shí)候我就找了同班最...
    編劇王安鵬閱讀 874評(píng)論 0 1
  • 稻田里的人踩著失望入冬 黃昏把西河的云彩染紅 大雁帶走南方的熱情 十月 緬懷的人今已故去疆土 落霞里掙扎著歸家的...
    寧谷閱讀 1,127評(píng)論 3 4
  • 你要對(duì)得起自己&陌仔 如果有一天,你閉著眼睛都睡不著,和睡神大戰(zhàn)了三百回合后,依舊睜著眼睛望向天花板,你會(huì)想些什么...
    陌籽閱讀 248評(píng)論 0 1

友情鏈接更多精彩內(nèi)容