【iOS】常用技術(shù)點小記2

xcrun: error: unable to find utility "xcodebuild", not a developer tool or in PATH

command+,,選擇Command Line Tools

Workaround for Cocoapods issue #7606

post_install do |installer|
    installer.pods_project.build_configurations.each do |config|
        config.build_settings.delete('CODE_SIGNING_ALLOWED')
        config.build_settings.delete('CODE_SIGNING_REQUIRED')
    end
end

關(guān)于NSArray+NSDictionary數(shù)據(jù)篩選邏輯

// 有如下一個array
NSArray *tempList = @[
                      @{
                          @"id1": 1,
                          @"name1": @"xxx",
                          @"list1": @[
                                  @{
                                      @"id2": 2,
                                      @"name2": @"yyy",
                                      @"list2": @[
                                              @{
                                                  @"id3": 3
                                                  }
                                              ]
                                      }
                                  ]
                          },
                      @{
                          @"id1": 1,
                          @"name1": @"xxx",
                          @"list1": @[
                                  @{
                                      @"id2": 2,
                                      @"name2": @"yyy",
                                      @"list2": @[
                                              @{
                                                  @"id3": 3
                                                  },
                                              @{
                                                  @"id3": 3
                                                  }
                                              ]
                                      }
                                  ]
                          }
                      ];
// 然后要求移除子list為空的元素
// 真正要執(zhí)行remove操作的對象
    NSMutableArray *list = [tempList mutableCopy];
    for (id tempObj1 in tempList) {
        // 真正更新的obj1
        NSMutableDictionary *obj1 = [tempObj1 mutableCopy];
        NSArray *tempList1 = tempObj1[@"list1"];
        // 真正要執(zhí)行remove操作的對象
        NSMutableArray *list1 = [tempList1 mutableCopy];
        for (id obj2 in tempList1) {
            //...類似上面步驟
        }
        // 數(shù)據(jù)更新,如果為空,則移除
        if (list1.count == 0) {
            // 由于list和tempList是copy方式,所以內(nèi)含的object也是copy
            [list removeObject:tempObj1];
        }
        else{
            // 更新list
            obj1[@"list1"] = list;
            list[[list indexOfObject:obj1]];
        }
    }

iPhone全面屏適配問題

// 底部布局邊距
    CGFloat layoutBottom = -20;
// iPhoneX是iOS11以后出現(xiàn)的(不包括越獄情況)
    if (@available(iOS 11.0, *)) {
        CGFloat bottom = UIApplication.sharedApplication.delegate.window.safeAreaInsets.bottom;
        // iPhoneX等
        if (bottom > 0) {
            layoutBottom = -bottom;
        }
    }
    [self.submitBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.mas_equalTo(layoutBottom);
    }];

關(guān)于pod庫如果只支持真機繞開pod lib lint的配置

  1. 命令行gem which cocoapods
  2. 命令行open /Library/Ruby/Gems/2.3.0/gems/cocoapods-1.5.3/lib/cocoapods/validator.rb
  3. when :ios相關(guān)改為command += %w(--help)
    最終效果

關(guān)于多級頁面跳轉(zhuǎn)

注意popToRootpushNewVC的先后順序,否則會出現(xiàn)內(nèi)存泄漏問題

[[self.goMyOrderBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        @strongify(self);
        // 選中我的頁面
        UINavigationController *nav = weakTabBarVC.viewControllers.lastObject;
        [weakTabBarVC setSelectedViewController:nav];
        
        // 跳轉(zhuǎn)到訂單頁面
        UIViewController *vc = [ZSMyOrderModuler getMyOrderViewController];
        vc.hidesBottomBarWhenPushed = YES;
        [nav pushViewController:vc animated:NO];
        
        // 返回首頁
        [self.navigationController popToRootViewControllerAnimated:NO];
    }];

關(guān)于tabbar選中某個頁面

// 盡量用這種方式而不是setSelectedIndex
UINavigationController *myCourseNav = [ZSApp.rootTabBarController.viewControllers objectAtIndex:1];
[ZSApp.rootTabBarController setSelectedViewController:myCourseNav];

-ObjC

這個flag告訴鏈接器把庫中定義的Objective-C類和Category都加載進(jìn)來。這樣編譯之后的app會變大(因為加載了其他的objc代碼進(jìn)來)。但是如果靜態(tài)庫中有類和category的話只有加入這個flag才行。

-all_load

這個flag是專門處理-ObjC的一個bug的。用了-ObjC以后,如果類庫中只有category沒有類的時候這些category還是加載不進(jìn)來。變通方法就是加入-all_load或者-force-load。-all_load會強制鏈接器把目標(biāo)文件都加載進(jìn)來,即使沒有objc代碼。-force_load在xcode3.2后可用。但是-force_load后面必須跟一個只想靜態(tài)庫的路徑。

全局隱藏導(dǎo)航欄返回按鈕文字

[UIBarButtonItem.appearance setBackButtonTitlePositionAdjustment:UIOffsetMake(NSIntegerMin, 0) forBarMetrics:UIBarMetricsDefault];

關(guān)于導(dǎo)航欄的正確隱藏

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    
    [self.navigationController setNavigationBarHidden:YES animated:animated];
}

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    
    [self.navigationController setNavigationBarHidden:NO animated:animated];
}

關(guān)于Xcode10斷點不執(zhí)行/執(zhí)行混亂問題

File > Workspace Settings >設(shè)置

關(guān)于tableView: heightForHeader/FooterInSection:注意

  • 即使你返回了numberOfSections為0,如果你設(shè)置height為0,則height會變成默認(rèn)的38,造成tableView的contentSize為2x38!=0,如果你監(jiān)聽這個contentSize則會出現(xiàn)問題。
  • 所以你的return 0.00001才行。
  • 然后監(jiān)聽contentSize.height需大于1才行。

關(guān)于RACObserve(self.array, count)監(jiān)聽不起作用問題

  • 使用mutableArrayValueForKey才能行
[[self mutableArrayValueForKey:@"array"] addObject:@"1"]

關(guān)于OC中對象地址和對象指針地址

打印對象地址: NSLog(@"%p", obj);
打印對象指針地址: NSLog(@"%x", obj);
判斷兩個對象地址相等: obj1 == obj2即可
判斷字符串相等: [str1 isEqualToString: str2]
注意[obj1 isEqual: obj2]只是比較hash值,不是內(nèi)存地址

關(guān)于Cell上的按鈕rac_signalForControlEvents:UIControlEventTouchUpInside方法多次調(diào)用問題

// 這種方法最靠譜
[cell.btn addTarget:self action:@selector(btn_clicked:) forControlEvents:UIControlEventTouchUpInside];

// 還有一種說是這樣,但親測并不起作用
[[[cell.btn rac_signalForControlEvents:UIControlEventTouchUpInside] takeUntil:cell.rac_prepareForReuseSignal] subscribeNext:^(__kindof UIControl * _Nullable x) {

}];

iOS9及以下UIBarButtonItem設(shè)置問題

// 如果為customView時,則需指定customView的frame,否則顯示不出來

- (UIBarButtonItem *)titleBtnItem{
    if (!_titleBtnItem) {
        UILabel *label = [UILabel new];
        label.font = ZSAppFont.system17.bold;
        label.text = @"學(xué)習(xí)內(nèi)容";
        [label sizeToFit]; // 注意此處
        _titleBtnItem = [[UIBarButtonItem alloc] initWithCustomView:label];
    }
    return _titleBtnItem;
}

關(guān)于CocoaPods中bitcode設(shè)置問題:

  • 如果讓當(dāng)前pod庫bitcode為NO,則在.podspec文件中設(shè)置如下:
s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO' }
  • 如果使工程中的所有pod庫bitcode都為NO,則在Podfile文件中設(shè)置如下:
post_install do |installer|
    installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
            config.build_settings['ENABLE_BITCODE'] = 'NO'
        end
    end
end

UIProgressView的兩種樣式

UIProgressViewStyleBar //沒有圓角
UIProgressViewStyleDefault //有圓角

// 如果改變圓角大小,可通過以下方式
for (UIImageView *iv in _progressView.subviews) {
  iv.layer.masksToBounds = YES;
  iv.layer.cornerRadius = 10;
 }

關(guān)于super view不可用而某個sub view可用的邏輯

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    UIView *hitTestView = [super hitTest:point withEvent:event];
    if ([self pointInside:point withEvent:event]) {
        CGPoint newPoint = [self convertPoint:point toView:self.rightView];
        BOOL rightViewEnabled  = CGRectContainsPoint(self.rightView.bounds, newPoint) && self.rightView.userInteractionEnabled;
        
        return rightViewEnabled
        ? self.rightView
        : (self.enabled && self.userInteractionEnabled && self.alpha > 0
           ? self
           : hitTestView);
    }
    return hitTestView;
}

Category交換系統(tǒng)dealloc方法

#import "UIView+Dealloc.h"
#import <objc/runtime.h>

@implementation UIView (Dealloc)

+ (void)load{
    Method systemDealloc = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));
    Method selfDealloc = class_getInstanceMethod(self, @selector(selfDealloc));
    method_exchangeImplementations(systemDealloc, selfDealloc);
}

- (void)selfDealloc{
    NSLog(@"%@ [deallocated]", self);
    
    [self selfDealloc];
}

@end

關(guān)于RAC下如何觸發(fā)UIButton的點擊事件

  • 定義按鈕事件處理邏輯
@weakify(self);
    self.btn.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            @strongify(self);
            
// 事件處理...

            [subscriber sendCompleted];
            return nil;
        }];
    }];
  • 點擊事件監(jiān)聽

注意這個地方要防止循環(huán)引用

    [[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *x) {
// x不能再重復(fù)使用,否則會循環(huán)應(yīng)用
// 應(yīng)該用[weakSelf.btn.rac_command execute:x]
// ?
        [x.rac_command execute:x];
// ?
        [weakSelf.btn.rac_command execute:x];
    }];
  • 觸發(fā)事件
[self.btn.rac_command execute: nil];

libc++abi.dylib`__cxa_throw:

編輯斷點

Masonry警告調(diào)試

  • 添加斷點


    添加斷點
  • 捕獲約束警告


    捕獲約束警告
  • 代碼中設(shè)置mas_key
// 按make_layout的順序
MASAttachKeys(self.attachBtn, self.lineView, self.teacherLabel, self.downloadBtn);
// 或者
self.attachBtn.mas_key = @"xxx";

獲取UIPageViewController當(dāng)前頁面的index

// delegate方法
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray<UIViewController *> *)previousViewControllers transitionCompleted:(BOOL)completed{
// 單頁展示
    UIViewController *currentVC = pageViewController.viewControllers.firstObject;
    self.selectedPageIndex = [self.pageViewControllers indexOfObject:currentVC];
}

關(guān)于UITableViewheightForFooter/Header代理方法不執(zhí)行問題

這種情況下,一般是因為只設(shè)置了heightForFooter/Header方法,而沒有設(shè)置viewForFooter/Header。

iOS打開設(shè)置頁面

  • iOS 10之前
`prefs:root=WIFI`
// 或者 
`UIApplicationOpenSettingsURLString`
  • iOS 10之后
`App-Prefs:root=WIFI`
  • 各個設(shè)置對應(yīng)shemes

無線局域網(wǎng) App-Prefs:root=WIFI
藍(lán)牙 App-Prefs:root=Bluetooth
蜂窩移動網(wǎng)絡(luò) App-Prefs:root=MOBILE_DATA_SETTINGS_ID
個人熱點 App-Prefs:root=INTERNET_TETHERING
運營商 App-Prefs:root=Carrier
通知 App-Prefs:root=NOTIFICATIONS_ID
通用 App-Prefs:root=General
通用-關(guān)于本機 App-Prefs:root=General&path=About
通用-鍵盤 App-Prefs:root=General&path=Keyboard
通用-輔助功能 App-Prefs:root=General&path=ACCESSIBILITY
通用-語言與地區(qū) App-Prefs:root=General&path=INTERNATIONAL
通用-還原 App-Prefs:root=Reset
墻紙 App-Prefs:root=Wallpaper
Siri App-Prefs:root=SIRI
隱私 App-Prefs:root=Privacy
Safari App-Prefs:root=SAFARI
音樂 App-Prefs:root=MUSIC
音樂-均衡器 App-Prefs:root=MUSIC&path=com.apple.Music:EQ
照片與相機 App-Prefs:root=Photos
FaceTime App-Prefs:root=FACETIME

iPhoneX判斷

#define kIsIphoneX CGSizeEqualToSize(CGSizeMake(1125, 2436), UIScreen.mainScreen.currentMode.size)

UIAlertController循環(huán)引用問題

一般來說,如果在UIAlertAction的handlerBlock中如果調(diào)用了alert對象,就會產(chǎn)生循環(huán)引用,解決方法如下:

UIAlertController *alert = [UIAlertController alertxxx];
__weak typeof(UIAlertController *)weakAlert = alert;
// 然后使用weakAlert即可

另一種方法:

__wak __block UIAlertController *alert = nil; // __block可以在handlerBlock中保留該局部變量
// 做一些配置
alert = [UIAlertController showXXX]; // 這里是自定義的展示方法,展示完畢返回一個alert對象

Pods庫頭文件不提示問題

在工程配置中User Header Search Paths添加$(PODS_ROOT)選擇recursive

正確獲取UIApplication的當(dāng)前顯示viewController

向UIApplication添加Category方法:

+ (UIViewController *)currentViewController{
    UIViewController *vc = self.keyWindow.rootViewController;
    while (1) {
        if ([vc isKindOfClass:UITabBarController.class]) {
            vc = ((UITabBarController *)vc).selectedViewController;
        }
        else if ([vc isKindOfClass:UINavigationController.class]) {
            vc = ((UINavigationController *)vc).visibleViewController;
        }
        else if (vc.presentedViewController) {
            vc = vc.presentedViewController;
        }
        else{
            break;
        }
    }
    return vc;
}

This app could not be installed at this time.解決方法

打開~/Library/Logs/CoreSimulator/CoreSimulator.log日志,如果提示did not have a CFBundleIdentifier in its Info.plist,則刪除~/Library/Developer/Xcode/DerivedData/文件夾重新編譯運行。

UILongPressGesture使用注意

UILongPressGestureRecognizer *gesture = [UILongPressGestureRecognizer new];
        [gesture.rac_gestureSignal subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
            @strongify(self);
// Began、Changed、Ended
            NSLog(@"%@",x);
            if (x.state == UIGestureRecognizerStateBegan) {
                [self.viewModel.saveQRCodeImageSubject sendNext:[UIImage imageNamed:@"qrcode_app"]];
            }
        }];

UITableViewStyleGrouped樣式下header多余空白問題

_tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, CGFLOAT_MIN)];
如果設(shè)置了tableView.delegate,則還要設(shè)置footerView為[UIView new],且高度為CGFLOAT_MIN;

Pod lib lint/repo push錯誤include of non-modular header inside framework module

解決辦法:pod lib lint xxx --use-libraries即可
或者
import ""頭文件方式改為import <>,然后重新編譯

iOS國際化

1. 新建.strings文件
2. 點擊`Localize`
3. 勾選`Chinese`和`English`
4. 選擇其他語言
5. 格式為`key = value;`

安裝CocoaPods報錯

執(zhí)行sudo gem install cocoapods報錯You don't have write permissions for the /usr/bin directory.
解決辦法:sudo gem install -n /usr/local/bin cocoapods

CocoaPods

pod install --verbose --no-repo-update
pod update --verbose --no-repo-update

關(guān)于UIButton設(shè)置頻繁setTitle時閃爍問題

其實這時候buttonType應(yīng)該為custom類型,然后:

btn.titleLabel.text = @"xxx";
[btn setTitle:@"xxx" forState:UIControlStateNormal];

XCode8以后安裝插件

https://blog.csdn.net/lincsdnnet/article/details/77412878

UIViewController生命周期

+[load]
-[initWithCoder:]
-[viewDidLoad:]
-[viewWillAppear:]
-[updateViewConstraints] //一般在這里進(jìn)行自動布局的代碼-Masonry/SnapKit
-[viewWillLayoutSubviews] //add/removeSubview操作都會引起多次條用layoutSubviews
-[viewDidLayoutSubviews]
-[viewWillLayoutSubviews]
-[viewDidLayoutSubviews]
-[viewDidAppear:]
-[viewWillDisappear:]
-[viewDidDisappear:]
-[dealloc]

OC Block

@property(nonatomic, copy)void (^屬性名)(參數(shù)類型);
// 或者定義為類型
typedef 返回值類型(^Block名)(參數(shù)類型);

Swift中的available

@available(iOS x, *) //方法、屬性
func xxx(){
}

if #available(iOS x, *) { //代碼塊
}

實現(xiàn)一個有placeholder的textView

import UIKit

class PlaceTextView: UITextView {

// MARK: - IBOutlets
    fileprivate lazy var placeholderLabel: UILabel = {
        let lb: UILabel = .init(frame: .init(x: 10, y: 10, width: self.bounds.width - 20, height: 20))
        lb.font = self.font
        lb.textColor = .lightGray
        lb.numberOfLines = 1
        lb.text = self.placeholder
        return lb
    }()
    
    fileprivate lazy var clearButton: UIButton = {
        let btn: UIButton = UIButton(type: .system)
        btn.setTitle("×", for: .normal)
        btn.titleLabel?.font = UIFont.systemFont(ofSize: 18)
        btn.tintColor = .white
        btn.backgroundColor = .gray
        btn.isHidden = true
        btn.layer.cornerRadius = self.clearButtonSize / 2
        btn.addTarget(self, action: #selector(self.clearInput), for: .touchUpInside)
        return btn
    }()
    
// MARK: - Properties
    fileprivate let clearButtonSize: CGFloat = 20
    
    var placeholder: String? = "請輸入..."{
        didSet{
            placeholderLabel.text = placeholder
        }
    }
    
    override var textContainerInset: UIEdgeInsets{
        didSet{
            super.textContainerInset = UIEdgeInsets(top: textContainerInset.top, left: textContainerInset.left, bottom: textContainerInset.bottom, right: textContainerInset.right + clearButtonSize + 10)
        }
    }
    
// MARK: - Initial Method
    private func setupUI() {
        self.textContainerInset = super.textContainerInset
        
        addSubview(clearButton)
        addSubview(placeholderLabel)

        NotificationCenter.default.addObserver(self, selector: #selector(textDidChange), name: NSNotification.Name.UITextViewTextDidChange, object: nil)
    }
    
 
// MARK: - Lifecycle Method
    override func awakeFromNib() {
        super.awakeFromNib()        
        setupUI()
    }
    
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        setupUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        layoutClearButton()
        layoutPlaceholderLabel()
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
// MARK: - Action & IBOutletAction
    @objc func clearInput(){
        text = nil
        textDidChange()
    }
    
    @objc fileprivate func textDidChange(){
        let isEmpty = text == nil || text.isEmpty
        placeholderLabel.text = isEmpty ? placeholder : nil
        clearButton.isHidden = isEmpty
    }
    
// MARK: - Override Method3
    
// MARK: - Private method
    fileprivate func layoutClearButton() {
        let x = bounds.width - clearButtonSize - 10
        let y = bounds.height / 2 - clearButtonSize / 2 + contentOffset.y
        clearButton.frame = CGRect(origin: .init(x: x, y: y), size: CGSize(width: clearButtonSize, height: clearButtonSize))
    }
    
    fileprivate func layoutPlaceholderLabel() {
        let width = clearButton.frame.origin.x - 20
        placeholderLabel.frame = CGRect(origin: .init(x: textContainerInset.left + 4, y: textContainerInset.top), size: CGSize(width: width, height: 14))
    }
    
// MARK: - Public Method
}

統(tǒng)計代碼行數(shù)

  • .swift、.c、.m、.h、.xib等文件
find . -name "*.m" -or -name "*.h" -or -name "*.xib" -or -name "*.c" -or -name "*.swift" |xargs wc -l

帶icon的label文本

let attach = NSTextAttachment()
attach.image = UIImage(named: "icon")
attach.bounds = CGRect(origin: CGPoint(x: 0, y: -label.font.pointSize / 2), size: attach.image!.size)
let attr = NSAttributedString(attachment: attach)
let text = NSMutableAttributedString(string: "  xxx")
text.insert(attr, at: 0)
label.attributedText = text

判斷字符是表情字符

extension String{
    var isEmoji: Bool{
        return rangeOfCharacter(from: .symbols) != nil
    }
}

修改UITextField的clear button

let clearButton = textFiled.value(forKeyPath:"_clearButton")
(clearButton as? UIButton)?.setImage(UIImage(named: "close"), for: .normal)

判斷當(dāng)前輸入字符為表情

UITextView/UITextField如果在輸入內(nèi)容改變時.textInputMode?.primaryLanguage == nil則當(dāng)前正在輸入的是表情字符。

FileManager.default.fileExists

var isDirectory = ObjCBool(true)
FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory)

關(guān)于Swift 字典-模型轉(zhuǎn)換時報錯:

[_SwiftTypePreservingNSNumber length]: unrecognized selector sent to instance xxx
  • 原因:在將dictionary轉(zhuǎn)換為model過程中,出現(xiàn)了model的屬性數(shù)據(jù)類型和字典值的類型不匹配情況;
  • 解決:將model中的屬性數(shù)據(jù)類型改為和dictionary一致;
  • 示例:
class Student: NSObject{
var id = 0 //注意是Int類型
var name = ""
}
// dictionary自動填充為model
let student = Student()
student.setValuesForKeys(["id": "0", "name": "xiaoming"]) 
//注意此處字典中的id數(shù)據(jù)類型為String,與model的Int不符合,則會出現(xiàn)如上的報錯信息;

Swift 函數(shù)

lroundf() //四舍五入 
ceil() //向上取整 
floor() //向下取整

Swift didSet, willSet記

var selectedClass: Class!{
    willSet{
        print(newValue)
    }
    didSet{
        guard selectedClass != oldValue else {
            return
        }
        requestStudentData()
    }
}

Swift reduce高階函數(shù)用法

let totalProgress = learnings.reduce(0){ $0 + $1.displayProgress }

UITextField輸入字符數(shù)限制

extension InformationEditVC: UITextFieldDelegate{
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if let cell = textField.superview?.superview as? InformationEditCell {
            return string == "" || cell.data.maximumLength == 0 || 
                textField.text!.length < cell.data.maximumLength
        }
        return false
    }
}

關(guān)于UITableViewController在Storyboard上的Cell復(fù)用問題

  • 注意這種情況下 不要dequeueReusableCell(withIdentifier: , for:)指定indexPath方式;
  • 因為系統(tǒng)創(chuàng)建cell的方式是從xib中init的,然后加入復(fù)用隊列;
  • 如果是自己手動register的cell,則要指定indexPath;

UISearchBar自定制

// 去掉黑邊
searchBar.backgroundImage = UIImage()
if let tf = searchBar.value(forKey: "searchField") as? UITextField{
  tf.subviews[0].corner(radius: 14) // 輸入框圓角
}
默認(rèn)上下內(nèi)邊距為8pt

關(guān)于Swift的array.insert方法注意事項:

  • 在Swift中,Array、Dictionary、Set等均為基本數(shù)據(jù)類型,所以是值拷貝;
  • insert方法的執(zhí)行過程類似于i += 1的操作,是在內(nèi)存中進(jìn)行的,完成之后將值重新付給array,所以會執(zhí)行array的didSet方法;

UITableViewCell的xib在其他view上的使用方法

@IBOutlet weak var infoView: UIView!{
        didSet{
            if let cell = resourceCell {
                infoView.addSubview(cell) // 需cell和cell.contentView都添加
                infoView.addSubview(cell.contentView)
                cell.frame = infoView.bounds
                cell.autoresizingMask = [.flexibleWidth, .flexibleHeight]
            }
        }
    }

UIButton設(shè)置選中/正常圖片的另一種方式

fileprivate lazy var favoriteButton: UIButton = {
    let btn = UIButton(type: .system)
    btn.setImage(UIImage.named("icon_favorite_24"), for: .normal)
    btn.sizeToFit()
    btn.addTarget(self, action: #selector(actionToggleFavorite(_:)), for: .touchUpInside)
    return btn
}()

@objc fileprivate func actionToggleFavorite(_ sender: UIBarButtonItem) {
    data.favorited = !data.favorited
    favoriteButton.tintColor = data.favorited  ? .appOrange  : .white// 改變tintColor即可
}

forEach{}注意

  • .forEach { $0.xxx = xxx }注意這種情況下,并不能修改$0的屬性值;
  • .forEach { (item) in item.xxx = xxx }才能修改屬性值;

UISlider注意事項

  • 注意thumbTintColorsetThumbImage不可同時設(shè)置(只對一種起作用);
  • minimumTrackTintColormaximumTrackTintColor和圖片的設(shè)置原理同上;

關(guān)于Storyboard上UITableViewController的設(shè)置問題:

  • 改變heightForHeaderInSection、heightForRowAt
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return section == 0 ? .zero : 10
    }
    
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return indexPath.section == 0 ? (abs(tableView.contentInset.top) + 110) : 54
    }
  • 設(shè)置BasicCell的icon,并設(shè)置tintColor
1. 設(shè)置icon的`Render As` 為`Template Image`
2. 設(shè)置cell的imageView的tintColor
3. 效果

Swift中關(guān)于UIViewController的init方法

  • 該方法在從xib初始化或無參數(shù)的init()時均會調(diào)用
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}

計算某個對象的所有后代節(jié)點-遞歸算法

private func descendantNodesOf(ancestor: TreeNode) -> [TreeNode]{
    var nodes = [TreeNode]()
    nodes.append(contentsOf: ancestor.subNodes)
    for node in ancestor.subNodes {
        nodes.append(contentsOf: descendantNodesOf(ancestor: node))
    }
    return nodes
}

App實時幀率計算

- (void)initCADisplayLink {
    self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
    
    [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)tick:(CADisplayLink *)link {
    if (self.lastTime == 0) {           //對LastTime進(jìn)行初始化
        self.lastTime = link.timestamp;
        return;
    }
    
    self.count += 1;   //記錄tick在1秒內(nèi)執(zhí)行的次數(shù)
    NSTimeInterval delta = link.timestamp - self.lastTime;  //計算本次刷新和上次更新FPS的時間間隔
    
    //大于等于1秒時,來計算FPS
    if (delta >= 1) {
        self.lastTime = link.timestamp;
        float fps = self.count / delta;         // 次數(shù) 除以 時間 = FPS (次/秒)
        self.count = 0;
        [self updateDisplayLabelText: fps];
    }
}

UIScrollView在當(dāng)前有導(dǎo)航欄的控制器里offset偏移問題

self.automaticallyAdjustsScrollViewInsets = false
self.edgesForExtendedLayout = .all

隱藏狀態(tài)欄

setStatusBarHidden(true, with: .none)
// 或者
app.isStatusBarHidden = true

iOS使用自定義字體

Info.plist
let font = UIFont(name: "xxx")

Swift4中系統(tǒng)通知名稱

// Notification.Name.XXX
NotificationCenter.default.addObserver(self, selector: #selector(playerDidReachEnd(_:)), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)

設(shè)置view的layer的類型

class ScrollingView: UIView {
  
  override class var layerClass : AnyClass {
    return CAScrollLayer.self
  }
  
}

Swift 格式化數(shù)字

String(format: "%.1f", slider.value)

Swift - switch-case 用法

  • 與where使用
let row = Row(rawValue: indexPath.row)!
    
    switch row {
    case .contentsGravity where !contentsGravityPickerVisible:
      showContentsGravityPicker()
    default:
      hideContentsGravityPicker()
    }
  • 組合用法
  @IBAction func scrollingSwitchChanged(_ sender: UISwitch) {
    let xOn = horizontalScrollingSwitch.isOn
    let yOn = verticalScrollingSwitch.isOn
    switch (xOn, yOn) {
    case (true, true):
      scrollingViewLayer.scrollMode = kCAScrollBoth
    case (true, false):
      scrollingViewLayer.scrollMode = kCAScrollHorizontally
    case (false, true):
      scrollingViewLayer.scrollMode = kCAScrollVertically
    default:
      scrollingViewLayer.scrollMode = kCAScrollNone
    }
  }

Swift代碼標(biāo)記

// MARK: 
// TODO:
// FIXME:

簡書上傳圖片自定義寬度

修改數(shù)值

自定義NSLog宏

#ifdef DEBUG
#define NSLog(FORMAT, ...) fprintf(stderr,"[%s][#%d] - %s\n",
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], 
__LINE__, 
[[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
#define NSLog(FORMAT, ...) nil
#endif

__VA_ARGS__ 可變參數(shù)的宏
__FILE__  文件全路徑
__FUNCTION__  類/實例方法
__LINE__  所在行

Xib/Storyboard兼容打開

Xcode設(shè)置Xib/Storyboard打開

導(dǎo)航欄動態(tài)透明

// 先設(shè)置為全透明
-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    
    UINavigationBar *navBar = self.navigationController.navigationBar;
    [navBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
    navBar.shadowImage = [UIImage new];
    navBar.translucent = true;
}

// 動態(tài)透明
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGFloat alpha = scrollView.contentOffset.y / 100;
    self.navigationController.navigationBar.backgroundColor = [UIColor.redColor colorWithAlphaComponent:alpha];
}

一種創(chuàng)建對象的代碼塊

UIView *view = ({
    UIView *v = [UIView new];
// do something...
    
    v;  // copy `v` to `view`
});

CoreText獲取CTLine的bounds方式

  • 方法1:
CGFloat a;
CGFloat d;
CGFloat l;
CGFloat width = CTLineGetTypographicBounds(moreLine, &a, &d, &l);
CGFloat height = a + d + l;
CGRect bounds = CGRectMake(0,0,width,height);
  • 方法2:
    只有當(dāng)options設(shè)置不同時,兩種方法計算得到的值才可能不一樣
CGRect bounds = CTLineGetBoundsWithOptions(moreLine, kCTLineBoundsUseOpticalBounds) 

Git移除變基

rm -rf .git/rebase-apply

UICollectionView空白時無法使用下拉刷新問題

collectionView.alwaysBounceVertical = YES;

禁用導(dǎo)航欄滑動返回

self.navigationController.interactivePopGestureRecognizer.enabled = NO;

Swift利用Mirror獲取class或struct的屬性和值

func getAllProperties(){
  let mirror = Mirror.init(reflecting: self)
  for (key, value) in mirror.children {
    print("\(key!): \(value)")
  }
}

關(guān)于UIApplication的keyWindow

  • 如果設(shè)置了project的Main Interface為某個storyboard,則application: didFinishLaunchingWithOptions:時,會某人創(chuàng)建一個window,并且設(shè)置如下:
window.rootViewController = storyboard.initialViewController;
  • 等完成啟動并return YES,之后才會將[window makeKeyAndVisible],而這時如果你想在該啟動方法中進(jìn)行一些UI方面的操作是不行的,因為它還沒有被makeKeyAndVisible。

  • 正確的做法是手動設(shè)置可見[window makeKeyAndVisible],然后做一些操作:

    [_window makeKeyAndVisible];

    // 廣告圖
    UIImageView *adIV = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    adIV.backgroundColor = [UIColor redColor];
    adIV.center = _window.center;
    [_window addSubview:adIV];
  • 另外,如果你沒有設(shè)置項目的Main Interface,那么你需要手動實例化window對象,并設(shè)置rootViewController和makeKeyAndVisible:
    _window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
    [_window makeKeyAndVisible];
    _window.rootViewController = [UIViewController new];

LaunchScreen.storyboard替換圖片資源后無法顯示問題

    1. 解決辦法:Simulator > Reset Content and Settings...
    1. 然后重新運行項目

UICollectionView上cell的布局屬性

UICollectionViewLayoutAttributes *attrs = 
[collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath];
UICollectionViewLayoutAttributes

UITextField事件

UIControlEventEditingDidBegin
UIControlEventEditingChanged
UIControlEventEditingDidEnd
UIControlEventEditingDidEndOnExit   

OC Block

@property(nonatomic,copy)void (^valueChanged)(HYRegisterInputView *input);

UITextView超鏈接

UITextView *textView = [[UITextView alloc] init];
textView.scrollEnabled = NO;
textView.editable = NO;
textView.textContainer.lineFragmentPadding = 0;
textView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
textView.delegate = self;

//代理方法
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)url inRange:(NSRange)characterRange
{
     return YES;
}

CocoaPods

a pod project builds all the individual pods as their own framework, and then combines them into one single framework: Pods-XXX.
https://cocoapods.org
https://opensource.org

Storyboard使用framework注意

When using storyboards, references to custom classes need to have both the class name and module set in the Identity Inspector. At the time of this storyboard’s creation, ThreeRingView was in the app’s module, but now it’s in the framework.

update storyboard settings

Swift Code-Access-Control

  • Public: for code called by the app or other frameworks, e.g., a custom view.

  • Internal: for code used between functions and classes within the framework, e.g., custom layers in that view.By default, Swift makes everything internal or visible only within its own module.

  • Fileprivate: for code used within a single file, e.g., a helper function that computes layout heights.

  • Private: for code used within an enclosing declaration, such as a single class block. Private code will not be visible to other blocks, such as extensions of that class, even in the same file, e.g., private variables, setters, or helper sub-functions.

StatusBar網(wǎng)絡(luò)請求指示器

UIApplication.shared.isNetworkActivityIndicatorVisible = true

SourceTree暫存

暫存
右鍵
恢復(fù)暫存

布局優(yōu)先級priority

bottom
bottom 1000
bottom 250

App Dynamic Type

  • 設(shè)置>輔助功能>LargeText>支持動態(tài)字體的應(yīng)用的閱讀字體body
storyboard設(shè)置
// 代碼設(shè)置
label.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)

// 字體改變通知
NotificationCenter.default.addObserver(forName: .UIContentSizeCategoryDidChange, object: .none, queue: OperationQueue.main) { [weak self] _ in
    self?.tableView.reloadData()
  }

NotificationCenter須知

Starting with iOS 9, it is no longer necessary to remove notification center observers. If your app’s deployment target is iOS 8, however, you will still need to do that!

  • iOS9以后,不再需要手動移除通知中心中add的observer

UITableView刷新

// Row insertion/deletion/reloading.

open func beginUpdates() // allow multiple insert/delete of rows and sections to be animated simultaneously. Nestable

open func endUpdates() // only call insert/delete/reload calls or change the editing state inside an update block. otherwise things like row count, etc. may be invalid.

UITextView自動高度

Disabling scrolling is of similar importance to setting a label to 0 lines

textView.isScrollEnabled = false; //類似于lable.numberOfLines = 0;

歸檔

  • swift3 - 只能對class類型歸檔
class Album: NSObject, NSCoding{
  var title = ""
    
    func encode(with aCoder: NSCoder) {
        aCoder.encode(title, forKey: "title")
    }
    
    required init?(coder aDecoder: NSCoder) {
        title = aDecoder.decodeObject(forKey: "title") as? String ?? ""
    }
}
  • swift4 - 對class、enum、struct類型歸檔
struct Album: Codable {
  let title : String
}

// encode
func saveAlbums() {
  let url = documents.appendingPathComponent(Filenames.Albums)
  let encoder = JSONEncoder()
  guard let encodedData = try? encoder.encode(albums) else {
    return
  }
  try? encodedData.write(to: url)
}

// decode
func getAlbums(){
let savedURL = documents.appendingPathComponent(Filenames.Albums)
var data = try? Data(contentsOf: savedURL)
if data == nil, let bundleURL = Bundle.main.url(forResource: Filenames.Albums, withExtension: nil) {
  data = try? Data(contentsOf: bundleURL)
}

if let albumData = data,
  let decodedAlbums = try? JSONDecoder().decode([Album].self, from: albumData) {
  albums = decodedAlbums
  saveAlbums()
}
}

KVO

  • swift4
private var valueObservation: NSKeyValueObservation!

valueObservation = coverImageView.observe(\.image, options: [.new]) { [unowned self] observed, change in
  if change.newValue is UIImage {
// do something...
  }
}
  • swift3
coverImageView.addObserver(self, forKeyPath: "image", options: .new, context: nil)

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "image", 
            change?[.newKey] is UIImage  {
// do something...
        }
    }

UITable趣談

Here’s a pseudo-explanation of what goes on when you create a new UITableView:
Table: Here I am! All I want to do is SHOW CELLS. Hey, how many sections do I have?
Data source: One!
Table: OK, nice and easy! And how many cells in that first section?
Data source: Four!
Table: Thanks! Now, bear with me, this might get a bit repetitive. Can I have the cell at section 0, row 0?
Data source: Here you go!
Table: And now section 0, row 1?
…and so on.

Swift元組類型Tuple

typealias AlbumData = (title: String, value: String)

let data = AlbumData(title: "xxx", value: "xxx")
// 也可以寫成
let data = AlbumData("xxx",  "xxx")

導(dǎo)航欄左側(cè)按鈕設(shè)置

  • 返回按鈕
// 默認(rèn)是nil,在父controller中設(shè)置
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:nil action:nil];
  • 左邊按鈕
UIBarButtonItemStylePlain //細(xì)
UIBarButtonItemStyleDone //粗

// 保留返回按鈕
self.navigationItem.leftItemsSupplementBackButton = true;
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Left" style:UIBarButtonItemStyleDone target:self action:@selector(action:)];

CSR申請

必填,不然創(chuàng)建的證書中沒有key

URL特殊字符編碼

"".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)

Git關(guān)于.xcuserstate不提交的方法

1. cd 項目工程目錄下

2. git rm --cached 項目名.xcodeproj/project.xcworkspace/xcuserdata/用戶名.xcuserdatad/UserInterfaceState.xcuserstate

// 重新提交改變到本地
3. git commit -m "removed `.xcuserstate` file;"

UILayoutGuide

UIView

  • layoutMarginsGuide
  • readableContentGuide

UIViewController

  • topLayoutGuide: UILayoutSupport (topAnchor、bottomAnchor、heightAnchor)
  • bottomLayoutGuide

NSLayoutAnchor

  • UIView

NSLayoutXAxisAnchor

  • leadingAnchor
  • trailingAnchor
  • leftAnchor
  • rightAnchor
  • centerXAnchor

NSLayoutYAxisAnchor

  • topAnchor
  • bottomAnchor
  • centerYAnchor
  • firstBaselineAnchor
  • lastBaselineAnchor

NSLayoutDimension

  • widthAnchor
  • heightAnchor

UIView的appearance配置

[UIActivityIndicatorView appearanceWhenContainedIn:[MBProgressHUD class], nil].color = UIColor.whiteColor;

strongSelf與weakSelf

  • 異步線程請求
var net: Network? = Network()
net!.request()
net = nil //線程回調(diào)的時候已經(jīng)被銷毀了,會出錯,或無提示
class Network: NSObject {
    func request() {
        print("requesting...")
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(3)) {
            [weak self] in
            
            guard let strongSelf = self else {
          fatalError("`self is nil`")
            }
            
 //如果有多步后續(xù)操作的話,就不能少了上述`guard let`判斷而直接用self?.updateModel
            strongSelf.updateModel()
            strongSelf.updateUI()
        }
    }
    
    func updateModel(){
        print("updated model")
    }
    
    func updateUI(){
        print("updated model")
    }
}

UITableView橫向滾動

class ViewController: UITableViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        self.tableView.rowHeight = self.view.bounds.width
        self.tableView.isPagingEnabled = true
        self.tableView.separatorStyle = .none
        
        // 旋轉(zhuǎn)
        self.view.transform = CGAffineTransform(rotationAngle: .pi/2)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        self.tableView.scrollToRow(at: IndexPath.init(row: 9, section: 0), at: .bottom, animated: false)
        
        super.viewDidAppear(animated)
    }

}

extension ViewController{
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell", for: indexPath)
        
        cell.contentView.transform = CGAffineTransform(rotationAngle: -.pi/2)
        
        return cell
    }
    
}

WKWebView進(jìn)度條

// 監(jiān)聽進(jìn)度條
wkWebView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        
        if keyPath == "estimatedProgress", 
            let progress = change?[.newKey] as? Float {
            
// 顯示進(jìn)度
            progressView.progress = progress == 1 ? 0 : progress
        }
}

iOS文字刪除線

NSStrikethroughStyleAttributeName: @(NSUnderlineStyleSingle|NSUnderlinePatternSolid),
NSStrikethroughColorAttributeName: [UIColor lightGrayColor]

OC可視化

IB_DESIGNABLE
@interface CircleView : UIView

@property (nonatomic, assign) IBInspectable CGFloat radius;

@end

NSLayoutConstraint基本用法

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.translatesAutoresizingMaskIntoConstraints = false;
    
    UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeSystem];
    [btn1 setTitle:@"Button1" forState:UIControlStateNormal];
    btn1.backgroundColor = [UIColor redColor];
    btn1.translatesAutoresizingMaskIntoConstraints = false;
    
    UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeSystem];
    [btn2 setTitle:@"Button2" forState:UIControlStateNormal];
    btn2.backgroundColor = [UIColor greenColor];
    btn2.translatesAutoresizingMaskIntoConstraints = false;
    
    [self.view addSubview:btn1];
    [self.view addSubview:btn2];
    
    NSDictionary *views = NSDictionaryOfVariableBindings(btn1, btn2);
    
    // 等寬
    NSArray *cons1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[btn1(btn2)]" options:0 metrics:nil views:views];
    NSArray *cons2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-100-[btn1(44)]" options:0 metrics:nil views:views];
    // 等高
    NSArray *cons3 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[btn2(btn1)]" options:0 metrics:nil views:views];
    NSArray *cons4 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[btn1][btn2]|" options:0 metrics:nil views:views];
    
    NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:btn2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:btn1 attribute:NSLayoutAttributeTop multiplier:1 constant:0];
    
    [self.view addConstraints:cons1];
    [self.view addConstraints:cons2];
    [self.view addConstraints:cons3];
    [self.view addConstraints:cons4];
    [self.view addConstraint:top];
}

多個UIButton點擊選中一個,并改變樣式

UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
    btn.layer.borderWidth = 1;
    btn.layer.cornerRadius = 5;
    [btn setTitle:title forState:UIControlStateNormal];
    [btn setTitle:title forState:UIControlStateSelected];
    [btn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor orangeColor] forState:UIControlStateSelected];
    [btn addTarget:self action:@selector(actionForButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
    btn.tintColor = [UIColor clearColor];

// 點擊事件
for (UIView *sub in self.subviews) 
    {
        if ([sub isKindOfClass:[UIButton class]]) 
        {
            UIButton *btn = (UIButton*)sub;
            btn.selected = [btn isEqual: sender];
            btn.layer.borderColor = btn.selected ? [UIColor orangeColor].CGColor : [UIColor lightGrayColor].CGColor;
        }
    }

UIScrollView約束

首先滿足:frame[left, top, bottom, right]

約束postion
約束size

其次要通過子view約束[left + right + size]水平滾動,[top + bottom + size]垂直滾動,來確定它的contentSize

約束子view位置
約束子view大小
寬度改為1/3
view2和view1等寬等高
view3和view2等寬等高
最后要注意view3的trailing和scrollView的trailing約束

(NSSet<UITouch *> *)touches取元素

UITouch *touch = touches.anyObject;

sending const NSString * to parameter of type NSString * discards qualifiers

static const NSString* str = @"xxx";
改成 static NSString* const str = @"xxx";
// 前者相當(dāng)于指針本身不可修改,后者表示指針指向的內(nèi)容不可修改,兩者的作用都是使str只只讀。

UITableViewStylePlain使得sectionHeader“懸浮”問題解決

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat height = _tableView.sectionHeaderHeight;
    CGFloat offY = scrollView.contentOffset.y;
    
    if (offY >= 0 && offY <= height) {
        scrollView.contentInset = UIEdgeInsetsMake(-offY, 0, 0, 0);
    } 
    else if (offY >= height) {
        scrollView.contentInset = UIEdgeInsetsMake(-height, 0, 0, 0);
    }
}

UIButton.system選中設(shè)置

UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
    [btn sizeToFit]; // 如果未設(shè)置btn.frame時應(yīng)該這么用
    btn.tintColor = [UIColor clearColor];
    [btn setTitle:@"編輯" forState:UIControlStateNormal];
    [btn setTitle:@"完成" forState:UIControlStateSelected];
    [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];

關(guān)于刪除cell

[_dataSource removeObjectAtIndex:index];
[_tableView beginUpdates];
[_tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:index]] withRowAnimation:UITableViewRowAnimationLeft];
// 如果是最后一行,則要刪除對應(yīng)的section
[_tableView deleteSections:[NSIndexSet indexSetWithIndex:index] withRowAnimation:UITableViewRowAnimationAutomatic];
[_tableView endUpdates];

mas_makeConstraints 與 mas_updateConstraints

注意在tableView.rowHeight = UITableViewAutomaticDimension時,盡量不要選用update方式,因為要動態(tài)計算高度,需一開始指定約束。

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

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

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