iOS diary140807

iOS7处理UiTableview转屏时定位的问题

首先,要先确认下uitableview在转屏过程中先后回调了哪些接口:

1、系统首先调用了uiviewcontroller的
    - (void)willRotateToInterfaceOrientation:duration:
2、之后调用了uitableview的
    - (CGFloat)tableView:heightForRowAtIndexPath:
    用以确认每个cell的高度
3、在调用uitableview的tableView:heightForRowAtIndexPath:过程中,uitableview又一一调用了对应cell的setFrame方法,以修改对应的高度。
    同时,由于frame的变化,使得每个cell分别调用了其中的layoutSubviews方法。
4、uitableview继承自uiscrollview,在转屏后会保证contentoffset位置不变,所以,在第3步,uitableview同时计算了转屏后会显示的cell,如果cell在转屏前的页面不存在,系统会调用uitableview的
    - (UITableViewCell *)tableView:cellForRowAtIndexPath:
5、调起转屏动画,系统会调用uiviewcontroller的
      -(void)willAnimateRotationToInterfaceOrientation:duration:
    和-(void)didRotateFromInterfaceOrientation:

两种不太好的解决方式

项目需要,uitablevlew在横竖屏下的cell的宽度和高度均有变化,为了保证在转屏后,用户看到的cell依旧可见,先后采用了以下方法:

  1. 转屏前(第1步)记住contentoffset,转屏后(第5步)再通过转屏前后cell高度的不同计算新的offset,然后设置uitableview的contentoffset
  2. 转屏前(第1步)记住第一个cell的indexpath,转屏后(第5步)滑倒对应位置

以上两种方法均可以实现转屏定位的效果,但都有问题:

  1. 如果在-(void)willAnimateRotationToInterfaceOrientation:duration:中设定位置,则转屏为“空转”,即转动过程中页面是白色的,动画完成后才会加载对应的cell。这是因为系统在第4步已经计算过即将显示的cell,并为其init过了,此时不会调用
  2. 如果在-(void)didRotateFromInterfaceOrientation:中设定位置,则转动过程中的页面UI既不是转动前页面的样式,也不是转动后的样式,而是第3步和第4步中系统计算出来的cell。而且,转完后会出现明显的页面闪动

最终解决办法

在分析过uiviewcontroller和uitableview在转动过程中经历的几个过程后,明白是需要采用伪装的方式让系统通过它的计算方式(第3、4步)获得到和转屏前相同的cell,这样转屏的动画和结果的定位就都准确了。
也就是在- (CGFloat)tableView:heightForRowAtIndexPath:这个方法上做文章,想办法让系统计算的横竖屏相同的contentoffset对应相同的indexpath,欺骗完成后,最后再绘制出真实的即可。
但是:iOS7开始系统竟然已经实现的相应的方法!

- (CGFloat)tableView:estimatedHeightForRowAtIndexPath:

这个估计值就是一种欺骗的手段,让页面先伪装一种估计值,cell显示(第4步cellFor)后再从heightFor取精确值,也就是在第1步和第2步之间添加了这个第1.5步!具体步骤如下:

1.在第1步中添加如下代码,获取预估的高度
 NSIndexPath* indexPath = [self.m_tableview indexPathForRowAtPoint:self.m_tableview.contentOffset];
if (indexPath.row > 0) {
    _estimatedHeight = self.m_tableview.contentOffset.y/indexPath.row;
}
2.在第1.5步添加
if (_estimatedHeight==0) {
    return defaultHeight;  //即转屏前row==0
}else{
    return _estimatedHeight;
}

iOS7如何隐藏状态栏

在viewWillAppear中添加:

self.navigationController.navigationBar.translucent = NO;
if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
    [self prefersStatusBarHidden];
    [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];
}

覆写prefersStatusBarHidden方法

- (BOOL)prefersStatusBarHidden
{
    return NO;//隐藏为YES,显示为NO
}

iOS diary140807

自定义uitableviewcell的宽度,以及一些诡异的问题处理

项目需要,希望uitableview处于页面中中间部分,但滚动指示条依然在整个window的边缘,解决办法无非两种:

  1. 把cell做小
  2. 把指示条放远

先说第二种方法:发现可以设置uitableview的scrollIndicatorInsets,从而任意改变指示条的位置,但是,这个位置只能在uitableview范围内!
那就只剩下第一种方法了,比较简单的就是自定义cell,把里面的内容都做一些内边距,这样看起来效果就像居中了~但问题在于,如果如此,cell的点击、删除、编辑等等uitableviewcell默认的一些系统方法展示出来的UI效果,都会是整个宽度的。
cell做小另一种方法就是修改scrollview的contentinset,因为uitableview继承自uiscrollview,这种方式的问题在于:依然不能改变cell的宽度,cell的宽度会默认设置为uitableview的宽度而不是scrollview的。
最后,采用了网上常用的覆写uitableviewcell的setFrame方法来完成这个工作,实际效果非常好:

- (void)setFrame:(CGRect)frame {
    frame.origin.x += ROOT_PORTRAIT_CONTENT_MARGIN_LEFT;
    frame.size.width -= 2 * ROOT_PORTRAIT_CONTENT_MARGIN_LEFT; 
    [super setFrame:frame];
  }

但是在iOS7下,使用该方法会出现一些诡异的问题:

    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0xc094560 h=--& v=--& H:[UITableViewWrapperView:0xc68c390(768)]>",
    "<NSAutoresizingMaskLayoutConstraint:0xbfd4250 h=-&- v=--& TimeLineLCellImg:0xbfa97e0.width == UITableViewWrapperView:0xc68c390.width - 972>"
)

    Will attempt to recover by breaking constraint 
<NSAutoresizingMaskLayoutConstraint:0xbfd4250 h=-&- v=--& TimeLineLCellImg:0xbfa97e0.width == UITableViewWrapperView:0xc68c390.width - 972>

通过研究iOS7的修改点,发现现在uitableviewcell的superview是UITableViewWrapperView,而不是uitableview。查看log日志,发现是由于我们在修改frame的时候和设置的AutoresizingMask冲突,虽然最终显示的正确,但是吐出warning日志还是需要处理。
找到冲突原因,修改就比较容易了,让uitableviewcell的frame为我们自己设置的,而不是采用AutoresizingMask的方式自动生成,因此在setframe中添加代码:

[self setAutoresizingMask:(UIViewAutoresizingNone)];

注意:不能通过设置[self setTranslatesAutoresizingMaskIntoConstraints:NO]而直接禁止Autoresizing,因为这样必须设置layoutconstraint采用autolayout的方式。通过log看出是将cell的宽度和它的superview的宽度做了autoresizing,只需打断这个设置即可。

##iOS根据class name生成class实例:
NSString *className = @”AbcViewController”;

if (NSClassFromString(className)) {

    Class aClass = NSClassFromString(className);
    id instance = [[aClass alloc] init];

    if ([instance isKindOfClass:[UIViewController class]]) {

        [(UIViewController *)instance setTitle:@"New Title"];
        [self.navigationController pushViewController:(UIViewController *)instance animated:YES];
    }
}

JAVA和Ruby的异同

由于JavaWeb在开发过程中太重,配置和开发周期比较长,不适合一个比较小的移动互联网项目,决定学习ROR,首先是ruby语言,作为一个只懂java和objective-c的客户端开发,通过比较java和ruby的异同点有利于很好的进行过度。

相同点

  • 都有内存管理和垃圾回收的机制
  • 所有对象都是强类型对象
  • 都包含public private 和 protected的方法
  • 都能内嵌说明文档,只不过ruby的说明文档叫rdoc。但在代码中的阅读方式和javadoc基本一致

不同点

  • ruby是解释型语言,不需要编译。java是需要编译才能执行的
  • ruby有很多种第三方的图形化编程工具,比如WxRuby,FXRuby等等
  • ruby中一段代码(包括class)的结束标志是end,而java使用大括号包起来
  • ruby中用require来替换import使用
  • ruby中所有成员变量都是私有的,在外部访问必须经过方法
  • 在使用ruby的进行方法调用时,其方法的成对括号是不需要写的,当然,写上也没有问题
  • ruby中什么都是对象,包括java中的基本数据类型
  • ruby中没有静态类型:无static
  • ruby中所有的变量名字只是一个标签(label),没有特定的类型关联他们:弱类型?
  • 变量初始化不需要定义类型:i.e. ruby : a = [1,2,3] java: int[] a = {1,2,3}
  • 单元测试不需要单独写,只需要调用方法就可以。同时,单元测试甚至可以在你的程序代码运行前,就能发现异常
  • 实例化:ruby: foo = Foo.new(“hi”) java: foo = new Foo(“hi”)
  • ruby中的构造函数被固定为initialize,而java中的构造函数为class name
  • ruby中使用“mixins”代替java中的interface
  • 使用YAML替代XML(xml的一个超集)
  • 使用nil代替null
  • == 和 equals() 与 java中的对应方法意思正好相反,比较是否是同一个对象用equals,比较两个对象的值是否相等用==

Mac下 尝试用Pelican和github构建个人博客

系统环境:OSX10.9.4
参考教程:http://www.dongxf.com/3_Build_Personal_Blog_With_Pelican_And_GitHub_Pages.html
        http://x-wei.github.io/pelican_github_blog.html

一直有做一个技术博客的打算,所以风风火火的github page肯定不能错过。非常喜欢markdown语言,平时写文档都是用它的,因为感觉迁移毫无压力,所以决定动手搞一个。

1、安装pelican

pelican是什么,麻烦请自己google,python不懂的麻烦看一下基础教程,有好处。

mac下对于pelican的安装非常方便,因为已经集成陪python和pip,先后在命令行下执行以下两条命令:

sudo pip install pelican
sudo pip install Markdown

在这里,我多次安装都失败了,失败后,命令行给提示了log的输出位置,手动查看error log,发现原来是权限问题,需要root权限,所以此处一定要加上sudo,网上很多教程都没有,所以个人需要养成自己解决问题的习惯。

2、创建blog(无责任copy)

创建一个 Blog 目录

mkdir myblog
cd myblog

快速创建 Blog

pelican-quickstart

根据提示一步步输入相应的配置项,不知道如何设置的接受默认即可,后续可以通过编辑pelicanconf.py文件更改配置。

以下是生成的目录结构:

myblog/
├── content              # 存放输入的源文件
│   └── (pages)          # 存放手工创建的静态页面
├── output               # 生成的输出文件
├── develop_server.sh    # 方便开启测试服务器
├── Makefile             # 方便管理博客的Makefile
├── pelicanconf.py       # 主配置文件
└── publishconf.py       # 发布时使用的配置文件

3、写文章

默认写文章的目录在刚刚生成目录中的content目录下,该目录位置可以在pelicanconf.py中进行配置,随意更换。

在文章的开始需要标记文章的“标题”“作者”“时间”“标签”等信息,添加方法如下

Title: Mac下 尝试用Pelican和github构建个人博客  
Date: 2014-07-31 17:50  
Category: mac  
Tags: mac, pelican, github  
Author: BillChai

下面就是内容了,你可以使用markdown标记语言进行书写

下面再介绍几个简单的命令

make html ------就是把content目录下的md文件解释成html,并输出到output目录下
make publish ------不仅生成文章的html,同时将套用主题theme
make serve ------启动一个本地http server,localhost:8000可以直接访问output下的数据

4、一键配置,发布到github

由于默认output文件夹默认是没有git的repository的,所以需要添加并绑定到github对应的repository中,添加步骤

cd output
git init
git add .
git remote add origin https://github.com/droison/droison.github.io
git pull -u origin master

以上代码在运行时,如果有任何问题,请自行google github使用(此处提供一个链接,点此跳转),中途可能会出现输入github帐号密码的情况。

下一步,添加一键发布到github的配置,修改Makefile(注意,python代码要注意缩进)

publish:
    $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)

github: publish
    cd $(OUTPUTDIR) ; git add . ;  git commit -am 'your comments' ; git push origin master

如此,每次写完博客,只需 make github,就完成一键上传了

5、更换主题

安装主题,比如bootstrap2:

git clone https://github.com/getpelican/pelican-themes.git
cd pelican-themes
pelican-themes -i bootstrap2

选择主题,在pelicanconf.py中添加

THEME = 'bootstrap2'

6、添加图片,添加评论

研究中

其它:Code block 红框问题

在提交blog时,markdown语法中的code块对中文出现红框问题,真是恶心了好久,通过查看html源码发现,问题处在一个err的css class中,该css定义在主题文件的pygment.css中,将下面这行注释掉,然后重新导入一遍主题就可以了

.highlight .err { border: 1px solid #FF0000 } / Error /

根据内容可以看出这是规定书写错误的代码的文本样式,可是平时代码中有的字符也会被识别成错误的代码,所以就造成了代码块中时不时出现恼人的红框的问题,原因清楚了,现在要解决它就很容易了,直接删掉那行中的border样式就可以了。如果你跟我一样完全不需要给这类代码规定特殊样式的话,直接删掉那行即可。
重新导入主题需要先删除原主题:

pelican-themes -r bootstrap2
pelican-themes -i bootstrap2

mac中安装maven3.2.2

本机OS X:10.9.3,安装XCode,

1、下载maven:http://maven.apache.org/

下载了lastest version,文件名:apache-maven-3.2.2-bin.tar.gz

双击解压

将解压后的apache-maven-3.2.2文件夹移到/Users/song/ (~目录)
即:/usr/local/maven/maven3.2.1

2、配置环境变量

终端中执行 open -e ~/.bash_profile  
然后输入 
    M3_HOME=/usr/local/maven/maven3.2.1
    PATH=$M3_HOME/bin:$PATH
    export M3_HOME
    export PATH
保存退出。

3、查看是否成功

新打开一个终端窗口
通过
    echo $M3_HOME
    echo $PATH
可查看刚设置的环境变量。

同时,输入
mvn -version
可以看到maven的版本信息了。

4、重新设置本地Repository的位置

在maven安装目录的conf目录下,open -e settings.xml
第54行左右有这么一段:

 <!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository
<localRepository>/path/to/local/repo</localRepository>
-->
在这段之后依样画葫芦加一行
<localRepository>具体的绝对路径</localRepository>

即可
保存退出。

Android 开发锁屏屏蔽Home键、原生锁屏界面

最近开发锁屏应用,需要屏蔽返回、音量、Home键,返回和音量很好弄,关键是Home键如何搞。花了很多心思,终于搞定了,项目代码涉密,不方便全传上来,下面主要介绍这个思路。

一、原理

Android2.3版本以前通过AttachWindows就可以很简单实现屏蔽,下面着重说4.0以后的。
访谈及结果分析

1、向系统注册锁屏界面Activity为启动器

这块是需要将该应用设定为桌面程序:

1
2
3
<intent-filter >
<category android:name="android.intent.category.HOME" />
</intent-filter>

2、设置该应用为默认启动器

多启动器共存的情况下当HOME事件发生系统会轮询并向用户询问使用哪个启动器并是否作为默认启动器,这一部分不可控,可做为用户引导,让用户选择本应用为默认启动器

3、选择真正的系统桌面

第2步骤中若用户选择的是其它桌面作为默认启动器,则无法屏蔽HOME键,此为用户体验问题了。可以在APP中做提醒引导用户去执行第2步操作来实现本应用作为默认启动器;
当第2步中本应用被选作默认启动器,则系统HOME事件将从此会导向本应用做处理:

注意:此处应该通过getIntent().getCategories()来获取是否是来自与Home事件的启动项。
android.intent.category.LAUNCHER:点击图标启动
android.intent.category.HOME:点击Home启动

因为本应用不具备桌面管理功能,故第一次HOME事件发生时本应用应该拦截HOME事件,轮询系统桌面管理应用提供给用户选择(得到的list会包含本应用,应remove掉,剩下的给用户选择),记下用户的选择,这样,在非锁屏状态时的HOME事件来到星酷时应当跳转到用户选择的桌面上去,完成“回到桌面”功能,而当锁屏状态,本应用正处于最前台进程,可忽略HOME事件,这就完成了“屏蔽HOME”功能了。
判断锁屏是否为前台方法为,判断是否在内存堆栈的栈顶:

1
2
3
ActivityManager manager=(ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
String name = manager.getRunningTasks(1).get(0).topActivity.getClassName();
name.equals(LockActivity.class.getName())

轮询系统启动器/桌面管理应用方法为:

1
2
3
Intent mIntent = new Intent(Intent.ACTION_MAIN);
mIntent.addCategory(Intent.CATEGORY_HOME);
packageManager.queryIntentActivities(mIntent, 0);

4、屏蔽系统原生锁屏界面

首先需要动态监听系统的开/关屏事件(注意是动态注册监听,不能在manifest中固定注册,否则无法实现),再则事件发生时启动本应用的锁屏Activity,在setContentView之前执行

1
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

5、锁屏状态下电话事件处理

通话过程中由于距离传感器的原因,会造成屏幕关闭,所以也会触发锁屏,这样便无法进行操作。因此通话过程中应当禁止启动锁屏界面。处理方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
TelephonyManager phoneManager = (TelephonyManager) this.getSystemService(TELEPHONY_SERVICE);
// 手动注册对PhoneStateListener中的listen_call_state状态进行监听
phoneManager.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
break;
case TelephonyManager.CALL_STATE_RINGING:
finish();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
finish();
default:
break;
}
super.onCallStateChanged(state, incomingNumber);
}
}, PhoneStateListener.LISTEN_CALL_STATE);

6、阻止锁屏界面出现在长按Home键的“最近使用应用列表”中

在manifest中对锁屏Application加

1
android:excludeFromRecents="true"

为避免某些异常,最好在锁屏的Activity添加

1
2
android:noHistory="true"
android:launchMode="singleTask"

7、项目需要的权限

1
2
3
4
5
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />

综上,一个锁屏界面需要的操作流程大致如下:

  • listenAndHandleScreenOffAction();
  • listenAndHandlePhoneAction();
  • dismissKeyguard();
  • handleHomeIntent();
  • processViewEvents();
  • handleUnlockEvent();

二、关键代码

暂时无法提供