iOS開發(fā)必備干貨

把自己以往做的項目中的一些小功能摳出來寫在這里陕见,供大家使用,會一直持續(xù)更新

UITextView自適應(yīng)高度

1.KVO

//靜態(tài)變量的地址可以保證context的獨(dú)一無二
static void * abc = &abc;

- (void)viewDidLoad {
    [super viewDidLoad];
    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(20, 20, 200, 35)];
    [self.view addSubview:textView];
    textView.layer.borderColor = [UIColor lightGrayColor].CGColor;
    textView.layer.borderWidth = 1;
    textView.layer.cornerRadius = 5;
    textView.font = [UIFont systemFontOfSize:20];

    //為textView添加一個觀察者,來觀察他的contentSize屬性的變化
    //context:上下文. 對某一個屬性或者方法之類的 跨方法使用時的唯一標(biāo)識
    [textView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:abc];
}

//當(dāng)前對象 如果觀察到了 某些變化時就會觸發(fā)下方的方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    //使用context作為判斷依據(jù)  盡量不要使用keyPath
    if (context == abc) {
        NSLog(@"%@", change);
        CGSize size = [change[@"new"] CGSizeValue];
        UITextView *textView = (UITextView *)object;
        CGRect frame = textView.frame;
        //限制高度 根據(jù)打印局雄,5行時 高度136 高度未變化也不更新布局
        if (size.height > 140 || size.height == frame.size.height) {
            return;
        }
        frame.size.height = size.height;
        textView.frame = frame;
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

2.sizeThatFits:

//文本更改時觸發(fā)高度自適應(yīng)
- (void)textViewDidChange:(UITextView *)textView {    

    //根據(jù)文本調(diào)整textView的高度
    CGSize sizeToFit = [textView sizeThatFits:CGSizeMake(textView.frame.size.width-16, MAXFLOAT)];
    
    //判斷高度是否變化,未變化不重新布局
    if (sizeToFit.height != textView.frame.size.height) {
        textView.frame = CGRectMake(textView.frame.origin.x, textView.frame.origin.y, textView.frame.size.width, sizeToFit.height);
    }
}

實現(xiàn)二維碼掃描的空心遮罩

cropRect為掃描二維碼框的Rect

1.CoreGraphics -> CGPath

- (void)setCropRect:(CGRect)cropRect{
    CAShapeLayer *cropLayer = [[CAShapeLayer alloc] init];    
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, nil, cropRect);
    CGPathAddRect(path, nil, self.view.bounds);
    
    [cropLayer setFillRule:kCAFillRuleEvenOdd];//生成空心遮罩
    [cropLayer setPath:path];
    CFRelease(path);//CF對象記得手動釋放
    [cropLayer setFillColor:[UIColor blackColor].CGColor];
    [cropLayer setOpacity:0.3];
    
    [cropLayer setNeedsDisplay];
    
    [self.view.layer addSublayer:cropLayer];
}

2.UIKit -> UIBezierPath

- (void)setCropRect:(CGRect)cropRect{
    CAShapeLayer *cropLayer = [[CAShapeLayer alloc] init];
    
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.view.bounds cornerRadius:0];
    UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:cropRect];
    [path appendPath:rectPath];

    [cropLayer setFillRule:kCAFillRuleEvenOdd];//生成空心遮罩
    [cropLayer setPath:path.CGPath];
    [cropLayer setFillColor:[UIColor blackColor].CGColor];
    [cropLayer setOpacity:0.3];
    
    [cropLayer setNeedsDisplay];
    
    [self.view.layer addSublayer:cropLayer];
}
空心遮罩.PNG

UIImageView的手勢縮放

通過UIScrollViewDelegate中的代理方法來對UIImageView實現(xiàn)簡單的縮放功能
附加手勢實現(xiàn):
縮放比例為1(未進(jìn)行縮放)時存炮,放大倍數(shù)
縮放比例大于1(進(jìn)行過縮放)時炬搭,還原倍數(shù)

_scrollView.minimumZoomScale = 1.0; // 最小縮放比例 
_scrollView.maximumZoomScale = 6.0; // 最大縮放比例
_imageView.userInteractionEnabled = YES; // 記得打開UIImageView的用戶交互,否則手勢無法觸發(fā)
#pragma mark - UIScrollView Delegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return _imageView; // 指定需要縮放的view穆桂,否則無法實現(xiàn)縮放功能(可為多個view)
}
#pragma mark - GestureRecogizer Method
- (void)addGestureRecognizer {
    // 雙擊的 Recognizer
    UITapGestureRecognizer* doubleRecognizer;
    doubleRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap:)];
    doubleRecognizer.numberOfTapsRequired = 2; // 雙擊
    // _imageView添加雙擊手勢
    [_imageView addGestureRecognizer:doubleRecognizer];
}

-(void)doubleTap:(UITapGestureRecognizer*)recognizer
{
    if (_scrollView.zoomScale > 1.0) {//放大過 --> 還原大小
        [UIView animateWithDuration:.2 animations:^{
            _scrollView.zoomScale = 1.0;
        }];
    } else {//未放大 --> 放大2倍
        [UIView animateWithDuration:.2 animations:^{
            _scrollView.zoomScale = 2.0;
        }];
    }
}
手勢縮放.gif

JSON與JSON字符串相互轉(zhuǎn)換

1.JSON轉(zhuǎn)JSON字符串
思路:NSDictionary/NSArray -> NSData -> NSString

方法:

+ (NSString *)parseJSONToJSONString:(id)JSON {

    NSError *error;

    NSData *JSONData = [NSJSONSerialization dataWithJSONObject:JSON options:NSJSONWritingPrettyPrinted error:&error];

    NSString *JSONString = [[NSString alloc] initWithData:JSONData encoding:NSUTF8StringEncoding];

    return JSONString;

}

2.JSON字符串轉(zhuǎn)JSON
思路:NSString -> NSData -> NSDictionary/NSArray

方法:

+ (id)parseJSONStringToJSON:(NSString *)JSONString {

    NSData *JSONData = [JSONString dataUsingEncoding:NSUTF8StringEncoding];

    id JSON = [NSJSONSerialization JSONObjectWithData:JSONData options:NSJSONReadingMutableLeaves error:nil];

    return JSON;
}

備注:
1.NSJSONReadingOptions

typedef NS_OPTIONS(NSUInteger, NSJSONReadingOptions) {
    NSJSONReadingMutableContainers = (1UL << 0),
    NSJSONReadingMutableLeaves = (1UL << 1),
    NSJSONReadingAllowFragments = (1UL << 2)
} API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
NSJSONReadingMutableContainers // 返回可變?nèi)萜鞴琋SMutableDictionary或NSMutableArray
NSJSONReadingMutableLeaves // 不僅返回的最外層是可變的, 內(nèi)部的子數(shù)值或字典也是可變對象
NSJSONReadingAllowFragments // 返回允許JSON字符串最外層既不是NSArray也不是NSDictionary,但必須是有效的JSON Fragment.

2.NSJSONWritingOptions

typedef NS_OPTIONS(NSUInteger, NSJSONWritingOptions) {
    NSJSONWritingPrettyPrinted = (1UL << 0),
    NSJSONWritingSortedKeys API_AVAILABLE(macos(10.13), ios(11.0), watchos(4.0), tvos(11.0)) = (1UL << 1)
} API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
NSJSONWritingPrettyPrinted // 是將生成的json數(shù)據(jù)格式化輸出享完,這樣可讀性高灼芭,不設(shè)置則輸出的json字符串就是一整行
NSJSONWritingSortedKeys // 輸出的json字符串就是一整行 僅支持iOS11以上

利用UIBezierPath繪制三角形

- (void)drawRect:(CGRect)rect {
    
    //繪制路徑
    UIBezierPath *path = [UIBezierPath bezierPath];
    
    //設(shè)置顏色
    [self.triangleColor set];
    
    //-------------繪制三角形------------
    //
    //                 B
    //                /\
    //               /  \
    //              /    \
    //             /__  __\
    //            A        C
    //
    
    //設(shè)置起點 A
    [path moveToPoint:CGPointMake(0, rect.size.height)];
    
    //添加一根線到某個點 B
    [path addLineToPoint:CGPointMake(rect.size.width * 0.5, 0)];
    
    //添加一根線到某個點 C
    [path addLineToPoint:CGPointMake(rect.size.width, rect.size.height)];
    
    //關(guān)閉路徑
    [path closePath];
    
    //填充(會把顏色填充進(jìn)去)
    [path fill];
}

驗證碼倒計時

- (void)theCountdown
{
    __block int timeout = 60;
    __weak __typeof(self)weakSelf = self;

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    //dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, <#intervalInSeconds#> * NSEC_PER_SEC, <#leewayInSeconds#> * NSEC_PER_SEC); intervalInSeconds(倒計時間隔),leeway(期望的延遲時間)
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    //dispatch_source_set_event_handler(dispatch_source_t source, dispatch_block_t _Nullable handler); dispatch_block_t 為block
    dispatch_source_set_event_handler(timer, ^{
        //因為^{}為block,有循環(huán)引用問題般又,所以需要弱引用倒計時按鈕
        if(timeout<=0){
            //倒計時結(jié)束彼绷,關(guān)閉
            dispatch_source_cancel(_timer);
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf.codeBtn setTitle:@"發(fā)送驗證碼" forState:0];
                [weakSelf.codeBtn setTitleColor:[UIColor whiteColor] forState:0];
                [weakSelf.codeBtn setUserInteractionEnabled:YES];
            });
        }else{
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf.codeBtn setTitle:[NSString stringWithFormat:@"%ds",timeout] forState:0];
                [weakSelf.codeBtn setTitleColor:[UIColor whiteColor] forState:0];
                [weakSelf.codeBtn setUserInteractionEnabled:NO];
            });
            timeout--;
        }
    });
    dispatch_resume(timer);
}

驗證碼倒計時.gif

下拉放大

#define headerHeight 200
@property (nonatomic, strong) UIImageView *iconIV;

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.iconIV = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"scrollViewImage"]];
    self.iconIV.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, headerHeight);
    //放大時不改變寬高比例
    self.iconIV.contentMode = UIViewContentModeScaleAspectFill;
    self.iconIV.clipsToBounds = YES;
    /*
     頭部視圖 x y width 都是不能設(shè)置的 而圖片要隨著下拉 調(diào)整y軸 所以圖片不能直接作為頭部視圖
     可以把圖片放到一個view里 view作為頭部 而且view不能剪切子視圖
     */
    UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, headerHeight)];
    headerView.clipsToBounds = NO;//不剪切子視圖超出部分!!!
    [headerView addSubview:self.iconIV];
    self.tableView.tableHeaderView = headerView;
    
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 10;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    cell.textLabel.text = @"這是個下拉放大的Demo!";
    return cell;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGPoint offset = scrollView.contentOffset;
    NSLog(@"%f", offset.y);
    //根據(jù)打印值:向上移動 正數(shù) 向下移動 負(fù)數(shù)
    CGFloat height = headerHeight - offset.y;
    self.iconIV.frame = CGRectMake(0, offset.y, self.view.frame.size.width, height);
}
下拉放大.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巍佑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子寄悯,更是在濱河造成了極大的恐慌萤衰,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猜旬,死亡現(xiàn)場離奇詭異脆栋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)洒擦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門椿争,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人熟嫩,你說我怎么就攤上這事丘薛。” “怎么了邦危?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵洋侨,是天一觀的道長。 經(jīng)常有香客問我倦蚪,道長希坚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任陵且,我火速辦了婚禮裁僧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘慕购。我一直安慰自己聊疲,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布沪悲。 她就那樣靜靜地躺著获洲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪殿如。 梳的紋絲不亂的頭發(fā)上贡珊,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機(jī)與錄音涉馁,去河邊找鬼门岔。 笑死,一個胖子當(dāng)著我的面吹牛烤送,可吹牛的內(nèi)容都是我干的寒随。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼妻往,長吁一口氣:“原來是場噩夢啊……” “哼逢防!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蒲讯,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灰署,沒想到半個月后判帮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡溉箕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年晦墙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肴茄。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡晌畅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寡痰,到底是詐尸還是另有隱情抗楔,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布拦坠,位于F島的核電站连躏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏贞滨。R本人自食惡果不足惜入热,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晓铆。 院中可真熱鬧勺良,春花似錦、人聲如沸骄噪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽链蕊。三九已至尾组,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間示弓,已是汗流浹背讳侨。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留奏属,地道東北人跨跨。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親勇婴。 傳聞我的和親對象是個殘疾皇子忱嘹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359