UICollectionViewFlowLayout

可以重写方法

  • (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;

给每个 cell 重写布局

另外还有下面这个方法在 view 的位置改变的时候实时调用上一个方法

  • (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds; // return YES to cause the collection view to requery the layout for geometry information

UIViewPropertyAnimator

动画除了[UIView animation...]
还可以使用UIView中的这个属性实现

创建一个 animator跟一个 block 实现具体的动画效果
然后启动 animator

添加Blur

let blurEffect = UIBlurEffect(style:.regular)

let visualEffectView = UIViewEffectView(effct:blurEffect)

self.addSubview(visualEffectView)

然后布局visualEffectView

蒙版的效果就有了

2018/11/20 posted in  iOS

Protocol

注意协议只是个声明文件,并没有实现。
谁声明,谁调用。

协议可用定义在单独.h文件中,也可用定义在某个类中:

(1) 如果这个协议只用在某个类中,应该把协议定义在该类中

(2) 如果这个协议用在很多类中,就应该定义在单独文件中

不过,如果子类自身又遵循了这个协议,但并没有实现,那么在运行时,系统会一级级往上查找,直到找到父类的方法实现。也就是说,只要知道苹果的私有方法名,并且确保自己的类是这个私有方法所属类的子类,就可以在子类中通过只声明不实现的方式执行父类中该私有方法的实现。

category

category也是一种特殊的协议,内部可以调用,外部不能调用、子类不能重写实现和重写,相当于是私有方法。

总结

协议就是定义公共接口的地方,只要遵守协议,就等于在头文件中定义了这些方法,只要实现就行了。

2018/11/16 posted in  iOS

Flutter && Python && OC

dart的函数定义如下所示

// Define a function.
printInteger(int aNumber) {
  print('The number is $aNumber.'); // Print to console.
}

// This is where the app starts executing.
main() {
  var number = 42; // Declare and initialize a variable.
  printInteger(number); // Call a function.
}

isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

如果函数只有单个语句,可以采用简略的形式:

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;


$dart取地址

String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

以分号结尾 参数类型要指定也可以不指定

=> 是{ return expr;} 的缩写

oc的函数定义

-(void)hello{

print(@"hello world");

}
-(void)hello(int a,int b){

print(@"hello world %d %d",a,b);

}


无需注意缩进 但是返回值类型一定要明确

python函数定义

def xxxx:
    print 'hello world'
    
def xxx(a,b):
   print(a,b)
   
def xxx(self,a,b)
   print a,b
   

由于没有分号和大括号所以一定要注意缩进,

2018/11/11 posted in  iOS

Swift中的字典转模型方法的迭代

模型可以是结构体也可以是继承自NSObject的类

class App: NSObject {
    var name:String = ""
    var age:Int = 0
    var boxDescription:String = ""
}


struct App :Decodable{
    var name:String
    var age:Int
    var description:String
    
    init(dic:[String:AnyObject]) {
        name = dic["name"] as! String
        age = dic["age"] as! Int
        description = dic["description"] as! String
    }
    
}

网络请求

 func fetchData(){
        let url = "https://maweefeng.github.io/appfeauterd.json"
        
        let session: URLSession = URLSession.shared
        
        let dataTask: URLSessionDataTask = session.dataTask(with: URL(string: url)!) { (data, response, error) in
            if(error == nil){
                var dict:NSDictionary? = nil
                do {
                    guard let jsondata = data else{return}
                    let app = try JSONDecoder().decode(App.self, from: jsondata)
                    print(app)
                    
//dict = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
//
//                    let app = App(dic: dict)
//                    print(app)

                    
                } catch {
                }
                print(dict!)
            }
        }
        dataTask.resume()
        
    }

JSONSerialization

之前我们字典转模型可能使用的方法可能是使用

  • model为结构体

    dict = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
    let app = App(dic: dict)
    print(app)
  • model为NSObject子类

    dict = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
    let app = App()
    app.setValuesForKeys(dict as! [String : Any])

JSONDecode

现在使用swift4新增了一种更方法的方法,就是JSONDecode

let app = try JSONDecoder().decode(App.self, from: jsondata)`

如果返回的data数据是一个object的字典,那么可以用上面的代码

如果data是一个包含app对象的数组,则可以简单的使用

let app = try JSONDecoder().decode([App].self, from: jsondata)`

另外一些情况,可能数组当中并不是每个键值对都一一对应,可能存在部分键值对缺失的情况,这样的话就会报错,有一个解决办法便是使那些可以为空的属性为optional,这样就可以解决错误问题。

2018/10/30 posted in  iOS

Swift单例写法

介绍swift的单例类写法之前,可以先回顾一下oc中单例的写法如下。

//单例类
@interface Manger : NSObject
+ (instancetype)sharedManger;
@end

//然后.m文件里写实现
@implementation Manger
+ (instancetype)sharedManger {
    static Manger *sharedManger = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedManger = [self new];
    });
    return sharedManger;
}

swift单例写法

  • 简单写法
import UIKit
//仿OC写法
class Manger: NSObject {
    static let instance: Manger = Manger()
    class func shared() -> Manger {
        return instance
    }
}
//简便写法
class Manger {
    static let shared = Manger()
}

这样对比下来swift真的是极大的减少了代码量,所有也会有一些人推荐如果开发新项目,建议使用swift开发,毫无疑问。

另外swift 还有一种类似oc的写法如下。

  • 复杂写法
class Singleton {
    class var sharedInstance: Singleton {
        struct Static {
            static var oncetoken: String? = NSUUID().uuidString

            static var instance: Singleton? = nil
        }
        DispatchQueue.once(token: Static.oncetoken) {
            Static.instance = Singleton()

        }
        return Static.instance!
    }
}

extension DispatchQueue {
    private static var _onceTracker = [String]()
    public class func once(token: String, block: () -> ()) {
        objc_sync_enter(self)
        defer {
            objc_sync_exit(self)
        }
        if _onceTracker.contains(token) {
            return
        }
        _onceTracker.append(token)
        block()
    }
    
    func async(block: @escaping ()->()) {
        self.async(execute: block)
    }

    func after(time: DispatchTime, block: @escaping ()->()) {
        self.asyncAfter(deadline: time, execute: block)
    }
}

之所以写DispatchQueue的扩展是因为swift3中取消了dispatch_once_t的使用
'dispatch_once' is unavailable in Swift: Use lazily initialized globals instead

建议使用懒加载的初始化全局替代。

因为原来自从swift 1.x开始swift就已经开始用dispatch_one机制在后台支持线程安全的全局lazy初始化和静态属性.所以static var背后已经在使用dispatch_once了.

2018/10/30 posted in  iOS

设置图片拉伸范围

- (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight 

这个函数是UIImage的一个实例函数,它的功能是创建一个内容可拉伸,而边角不拉伸的图片,需要两个参数,第一个是左边不拉伸区域的宽度,第二个参数是上面不拉伸的高度。

根据设置的宽度和高度,将接下来的一个像素进行左右扩展和上下拉伸。

注意:可拉伸的范围都是距离leftCapWidth后的1竖排像素,和距离topCapHeight后的1横排像素。

参数的意义是,如果参数指定10,5。那么,图片左边10个像素,上边5个像素。不会被拉伸,x坐标为11和一个像素会被横向复制,y坐标为6的一个像素会被纵向复制。

注意:只是对一个像素进行复制到一定宽度。而图像后面的剩余像素也不会被拉伸。

UIImage *img=[UIImage imageNamed:@"bubbleSelf.png"];
img=[img stretchableImageWithLeftCapWidth:15 topCapHeight:12];
UIImageView *imgView=[[UIImageView alloc]initWithImage:img];
[imgView setFrame:CGRectMake(10, 10, 200, 200)];
[self. view addSubview:imgView];
2018/10/20 posted in  iOS

Value and Reference Collections

值类型和引用类型的集合有所不同,看下图可知。

屏幕快照 2019-01-10 下午5.25.48

示例1      
let x = NSMutableArray()
x.add("🐵")
let y = x
y.add("🐶")    
print(x,y)
//结果
(
    "🐵",
    "🐶"
) (
    "🐵",
    "🐶"
)  
示例2     
var x:[String] = []
x.append("🐵")
var y = x
y.append("🐶")
    
print(x,y)
//结果  
["🐵"] ["🐵", "🐶"]

打印内存地址,探究 x 和 y 内存指向空间的关系。

示例3  
var x:[String] = []
x.append("🐵")
let xaddress = String(format: "%p", x)
print("xaddress===\(xaddress)")
var y = x
let yaddress = String(format: "%p", y)
print("yaddress===\(yaddress)")
y.append("🐶")
print("yaddress===\(yaddress)")

//结果
xaddress===0x600000434640
yaddress===0x6000004346a0
yaddress===0x6000004346a0
示例4  
let x = NSMutableArray()
x.add("🐵")
let xaddress = String(format: "%p", x)
print("xaddress===\(xaddress)")
let y = x
let yaddress = String(format: "%p", y)
print("yaddress===\(yaddress)")
y.add("🐶")
print("yaddress===\(yaddress)")

//结果
xaddress===0x6000004508c0
yaddress===0x6000004508c0
yaddress===0x6000004508c0

示例3中的 var y = x会使y开辟一个新的内存空间,内容是复制x的内容,y添加元素,从而不会影响到 x。

但是示例4对于 let y = x 没有开辟新的内存空间 还是指向同一块区域,所以当 y 发生改变的时候 x 的值也发生了改变。

结语

NS前缀的是引用类型,引用类型只存在一份内存,因此改变一个值就会引起另一个值的改变,这个需要引起注意。

2018/10/20 posted in  iOS

iOS开发一些小技巧

  • 去掉xcode黄色警告的方法

    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
    your code with warning
    #pragma clang diagnostic pop
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wunused-variable"
    your code with warning
    #pragma clang diagnostic pop
  • 控制器init方法执行时间

    _coverView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREENWIDTH, SCREENHEIGHT)];
    在控制器init方法中初始化一个大小为self.view.bounds的uiview必须等待视图出来的时候才会进行,要不然一直在主线程等待。
    如果直接给CGRectmake设置的话不用等界面加载就会执行,应该是一个主线程问题。
    这个时候如果直接去xib里面的控件,就会出现取不到的情况,取出来是nil。
  • 判断字符串为空

    今天遇到个问题,后台用java写的,给我传回一个空字符串,类型是NSNull,用
    stringWithFormat:转出来居然是@"<null>",
    注意,这不是空,NSNull和nil是不同的。需要用 xxx == [NSNull null]加以判断。
    去掉UITableView多余的空白行分割线
    有一种简单的方法
    self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
  • 如何将本地代码提交到远程git仓库

    * 第一步:建立git仓库 
    cd到你的本地项目根目录下,执行git命令 
    git init
    * 第二步:将项目的所有文件添加到仓库中
    git add .
    * 第三步:将add的文件commit到仓库
    git commit -m "注释语句"
    * 第四步:去github上创建自己的Repository
    * 第五步:重点来了,将本地的仓库关联到github上
    git remote add origin 仓库url
    后面的https链接地址换成你自己的仓库url地址,也就是上面红框中标出来的地址
    * 第六步:上传github之前,要先pull一下,执行如下命令:
    git pull origin master
    * 第七步,也就是最后一步,上传代码到github远程仓库
    git push -u origin master
    git push -u origin master -f //强制push
    每次如果有代码需要提交必须要add一下 并且在本地commit 打下log 再去拉取remote代码,
    然后push到remote的这样一个流程。
  • button 的文字显示不全

     [label setAdjustsFontForContentSizeCategory:YES]调节字体大小.
    
  • 从xib加载View

    // Instantiate the nib content without any reference to it.
    NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:@"EPPZPlainView" owner:nil options:nil];
    // Find the view among nib contents (not too hard assuming there is only one view in it).
    UIView *plainView = [nibContents lastObject];
    // Some hardcoded layout.
    CGSize padding = (CGSize){ 22.0, 22.0 };
    plainView.frame = (CGRect){padding.width, padding.height, plainView.frame.size};
    // Add to the view hierarchy (thus retain).
    [self.view addSubview:plainView];
  • 修改导航条的颜色

    self.navigationController.navigationBar.barTintColor = [UIColor redColor];```
    
  • 当某个UIView调用drawRect方法之后,它的默认背景就是黑色的

    解决办法有在初始化的时候设置backgroundColor

    - (void)awakeFromNib {
        [super awakeFromNib];
    self.backgroundColor = [UIColor clearColor];
    // Initialization code
    }
    - (id)initWithFrame:(CGRect)frame
    {
    self = [super initWithFrame:frame];
    if (self) {
    // Initialization code
    self.backgroundColor = [UIColor blueColor]
    }
    return self;
    }
  • 或者在drawRect里面写下如下代码

    - (void)drawRect:(CGRect)rect
    {
    // Drawing code
    [[UIColor blueColor] setFill]; // changes are here
    UIRectFill(rect); // and here
    }
  • 主线程死锁

        NSLog(@"1---%@",[NSThread currentThread]);
        dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"2");
    });
    NSLog(@"3");
    NSLog(@"1---%@",[NSThread currentThread]);
    dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"2");
    });
    NSLog(@"3");
    //打印顺序 1,3,2
    先加入的通知先收到通知
    先去执行通知的内容
  • 解决tableviewsection悬浮的问题

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
        CGFloat sectionHeaderHeight = 50;
    if (scrollView.contentOffset.y<=sectionHeaderHeight&&scrollView.contentOffset.y>=0) {
    scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
    } else if (scrollView.contentOffset.y>=sectionHeaderHeight) {
    scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0);
    }
    }
  • NSURLSession请求

     NSURLSession * session = [NSURLSession sharedSession];
        NSURLSessionDataTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    NSDictionary * dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
    NSLog(@"%@",dic);
    }];
    [task resume];
  • 实现点击按钮出发一次震动,比如“即刻”app的点赞

    导入AudioToolBox.framework
    在按钮的点击方法中执行
    AudioServicesPlaySystemSound(kSystemSo undID_Vibrate);//上面是震动方法 不灵巧
    可以使用最新的
    UIImpactFeedbackGenerator * zhend = [[UIImpactFeedbackGenerator alloc]initWithStyle:UIImpactFeedbackStyleLight];
    [zhend impactOccurred];
  • getter方法中为何不能用self

    有经验的开发者应该都知道这一点,在getter方法中是不能使用self.的,比如:

    - (NSString *)name
    {
    NSLog(@"rewrite getter");
    return self.name; // 错误的写法,会造成死循环
    }

    原因代码注释中已经写了,这样会造成死循环。这里需要注意的是:self.name实际上就是执行了属性name的getter方法,getter方法中又调用了self.name, 会一直递归调用,直到程序崩溃。

2018/10/15 posted in  iOS