0%

四舍五入保留两位小数

NSString *strOdds = [NSString stringWithFormat:@"%.2f", odds]; //保留两位小数
该方法会四舍五入保留两位小数,不足两位小数后面补0.如果想在四舍五入时去掉小数点后多余的0,可使用下面的方法:

1
2
3
4
5
6
7
8
9
10
11
- (NSString *)roundingAfterPoint:(NSInteger)position roudingMode:(NSRoundingMode)roundingMode
{
NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:roundingMode scale:position raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];

float f_price = self.floatValue;
NSDecimalNumber *ouncesDecimal = [[NSDecimalNumber alloc] initWithFloat:f_price];
NSDecimalNumber *roundedOunces = [ouncesDecimal decimalNumberByRoundingAccordingToBehavior:roundingBehavior];

NSString *rs = [NSString stringWithFormat:@"%@",roundedOunces];
return rs;
}

调用:
NSString *rs = [self roundingAfterPoint:2 roudingMode:NSRoundPlain];

在进行浮点型数值计算时最好统一类型,比如全部统一为float计算,或全部为double型计算.否则可能会出现误差,导致计算不一致.

浮点型数值取整

  • round:如果参数是小数,则求本身的四舍五入。
  • ceil:如果参数是小数,则求最小的整数但不小于本身.
  • floor:如果参数是小数,则求最大的整数但不大于本身.

Example:如果值是3.4的话,则round 3.000000, ceil 4.000000, floor 3.00000

另外,强制取整(int)3.4 = 3,直接丢弃小数位仅保留整数位,类似于floor函数.

swift封装打印函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let dateFormatter = DateFormatter()

//日志打印
func DLog<T>(_ message:T, file:String = #file, function:String = #function, line:Int = #line) {
#if DEBUG

//获取文件名
let fileName = (file as NSString).lastPathComponent

// 为日期格式器设置格式字符串
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
// 使用日期格式器格式化当前日期、时间
let datestr = dateFormatter.string(from: Date())

//日志内容
let consoleStr = "\(datestr) [\(fileName):\(line)][\(function)]\(message)"

//打印日志内容
print(consoleStr)

#endif
}

打印对象地址

1
2
3
4
5
6
7
extension String {
static func pointer(_ object: AnyObject?) -> String {
guard let object = object else { return "nil" }
let opaque: UnsafeMutableRawPointer = Unmanaged.passUnretained(object).toOpaque()
return String(describing: opaque)
}
}

使用

1
2
DLog(self)
DLog("self p:\(Unmanaged.passUnretained(self).toOpaque())")

打印如下:

1
2
2019-02-27 11:58:57.378 [ViewController.swift:30][viewDidAppear]<demo.ViewController: 0x1059c2170>
2019-02-27 11:58:57.383 [ViewController.swift:31][viewDidAppear]self p:0x00000001059c2170

不过还是推荐Swift - 日志框架XCGLogger的使用详解

layoutSubviews的调用时机

方法说明:

该方法在iOS 5.1及更低版本上不执行任何操作。 否则,该方法将使用你设置的约束来确定子视图的大小和位置。

该方法由layoutIfNeeded自动调用,所以我们不能手动去调用它.在需要重新布局子视图时,可以调用[self layoutIfNeeded][self setNeedLayout];

手动触发layoutSubviews

调用[self layoutIfNeeded][self setNeedLayout];

layoutIfNeededsetNeedLayout的区别:

调用 layoutIfNeeded 会马上执行 layoutSubviews 方法,立即更新视图的布局。但是调用setNeedLayout并不会马上执行layoutSubviews,而是等到runloop结束时调用.

自动触发layoutSubviews

如果一个viewA还没有被添加到父视图上面去,那么无论你对它进行什么操作(设置frame,添加子视图等等),它的layoutSubviews方法始终是不会被调用的.

阅读全文 »

动画-layoutIfNeeded父控制器的self.view,子控制器的子视图如果重写了-layoutSubviews也将动画布局.
需求:导航栏根据子VC的tableview的位移动画.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if (contentOffsetY > 0) {
//上滑

[self.headerContainerView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_topMargin).with.offset(startY - endY);
}];

[self.view setNeedsUpdateConstraints];
[self.view updateConstraintsIfNeeded];

[UIView animateWithDuration:0.3 animations:^{
self.contentScrollView.contentInset = UIEdgeInsetsMake(-endY, 0, 0, 0);

[self.view layoutIfNeeded]; //问题点

self.headerContainerView.headerView.matchOtherDesBgView.alpha = 0;
self.headerContainerView.headerView.team1NameLabel.alpha = 0;
self.headerContainerView.headerView.team2NameLabel.alpha = 0;

self.headerContainerView.headerView.teamLogoBgView.transform = CGAffineTransformMakeScale(0.5, 0.5);
CGAffineTransform currentT = self.headerContainerView.headerView.teamLogoBgView.transform;
CGAffineTransform translation = CGAffineTransformTranslate(currentT, 0, teamLogoBgViewOffsetY);
self.headerContainerView.headerView.teamLogoBgView.transform = translation;
}];
}

由于需要动画的改变父控制器里的view位移,又因为使用了xib,所以动画时需要在动画block里写[self.view layoutIfNeeded];但这句代码导致了子控制器的视图的-layoutSubviews方法被调用.于是子控制器的视图在布局时也会动画的进行(而这种效果不是你需要的从而变成了bug.)
这里的bug就是:子控制器里的tableViewCell因为重写了-layoutSubviews,而cell上的子控件KDGMatchDetailItemView也重写了-layoutSubviews.所以KDGMatchDetailItemView在布局时出现动画的往右移动.
解决办法:当导航栏收缩后,就不要执行[self.view layoutIfNeeded];加个判断条件:

1
2
3
if (contentOffsetY > 0  && self.contentScrollView.contentInset.top != -endY) {
//动画代码
}

系统的-layoutIfNeeded的实现应该是调用了视图的-layoutSubviews方法.父视图-layoutSubviews方法的调用可能会引起子视图-layoutSubviews方法的调用.

NexT is a high quality elegant Jekyll theme ported from Hexo Next. It is crafted from scratch, with love.

阅读全文 »

之前做过一个视频信息列表展示的模块,cell很简单就是左边图片,右边文字信息。当时用的SDWebImage加载图片并没有看到图片错乱的情况。但是,如果是自己写的图片下载器,不注意处理是会导致图片错乱的。

今天写了个Demo,验证及解决这个问题。
实验环境:cell依然是左边图片,右边文字信息。图片两张,一张大图片A(风景),一张小图片B(人物),采用自己实现的原始图片下载器异步下载,block里回调设置cell的图片。要求偶数行的图片是风景,奇数行的图片是人物。
整个界面期望如下:

但实际可能出现bug,如下图:

数据错乱原因分析

cell上的数据错乱显然是由于cell的重用导致的。由于图片是异步下载的,下载完成才给cell设置,但是在这个过程中用户可能会上下滑动,滑动的时候会导致cell的重用,比如第0行是设置大图片的,第11行是设置小图片的,用户在滑动的过程中,因为cell的重用第11行的cell可能使用的是第0行的cell,这时第0行的block回调设置的cell和第11行的block回调设置的cell是同一个,即cell的重用导致两个block回调时设置的其实是同一个cell上的imageView。这就是问题的关键。

因为图片是异步下载的,你也不知道哪个block会先回调,如果小图片的block先回调那么这个cell的图片就先被设置为小图片,如果后来大图片的block回来了,那么你会看到图片被替换成大图片,这种情况还算比较好,但如果大图片下载失败或者小图片的block最后回调,那么你看到的将是小图片加大图片的文字信息,这时数据就错乱了。

如何解决

如果不重用cell,当然是可以解决该问题的,但是内存肯定会浪费不少。

解决的方案有两种:

方案一:在下载完成的回调里进行区分,如果不一致则不设置imageView。

阅读全文 »

OC的变量:

OC有4种基本数据类型:char,int,float,double。它们又有一些限定词:signed,unsigned,short,long,long long。

实例变量和成员变量是一个概念。

@property

@property指令作用:声明了一个属性及属性的两个访问器方法。

如果在@implementation块里@synthesize和@dynamic都没写,那么编译器会帮你自动添加@syntheszie property = _property;有一种情况除外:子类属性和父类属性同名.此时会报警告:

“Auto property synthesis will not synthesize property ‘delegate’; it will be implemented by its superclass, use @dynamic to acknowledge intention”

这个时候就需要自己实现属性了.两种常用解决办法:

1
2
3
1.@dynamic delegate; //表示使用父类的实现,此时子类内部将无法访问_delegate.

2.@synthesize delegate = _delegate; //子类内部可以访问_delegate.
阅读全文 »