【iOS】常用技术点小记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

关于NSArray+NSDictionary数据筛选逻辑

// 有如下一个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为空的元素
// 真正要执行remove操作的对象
    NSMutableArray *list = [tempList mutableCopy];
    for (id tempObj1 in tempList) {
        // 真正更新的obj1
        NSMutableDictionary *obj1 = [tempObj1 mutableCopy];
        NSArray *tempList1 = tempObj1[@"list1"];
        // 真正要执行remove操作的对象
        NSMutableArray *list1 = [tempList1 mutableCopy];
        for (id obj2 in tempList1) {
            //...类似上面步骤
        }
        // 数据更新,如果为空,则移除
        if (list1.count == 0) {
            // 由于list和tempList是copy方式,所以内含的object也是copy
            [list removeObject:tempObj1];
        }
        else{
            // 更新list
            obj1[@"list1"] = list;
            list[[list indexOfObject:obj1]];
        }
    }

iPhone全面屏适配问题

// 底部布局边距
    CGFloat layoutBottom = -20;
// iPhoneX是iOS11以后出现的(不包括越狱情况)
    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);
    }];

关于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相关改为command += %w(--help)
    最终效果

关于多级页面跳转

注意popToRootpushNewVC的先后顺序,否则会出现内存泄漏问题

[[self.goMyOrderBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        @strongify(self);
        // 选中我的页面
        UINavigationController *nav = weakTabBarVC.viewControllers.lastObject;
        [weakTabBarVC setSelectedViewController:nav];
        
        // 跳转到订单页面
        UIViewController *vc = [ZSMyOrderModuler getMyOrderViewController];
        vc.hidesBottomBarWhenPushed = YES;
        [nav pushViewController:vc animated:NO];
        
        // 返回首页
        [self.navigationController popToRootViewControllerAnimated:NO];
    }];

关于tabbar选中某个页面

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

-ObjC

这个flag告诉链接器把库中定义的Objective-C类和Category都加载进来。这样编译之后的app会变大(因为加载了其他的objc代码进来)。但是如果静态库中有类和category的话只有加入这个flag才行。

-all_load

这个flag是专门处理-ObjC的一个bug的。用了-ObjC以后,如果类库中只有category没有类的时候这些category还是加载不进来。变通方法就是加入-all_load或者-force-load。-all_load会强制链接器把目标文件都加载进来,即使没有objc代码。-force_load在xcode3.2后可用。但是-force_load后面必须跟一个只想静态库的路径。

全局隐藏导航栏返回按钮文字

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

关于导航栏的正确隐藏

- (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];
}

关于Xcode10断点不执行/执行混乱问题

File > Workspace Settings >设置

关于tableView: heightForHeader/FooterInSection:注意

  • 即使你返回了numberOfSections为0,如果你设置height为0,则height会变成默认的38,造成tableView的contentSize为2x38!=0,如果你监听这个contentSize则会出现问题。
  • 所以你的return 0.00001才行。
  • 然后监听contentSize.height需大于1才行。

关于RACObserve(self.array, count)监听不起作用问题

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

关于OC中对象地址和对象指针地址

打印对象地址: NSLog(@"%p", obj);
打印对象指针地址: NSLog(@"%x", obj);
判断两个对象地址相等: obj1 == obj2即可
判断字符串相等: [str1 isEqualToString: str2]
注意[obj1 isEqual: obj2]只是比较hash值,不是内存地址

关于Cell上的按钮rac_signalForControlEvents:UIControlEventTouchUpInside方法多次调用问题

// 这种方法最靠谱
[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设置问题

// 如果为customView时,则需指定customView的frame,否则显示不出来

- (UIBarButtonItem *)titleBtnItem{
    if (!_titleBtnItem) {
        UILabel *label = [UILabel new];
        label.font = ZSAppFont.system17.bold;
        label.text = @"学习内容";
        [label sizeToFit]; // 注意此处
        _titleBtnItem = [[UIBarButtonItem alloc] initWithCustomView:label];
    }
    return _titleBtnItem;
}

关于CocoaPods中bitcode设置问题:

  • 如果让当前pod库bitcode为NO,则在.podspec文件中设置如下:
s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO' }
  • 如果使工程中的所有pod库bitcode都为NO,则在Podfile文件中设置如下:
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;
 }

关于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交换系统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

关于RAC下如何触发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;
        }];
    }];
  • 点击事件监听

注意这个地方要防止循环引用

    [[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *x) {
// x不能再重复使用,否则会循环应用
// 应该用[weakSelf.btn.rac_command execute:x]
// ❌
        [x.rac_command execute:x];
// ✅
        [weakSelf.btn.rac_command execute:x];
    }];
  • 触发事件
[self.btn.rac_command execute: nil];

libc++abi.dylib`__cxa_throw:

编辑断点

Masonry警告调试

  • 添加断点


    添加断点
  • 捕获约束警告


    捕获约束警告
  • 代码中设置mas_key
// 按make_layout的顺序
MASAttachKeys(self.attachBtn, self.lineView, self.teacherLabel, self.downloadBtn);
// 或者
self.attachBtn.mas_key = @"xxx";

获取UIPageViewController当前页面的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];
}

关于UITableViewheightForFooter/Header代理方法不执行问题

这种情况下,一般是因为只设置了heightForFooter/Header方法,而没有设置viewForFooter/Header

iOS打开设置页面

  • iOS 10之前
`prefs:root=WIFI`
// 或者 
`UIApplicationOpenSettingsURLString`
  • iOS 10之后
`App-Prefs:root=WIFI`
  • 各个设置对应shemes

无线局域网 App-Prefs:root=WIFI
蓝牙 App-Prefs:root=Bluetooth
蜂窝移动网络 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
通用-关于本机 App-Prefs:root=General&path=About
通用-键盘 App-Prefs:root=General&path=Keyboard
通用-辅助功能 App-Prefs:root=General&path=ACCESSIBILITY
通用-语言与地区 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循环引用问题

一般来说,如果在UIAlertAction的handlerBlock中如果调用了alert对象,就会产生循环引用,解决方法如下:

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的当前显示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)];
如果设置了tableView.delegate,则还要设置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报错

执行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

关于UIButton设置频繁setTitle时闪烁问题

其实这时候buttonType应该为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] //一般在这里进行自动布局的代码-Masonry/SnapKit
-[viewWillLayoutSubviews] //add/removeSubview操作都会引起多次条用layoutSubviews
-[viewDidLayoutSubviews]
-[viewWillLayoutSubviews]
-[viewDidLayoutSubviews]
-[viewDidAppear:]
-[viewWillDisappear:]
-[viewDidDisappear:]
-[dealloc]

OC Block

@property(nonatomic, copy)void (^属性名)(参数类型);
// 或者定义为类型
typedef 返回值类型(^Block名)(参数类型);

Swift中的available

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

if #available(iOS x, *) { //代码块
}

实现一个有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
}

统计代码行数

  • .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)

判断当前输入字符为表情

UITextView/UITextField如果在输入内容改变时.textInputMode?.primaryLanguage == nil则当前正在输入的是表情字符。

FileManager.default.fileExists

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

关于Swift 字典-模型转换时报错:

[_SwiftTypePreservingNSNumber length]: unrecognized selector sent to instance xxx
  • 原因:在将dictionary转换为model过程中,出现了model的属性数据类型和字典值的类型不匹配情况;
  • 解决:将model中的属性数据类型改为和dictionary一致;
  • 示例:
class Student: NSObject{
var id = 0 //注意是Int类型
var name = ""
}
// dictionary自动填充为model
let student = Student()
student.setValuesForKeys(["id": "0", "name": "xiaoming"]) 
//注意此处字典中的id数据类型为String,与model的Int不符合,则会出现如上的报错信息;

Swift 函数

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

Swift didSet, willSet记

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

Swift reduce高阶函数用法

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

UITextField输入字符数限制

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
    }
}

关于UITableViewController在Storyboard上的Cell复用问题

  • 注意这种情况下 不要dequeueReusableCell(withIdentifier: , for:)指定indexPath方式;
  • 因为系统创建cell的方式是从xib中init的,然后加入复用队列;
  • 如果是自己手动register的cell,则要指定indexPath;

UISearchBar自定制

// 去掉黑边
searchBar.backgroundImage = UIImage()
if let tf = searchBar.value(forKey: "searchField") as? UITextField{
  tf.subviews[0].corner(radius: 14) // 输入框圆角
}
默认上下内边距为8pt

关于Swift的array.insert方法注意事项:

  • 在Swift中,Array、Dictionary、Set等均为基本数据类型,所以是值拷贝;
  • insert方法的执行过程类似于i += 1的操作,是在内存中进行的,完成之后将值重新付给array,所以会执行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设置选中/正常图片的另一种方式

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不可同时设置(只对一种起作用);
  • minimumTrackTintColormaximumTrackTintColor和图片的设置原理同上;

关于Storyboard上UITableViewController的设置问题:

  • 改变heightForHeaderInSectionheightForRowAt
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
    }
  • 设置BasicCell的icon,并设置tintColor
1. 设置icon的`Render As` 为`Template Image`
2. 设置cell的imageView的tintColor
3. 效果

Swift中关于UIViewController的init方法

  • 该方法在从xib初始化或无参数的init()时均会调用
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}

计算某个对象的所有后代节点-递归算法

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进行初始化
        self.lastTime = link.timestamp;
        return;
    }
    
    self.count += 1;   //记录tick在1秒内执行的次数
    NSTimeInterval delta = link.timestamp - self.lastTime;  //计算本次刷新和上次更新FPS的时间间隔
    
    //大于等于1秒时,来计算FPS
    if (delta >= 1) {
        self.lastTime = link.timestamp;
        float fps = self.count / delta;         // 次数 除以 时间 = FPS (次/秒)
        self.count = 0;
        [self updateDisplayLabelText: fps];
    }
}

UIScrollView在当前有导航栏的控制器里offset偏移问题

self.automaticallyAdjustsScrollViewInsets = false
self.edgesForExtendedLayout = .all

隐藏状态栏

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

iOS使用自定义字体

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

Swift4中系统通知名称

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

设置view的layer的类型

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

Swift 格式化数字

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代码标记

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

简书上传图片自定义宽度

修改数值

自定义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__ 可变参数的宏
__FILE__  文件全路径
__FUNCTION__  类/实例方法
__LINE__  所在行

Xib/Storyboard兼容打开

Xcode设置Xib/Storyboard打开

导航栏动态透明

// 先设置为全透明
-(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;
}

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

一种创建对象的代码块

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:
    只有当options设置不同时,两种方法计算得到的值才可能不一样
CGRect bounds = CTLineGetBoundsWithOptions(moreLine, kCTLineBoundsUseOpticalBounds) 

Git移除变基

rm -rf .git/rebase-apply

UICollectionView空白时无法使用下拉刷新问题

collectionView.alwaysBounceVertical = YES;

禁用导航栏滑动返回

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)")
  }
}

关于UIApplication的keyWindow

  • 如果设置了project的Main Interface为某个storyboard,则application: didFinishLaunchingWithOptions:时,会某人创建一个window,并且设置如下:
window.rootViewController = storyboard.initialViewController;
  • 等完成启动并return YES,之后才会将[window makeKeyAndVisible],而这时如果你想在该启动方法中进行一些UI方面的操作是不行的,因为它还没有被makeKeyAndVisible

  • 正确的做法是手动设置可见[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];
  • 另外,如果你没有设置项目的Main Interface,那么你需要手动实例化window对象,并设置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网络请求指示器

UIApplication.shared.isNetworkActivityIndicatorVisible = true

SourceTree暂存

暂存
右键
恢复暂存

布局优先级priority

bottom
bottom 1000
bottom 250

App Dynamic Type

  • 设置>辅助功能>LargeText>支持动态字体的应用的阅读字体body
storyboard设置
// 代码设置
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")

导航栏左侧按钮设置

  • 返回按钮
// 默认是nil,在父controller中设置
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:nil action:nil];
  • 左边按钮
UIBarButtonItemStylePlain //细
UIBarButtonItemStyleDone //粗

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

CSR申请

必填,不然创建的证书中没有key

URL特殊字符编码

"".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)

Git关于.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 //线程回调的时候已经被销毁了,会出错,或无提示
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`")
            }
            
 //如果有多步后续操作的话,就不能少了上述`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
        
        // 旋转
        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进度条

// 监听进度条
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 {
            
// 显示进度
            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";
// 前者相当于指针本身不可修改,后者表示指针指向的内容不可修改,两者的作用都是使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选中设置

UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
    [btn sizeToFit]; // 如果未设置btn.frame时应该这么用
    btn.tintColor = [UIColor clearColor];
    [btn setTitle:@"编辑" forState:UIControlStateNormal];
    [btn setTitle:@"完成" forState:UIControlStateSelected];
    [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];

关于删除cell

[_dataSource removeObjectAtIndex:index];
[_tableView beginUpdates];
[_tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:index]] withRowAnimation:UITableViewRowAnimationLeft];
// 如果是最后一行,则要删除对应的section
[_tableView deleteSections:[NSIndexSet indexSetWithIndex:index] withRowAnimation:UITableViewRowAnimationAutomatic];
[_tableView endUpdates];

mas_makeConstraints 与 mas_updateConstraints

注意在tableView.rowHeight = UITableViewAutomaticDimension时,尽量不要选用update方式,因为要动态计算高度,需一开始指定约束。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容