今天在经历数番折磨之后,终于成功的把应用提交到iTunes Connect上面了,看到状态变成“Upload Received”,终于送了一口气,可是没过一会儿,刷新页面一看,状态就变成红色的错误“missing localized screenshots”

按字面意思就是缺少截屏,但是我的3.5inch 和 4inch的图都上传了4张,没问题啊。上网检索了一番,发现可能的可能情况如下:

  • iPhone5的没上传
  • 写了device适配iPad,但是没上传iPad相关的截图

我在应用详情里面编辑截屏,往4inch里面再上传了一张截屏,然后保存。保存的时候,提示我iPad的截屏没有上传。这个时候我就奇怪了,因为我这个应用是打算只发布到iPhone的。

回到xcode里面一看,玩蛋,devices里面写的是universe。现在只能看看iPad下面能不能正常工作了,可惜不能。

现在的问题就变成了如何取消已经上传的应用并重新上传

第一步:取消应用

进入itunes connect -> Manage Your Apps -> 点击对应应用 -> View Details -> Version Information -> Binary Details -> 进入之后,右上角有一个Reject This Binary

点击之后,会要求表明再次上传的包是否有修改,是否加密,以及广告策略

完成之后就能继续上传

第二部:重新上传

在xcode里面,点击对应的target -> General -> Devices 选择iPhone。重新archive,上传即可。

参考文章:
http://miraclei.net/index.php/2013/01/app-store上传应用程序后执行取消操作/

http://stackoverflow.com/questions/12760709/missing-localized-screenshots-error-on-itunes

http://stackoverflow.com/questions/17259886/itunes-connect-complains-missing-localized-screenshot-despite-iphone-5-screens

清除所有的mobileprovision

通过终端进行删除

首先cd到目录”~/Library/MobileDevice/Provisioning Profiles”

cd ~/Library/MobileDevice/Provisioning Profiles/

然后删除里面所有的mobileprovision文件

rm *.mobileprovision

恢复需要的账号里的mobileprovision

这样再看xcode的时候,所有的mobileprovision,发现没有任何provisioning profile了。这个时候不需要再次的去一个一个的添加。

访问XCode的Preferences>Accounts,在Apple IDs里面找到你的帐号,选中后,在右侧,在Name下面会有一行描述。双击。在弹出窗口里面,有个刷新按钮,点击之后,属于这个帐号的provisioning profile就会再次出现

图文步骤

1. 进入Preferences

进入Preferences

2. show details

show details

3. 刷新

刷新按钮

今天在项目开发中,需要给一个UILabel设定一个NSMutableAttributedString,用来显示多种颜色。

NSString * originInfo = [NSString stringWithFormat:@"库存:%@/%@",[data objectForKey:@"libaoResidue"],[data objectForKey:@"libaoTotal"]];
NSRange range = NSMakeRange(3,originInfo.length - 3);
UIColor * blue = [UIColor blueColor];
            
NSMutableAttributedString * info = [[NSMutableAttributedString alloc] initWithString:originInfo];
[info addAttribute:NSForegroundColorAttributeName
             value:blue 
             range:range];

这样的显示效果很正常,可是在后面修改颜色的时候,把blue的定义修改成这样

UIColor * blue = [UIColor colorWithRed:30/250 
                                 green:181/250 
                                  blue:247/250 
                                 alpha:1];

文字就一直都显示为黑色,没有半点作用。

后面仔细看了下,才明白,UIColor的colorWithRed:green:blue:方法,接受的参数是CGFloat,而30/250的结果不是CGFloat,因此正确的定义方式应该是这样:


UIColor * blue = [UIColor colorWithRed:30.0f/250.0f 
                                 green:181.0f/250.0f 
                                  blue:247.0f/250.0f 
                                 alpha:1];

原理

UIScrollView的滚动,真正滚动的不是scrollView本身,而是它的content。scrollView根据自身的frame.size和它的content的size,从而计算content的滚动。正常情况下,content的size是大雨scrollView的,这样就能够任意的滚动

任意滚动

所以在scrollView的content的宽度小于或等于scrollView.frame.size.width的时候,没有办法做横向的滚动,因为content已经全部展示在scrollView中了,没有滚动的必要。

禁止了横向滚动

同理,在scrollView的content的高度小于或等于scrollView.frame.size.height的时候,没有办法做纵向的滚动。

禁止了纵向滚动

实现

现在咱们要做的就是在代码里面针对性的进行修改,以禁止竖向滚动为例:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 使得contentSize的height等于_scroller的高度,就能禁用纵向滚动
    self.scroller.contentSize = CGSizeMake(640, _scroller.frame.size.height);
    _scroller.delegate = self;
}

参考文献

UIScrollView Class Reference

Disabling vertical scrolling in UIScrollView

起因

现在IOS平台下的应用开发都已经趋向IOS7的风格,哪怕系统本身不是IOS7的。就比如我们这个应用,开始的时候使用按钮来实现类似UISegmentedControl的UI和功能,但是后期,需求一改,要求用原生的实现。整个应用都是IOS7风格的,在IOS7下,使用原生的倒也简单,但是在IOS6下面就困难了,立体化的控件是那样的突兀。

没办法,只好在一起走上Google之路,不过总算有个好结果。

设置UISegmentedControl的背景

在看下文之前,先看这里,没有这个在stackoverflow上面的精彩回答,也就不会有这篇文章了,万分感谢!

有了上面的基本技术知识储备,再上一张最终效果图,方便描述思路
最终效果图

从stackoverflow的那个回答,可以知道,自定义一个UISegmentedControl需要的素材有三种类型:选中时的按钮图片、没选中时候的按钮图片和两个按钮中间的图片(这里说的是只有左右两个按钮的情况)

在那个回答中,作者很耐心的给出了针对性的5张图片,但是在我的最终效果里面,中间的那个线是一直不变的,不管两边如何变化,所以我只需要三张图片
需要的素材

再然后就是复制修改代码了:

/* Unselected background */
UIImage *unselectedBackgroundImage = [[UIImage 
                           imageNamed:@"segment-unselected-item.png"] 
          resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
[[UISegmentedControl appearance] setBackgroundImage:unselectedBackgroundImage
                                           forState:UIControlStateNormal
                                         barMetrics:UIBarMetricsDefault];

/* Selected background */
UIImage *selectedBackgroundImage = [[UIImage 
                           imageNamed:@"segment-selected-item.png"] 
          resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
[[UISegmentedControl appearance] setBackgroundImage:selectedBackgroundImage
                                           forState:UIControlStateSelected
                                         barMetrics:UIBarMetricsDefault];

/* Image between two unselected segments */
UIImage *bothUnselectedImage = [[UIImage 
                           imageNamed:@"segment-middle.png"] 
          resizableImageWithCapInsets:UIEdgeInsetsMake(15, 0, 15, 0)];
[[UISegmentedControl appearance] setDividerImage:bothUnselectedImage
                             forLeftSegmentState:UIControlStateNormal
                               rightSegmentState:UIControlStateNormal
                                      barMetrics:UIBarMetricsDefault];

/* Image between segment selected on the left and unselected on the right */
UIImage *leftSelectedImage = [[UIImage 
                           imageNamed:@"segment-middle.png"] 
          resizableImageWithCapInsets:UIEdgeInsetsMake(15, 0, 15, 0)];
[[UISegmentedControl appearance] setDividerImage:leftSelectedImage
                             forLeftSegmentState:UIControlStateSelected
                               rightSegmentState:UIControlStateNormal
                                      barMetrics:UIBarMetricsDefault];

/* Image between segment selected on the right and unselected on the left */
UIImage *rightSelectedImage = [[UIImage 
                                     imageNamed:@"segment-middle.png"] 
                    resizableImageWithCapInsets:UIEdgeInsetsMake(15, 0, 15, 0)];
[[UISegmentedControl appearance] setDividerImage:rightSelectedImage
                             forLeftSegmentState:UIControlStateNormal
                               rightSegmentState:UIControlStateSelected
                                      barMetrics:UIBarMetricsDefault];

在完成上述代码之后,运行,就能看到UISegmentedControl的背景已经成功的IOS7风格化了
完成UISegmentedControl的背景

但还是有些瑕疵,在选中的segment上,字体是白色的,很难识别,而且字体对中文的支持不友好,一大一小的,所以还需要一些美化工作

一些美化

万能的stackoverflow,再次从它那边找到了完美方案。对于中文,使用Helvetica字体是一个不错的选择,然后可以选择性的去掉不好看的阴影,别的设定就是看个人需要,随意增删了。下面给出我的设定:

NSDictionary *selectedAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                [UIFont fontWithName:@"Helvetica" size:14.0f], UITextAttributeFont,
                                [UIColor colorWithRed:11/255.0 green:96/255.0 blue:175/255.0 alpha:1],
                                UITextAttributeTextColor,
                                [UIColor clearColor], UITextAttributeTextShadowColor,
    nil];
[segment setTitleTextAttributes:selectedAttributes forState:UIControlStateSelected];

NSDictionary *normalAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                       [UIFont fontWithName:@"Helvetica" size:14.0f], UITextAttributeFont,
                                       [UIColor whiteColor], UITextAttributeTextColor,
                                       [UIColor clearColor], UITextAttributeTextShadowColor,
                                       nil];
[segment setTitleTextAttributes:normalAttributes forState:UIControlStateNormal];

好了,一个IOS7风格的UISegmentedControl完成了,下面看看实现的和真正原生的IOS7风格的UISegmentedControl有什么区别不

区别

边框的细线有点偏粗,字体偏大,圆角过小。这些还是明天再改吧,困了…

后续新增

  • 修改了segment-unselected-item.png的细线宽度,由2px改成1px(@2x的由4px改成2px)
  • 字体尺寸修改为12.0f
  • 修改segment-unselected-item@2x.png和segment-selected-item@2x.png这两张图片的圆角,由5px增加为10px(根据IOS7的风格,UISegmentedControl的圆角弧度是10px)

在上一张最终的结果比较

最终比较

项目需求

最近开发的项目,需要一个webView,同时这个webView会需要引入一些项目中的资源:

  • 一个本地的html文件,作为webView的模板
  • 两张loading图片,在图片未加载的时候进行占位
  • jquery.js,scrollLoading.js 也是本地的,实现滚动加载图片功能

然后就开始了漫长的Google历程。

在webView中引入本地的html文件

这里最主要的一个webView的方法是:loadHTMLString:baseURL: 把HTML文件的内容以字符串的形式加载到webView里面,然后解析。


// get the model which is a html file for the webView
NSString * htmlPath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
NSString * htmlCont = [NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil];

// load the html file to webView
[_webView loadHTMLString:htmlCont baseURL:nil];

通过上述方法,很方便的就能把一个HTML文件加载到webView中,很简单吧,接下来,来点进阶功能!

在webView中引入本地的image文件

这个功能的实现,很大程度上是借鉴了这篇文章:UIWebView – Loading External Images and CSS。大家可以去看看这篇文章,或者看我下面的继续描述。

这个功能的实现是承接上面那个方法的进一步扩展,最关键的是那个baseURL。先看实现代码:


// get the model which is a html file for the webView
NSString * htmlPath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
NSString * htmlCont = [NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil];
    
// 获取当前应用的根目录
NSString *path = [[NSBundle mainBundle] bundlePath];
NSURL *baseURL = [NSURL fileURLWithPath:path];
    
// 通过baseURL的方式加载的HTML
// 可以在HTML内通过相对目录的方式加载js,css,img等文件
[_webView loadHTMLString:htmlCont baseURL:baseURL];


在object-c里面通过如上面的方式加载HTML文件,指定了baseURL的值为程序的bundlePath,然后在HTML文件里面就可以自由的通过直接书写标签的方式加载图片图片文件了

;

要注意的是:所有在应用内的资源文件都是在baseURL的根目录也就是此代码中的bundlePath的根目录,所以图片资源,不管在项目里面放在哪个目录结构下,在HTML内引用的时候,都是直接根目录的。

baseURL到底是什么东西?对此,我也很好奇,所以我NSLog了代码里面的baseURL,然后得到的结果是:file:///Users/(用户名)/Library/Application%20Support/iPhone%20Simulator/7.0.3/Applications/(一些大写字母加连字符加数字的序列号)/(应用名).app/ 。然后我在终端里面找到这个目录,打开一看,发现都是一些HTML,image,txt等静态资源。

bundlePath和其中的内容展示

至此,在webView中插入本地image资源的功能已经实现了,下面是更有挑战性的功能:添加js文件

添加本地js文件到webView中

这个实现说起来其实很简单,因为不需要任何代码层面上的修改,只需要按上面添加image的方式,在script的src里面直接写js的文件名即可。

但是如果直接这样写,你就会发现js资源根本没有被加载。到底image和js有什么区别?看上面的图片,可以看到默认在bundlePath里面是没有我引入到工程里面的jquery.js和scrollLoading.js的。那么,这个是不是导致js资源没有被正确加载的原因?

在这篇文章:How to load a local .CSS file & JavaScript resources using iPhone UIWebView Class和这篇文章iPhone基于lightbox的图片放大特效和网页布局中,都提及到一个

Select .js file and in the “Detail” view unselect the bullseye column indicating it is compiled code

In the “Groups & files” view expand the “Targets” tree and expand the application then go to “Copy Bundle Resources” and drag the *.js files into it.

方法是有了,可是这种英文的描述,还没有附加图片,实在是让人看不懂,大致知道的就是:js文件在xcode里面,默认是一种需要被编译的文件,这就导致它不会被放到我们刚刚放到的BundlePath(更专业的名称应该是Bundle Resources)里

所以要解决的问题是,怎样才能使得js文件不被编译并且放到Bundle Resources中。

期间为了理解上面那两句英文而Google的经历就不说了,直接说结果吧。

在xcode里面,每个project都有至少一个Targets(多个的也有,但是我不懂),在Targets里面(打开Targets的方式是在左侧栏,点击project,在中间的内容区,就会出现project和Targets),存放了一些资源文件

Build Phases下可以看到,跟本次内容关联最大的有两项:Compile SourcesCopy Bundle Resources

在没修改的情况下,展开Compile Sources就能看到找了很久的jquery.js和scrollLoading.js

打开Targets(基于xcode5的界面)
打开Targets(基于xcode5的界面)

展开Compile Sources后能看到两个js文件
展开Compile Sources后能看到两个js文件

接下来要做的很简单,从Compile Sources中删除两个js文件,再在Copy Bundle Resources中添加这两个文件,一切搞定。

想来(偷懒,不想Google继续深入了解了),Compile Sources是放置那些需要被编译的文件,.h,.m和冤枉的.js文件等等,而Copy Bundle Resources里面放的是一些资源文件,在程序在运行时会引入的,同时在项目打包之后也依旧存在的文件。

其他格式的资源文件,在添加的时候也大致就是这个流程,不重复说了。

说完了,就这么多了,也欢迎补充一些我说的不全面或者遗漏的地方,毕竟我还是个新手

In MainViewController.m, add:


- (void)viewDidLayoutSubviews{ 

  if ([self respondsToSelector:@selector(topLayoutGuide)]) // iOS 7 or above 
  { 
    CGFloat top = self.topLayoutGuide.length; 
    if(self.webView.frame.origin.y == 0){ 
      // We only want to do this once, or if the view has somehow been "restored" by other code. 
      self.webView.frame = CGRectMake(self.webView.frame.origin.x, self.webView.frame.origin.y + top, self.webView.frame.size.width, self.webView.frame.size.height - top); 
    } 
  } 
}


在MainViewController.m中添加代码

在MainViewController.m中添加代码

参考文章地址:http://community.phonegap.com/nitobi/topics/inconsistent_ios_7_status_bar_build_problem