作者: xiao, yanzi

Appium 搭建 IOS

1、 安装Xcode

%title插图%num

xcode是苹果的一个开发工具,我们主要使用xocde自带的ios模拟器,在模拟器上进行iOS app的自动化测试。
打开MAC机上的APP Strore,搜索Xcode,进行安装或者更新,注意观察下xcode的更新日志,上面有写支持的iOS SDK版本。

2、 安装JDK
默认安装JDK
编辑环境变量文件
vi ~/.bash_profile
编辑后生效
source ~/.bash_profile
.bash_profile文件内容
JAVA_HOME=”/Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/”
PATH=”PATH:PATH:JAVA_HOME/bin”
export JAVA_HOME
export PATH

3、 安装homebrew
brew是mac上一个软件管理工具,通过它可以在终端输入命令安装各种软件包
打开https://brew.sh/

%title插图%num

将地址拷贝到终端进行安装
(brew list列出已安装的软件)

4、 安装libimobiledevice
主要是第三方访问iOS设备的接口封装
brew install –HEAD libimobiledevice

5、 安装ideviceinstaller
用于给iOS设备安装卸载应用或者备份应用
brew install –HEAD ideviceinstaller

6、 安装ios-webkit-debug-proxy
在做iOS的h5页面的时候,肯定会需要去做webview调试来进行代码调试,而iOS webview调试需要区分真机和模拟器,因为具体的实现原理不一样的,模拟器使用 remote debugger,可以直接通过safari远程调试,而真机使用 ios-webkit-debugger-proxy去远程调试的。
注意,测试包必须要使用develop证书打包,才可以进行真机调试。
通过如下命令进行安装
brew install ios-webkit-debug-proxy

安装之后运行如下命令查看是否能够调起ios-webkit-debug-proxy
ios_webkit_debug_proxy -c XXXXXXXXXX:27753 -d XXXXXXXXXX表示设备UDID

运行上述命令发现报错如下:
Could not connect to lockdownd. Exiting.: Permission denied
通过查找资料,发现网上大部分的解决方案是执行如下命令:
sudo chmod -R 777 /var/db/lockdown/

appium如何启动ios-webkit-debug-proxy
发现ios-webkit-debug-proxy可以通过设置DesiredCapabilities来调起,代码如下:
cap.setCapability(“startIWDP”,true);

7、 安装maven
默认安装maven
配置环境变量:
MAVEN_HOME=”/Users/young/Documents/dev/apache-maven”
然后在PATH变量*后追加:$MAVEN_HOME/bin
*后在输入 export MAVEN_HOME

8、 安装android sdk
默认安装android-sdk
配置环境变量:
ANDROID_HOME=”/Users/young/Documents/dev/android-sdk-macosx/”
在PATH变量后续追加:
:ANDROIDHOME/tools:ANDROIDHOME/tools:ANDROID_HOME/platform-tools
*后要export ANDROID_HOME
完整的.bash_profile内容(包含了JAVA、Android和Maven的环境变量配置):
ANDROID_HOME=”/Users/young/Documents/Dev/android-sdk-macosx/”
JAVA_HOME=”/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/”
MAVEN_HOME=”/Users/young/Documents/Dev/apache-maven”
PATH=”PATH:PATH:JAVA_HOME/bin:ANDROIDHOME/tools:ANDROIDHOME/tools:ANDROID_HOME/platform-tools:$MAVEN_HOME/bin”
export JAVA_HOME
export ANDROID_HOME
export MAVEN_HOME
export PATH

9、 安装node
brew install node
检查版本node -v

10、 安装npm
是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。
brew install npm

11、 安装appium
npm install -g appium
或者
npm install cnpm -g –registry=https://registry.npm.taobao.org
npm view appium versions #查看appium所有的版本号

12、 安装ios-deploy
是一个使用命令行安装ios app到连接的设备的工具,原理是根据os x命令行工程调用系统底层函数,获取连接的设备、查询/安装/卸载app。
npm install -g ios-deploy

13、 安装appium-doctor
是一个用于验证appium安装环境的工具,可以诊断出Node/iOS/Android环境配置方面的常见问题。
npm install appium-doctor -g
之后执行 appium-doctor 查看环境

%title插图%num

14、 安装WebDriverAgent(WDA)
如果是模拟器测试,不需要安装WDA,如果用真机一定要安装WDA
前提:电脑必须安装了Carthage
可以通过brew install Carthage
或者通过github下载pkg文件直接安装(源码安装可能会报错)https://github.com/Carthage/Carthage/releases

WebDriverAgent安装步骤:
1.进入WebDriverAgent
方式一:
从github上下载代码
git clone https://github.com/facebook/WebDriverAgent
用此方法安装,在appium执行的时候,要加
“xcodeOrgId”: “”,
“xcodeSigningId”: “iPhone Developer” 这两个参数,
TeamID 在 https://developer.apple.com/account/ 里左侧的 Member ship 中找到

%title插图%num

例如:
{
“platformName”: “iOS”,
“platformVersion”: “10.3”,
“deviceName”: “test”,
“automationName”: “XCUITest”,
“app”: “/Users/automation/test/VWallet.ipa”,
“bundleId”: “com.xiaoV.conchBeta”,
“udid”: “ab2ac6b5e375211d5bd8e8e3eedeb15704759390”,
“xcodeOrgId”: “6XXXXXXXXX”,
“xcodeSigningId”: “iPhone Developer”
}

方式二:
cd /Applications/Appium.app/Contents/Resources/app/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent

%title插图%num

目录在appium安装目录下,具体路径参照自己的路径,可以通过find命令查找

2.在当前目录下执行脚本
sh ./Scripts/bootstrap.sh

3.编译WebDriverAgent.xcodeproj
双击打开当前目录下的WebDriverAgent.xcodeproj文件(工程文件在第1步的目录下)

注意:build时需要指定一个Development team,可以用个人Apple ID账号,生成个人证书和team(在Xcode->Preferences->Account中配置),也可以利用开发者账号。

%title插图%num

%title插图%num

%title插图%num

PROJECT->WebDriverAgent、TARGETS->WebDriverAgentLib和WebDriverAgentRunner的Signing使用个人的证书和Team(画黑色横线的部分)

%title插图%num
TARGETS->WebDriverAgentLib和WebDriverAgentRunner的Product Bundle Identifier使用一个新名字,因为Bundle ID不能重复

%title插图%num
点击Product->Build,将工程文件编译一下

4.测试一下,然后手机上信任证书
安装方式一:
还是在WebDriverAgent目录下,执行下面的命令来测试一下是否生效了
xcodebuild -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination ‘id=你的udid’ test
例如:
xcodebuild -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination ‘id=3340586f5abe813300530112f2238ac9b7e31e88’ test
如果没有报错证明安装成功,同时手机上会安装一个WebDriverAgent软件

%title插图%num

说明:如果编译WebDriverAgent时使用了自己的证书,需要再手机设置->通用里信任一下证书

安装方式二:
菜单栏选择目标设备

%title插图%num
Scheme选择WebDriverAgentRunner

%title插图%num
*后运行 Product -> Test
一切正常的话,手机上会出现一个无图标的WebDriverAgent应用,启动之后,马上又返回到桌面。这是很正常的不要奇怪。
此时控制台界面可以看到设备的IP。如果看不到的话,使用这种方法打开

%title插图%num

通过上面给出的IP和端口,加上/status合成一个url地址。例如http://10.0.0.1:8100/status,然后浏览器打开。如果出现一串JSON输出,说明WDA安装成功了。

测试WebDriverAgent时遇到的问题1:
Testing failed:
Signing for “WebDriverAgentRunner” requires a development team. Select a development team in the project editor.
Code signing is required for product type ‘UI Testing Bundle’ in SDK ‘iOS 10.3’
需要下图的这两个target都配置一下debug的证书和配置文件,然后编译

%title插图%num

测试WebDriverAgent时遇到的问题2:
Testing failed:
Test target WebDriverAgentRunner encountered an error (Early unexpected exit, operation never finished bootstrapping – no restart will be attempted)

解决方法:
进入手机的设置->通用->描述文件与设备管理,信任编译WebDriverAgent时选择的证书
然后再执行该命令就可以了

%title插图%num
15、 安装IOS模拟器
打开已经安装好的xcode(位于/Applactions/Xcode),如图

%title插图%num

然后选择Create a new Xcode Project,接下来按照图操作

%title插图%num

%title插图%num

%title插图%num

%title插图%num

%title插图%num

然后会是这个样子

%title插图%num

如果你的xode中存在iOS SDK,那么点击

%title插图%num

这个地方会弹出可用的模拟器,比如我的

%title插图%num

此时点击这个iPhone 6就会由这个模拟器来执行这个project。点击这个三角符号,进行运行操作

%title插图%num

注意观察

%title插图%num

IOS 模拟器启动了

%title插图%num

如果没有ios SDK可以在这里下载更多的ios simulator

%title插图%num

点击之后如图

%title插图%num

注意:根据自己需求来下载相应的iOS SDK。
另外你还可以将ios模拟器固定在DOCK,这样每次启动ios模拟器可以从dock上点击启动即可,如下图所示:

%title插图%num

iOS获取手机上app信息

在代码中插入一段查询网址即可获取app信息以及版本信息。
查询命令:http://itunes.apple.com/lookup?id=xxxxxxxxx(9位appid数字)

获取设备上所有app (为LSApplicationProxy 对象)

  1. Class LSApplicationWorkspace_class = objc_getClass(“LSApplicationWorkspace”);
  2. NSObject *workspace = [LSApplicationWorkspace_class performSelector:@selector(defaultWorkspace)];
  3. NSArray *apps = [workspace performSelector:@selector(allInstalledApplications)];
  4. NSLog(@”apps: %@”, apps);

 

获取版本信息

  [apps[0] performSelector:NSSelectorFromString(@"shortVersionString")]

 

其他

1 /* Generated by RuntimeBrowser
2    Image: /System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices
3  */
4
5 @interface LSApplicationProxy : LSBundleProxy <NSSecureCoding> {
6     NSNumber *_ODRDiskUsage;
7     NSArray *_UIBackgroundModes;
8     NSArray *_*Plugins;
9     NSArray *_appTags;
10     NSString *_applicationDSID;
11     NSString *_applicationVariant;
12     NSArray *_audioComponents;
13     NSString *_companionAppIdentifier;
14     NSArray *_deviceFamily;
15     NSUUID *_deviceIdentifierForVendor;
16     NSArray *_directionsModes;
17     NSNumber *_downloaderDSID;
18     NSNumber *_dynamicDiskUsage;
19     NSArray *_externalAccessoryProtocols;
20     NSNumber *_familyID;
21     unsigned int _flags;
22     NSDictionary *_groupContainers;
23     NSArray *_groupIdentifiers;
24     unsigned int _installType;
25     NSNumber *_itemID;
26     NSString *_itemName;
27     NSString *_minimumSystemVersion;
28     long _modTime;
29     unsigned int _originalInstallType;
30     NSArray *_plugInKitPlugins;
31     NSArray *_pluginUUIDs;
32     NSArray *_privateDocumentIconNames;
33     LSApplicationProxy *_privateDocumentTypeOwner;
34     NSNumber *_purchaserDSID;
35     long _regTime;
36     NSDate *_registeredDate;
37     NSArray *_requiredDeviceCapabilities;
38     NSString *_sdkVersion;
39     NSString *_shortVersionString;
40     NSString *_sourceAppIdentifier;
41     NSNumber *_staticDiskUsage;
42     NSString *_storeCohortMetadata;
43     NSNumber *_storeFront;
44     NSString *_teamID;
45     NSString *_vendorName;
46     NSNumber *_versionID;
47 }
48
49 @property (nonatomic, readonly) NSNumber *ODRDiskUsage;
50 @property (nonatomic, readonly) NSArray *UIBackgroundModes;
51 @property (nonatomic, readonly) NSArray **Plugins;
52 @property (nonatomic, readonly) NSArray *appTags;
53 @property (nonatomic, readonly) NSString *applicationDSID;
54 @property (nonatomic, readonly) NSString *applicationIdentifier;
55 @property (nonatomic, readonly) NSString *applicationType;
56 @property (nonatomic, readonly) NSString *applicationVariant;
57 @property (nonatomic, readonly) NSArray *audioComponents;
58 @property (nonatomic, readonly) NSNumber *betaExternalVersionIdentifier;
59 @property (nonatomic, readonly) NSString *companionApplicationIdentifier;
60 @property (nonatomic, readonly) NSArray *deviceFamily;
61 @property (nonatomic, readonly) NSUUID *deviceIdentifierForVendor;
62 @property (nonatomic, readonly) NSArray *directionsModes;
63 @property (nonatomic, readonly) NSNumber *downloaderDSID;
64 @property (nonatomic, readonly) NSNumber *dynamicDiskUsage;
65 @property (nonatomic, readonly) NSArray *externalAccessoryProtocols;
66 @property (nonatomic, readonly) NSNumber *externalVersionIdentifier;
67 @property (nonatomic, readonly) NSNumber *familyID;
68 @property (nonatomic, readonly) BOOL fileSharingEnabled;
69 @property (nonatomic, readonly) NSDictionary *groupContainers;
70 @property (nonatomic, readonly) NSArray *groupIdentifiers;
71 @property (nonatomic, readonly) BOOL hasMIDBasedSINF;
72 @property (nonatomic, readonly) BOOL hasSettingsBundle;
73 @property (nonatomic, readonly) BOOL iconIsPrerendered;
74 @property (nonatomic, readonly) NSProgress *installProgress;
75 @property (nonatomic, readonly) unsigned int installType;
76 @property (nonatomic, readonly) BOOL isAdHocCodeSigned;
77 @property (nonatomic, readonly) BOOL isAppUpdate;
78 @property (nonatomic, readonly) BOOL isBetaApp;
79 @property (nonatomic, readonly) BOOL isInstalled;
80 @property (nonatomic, readonly) BOOL isNewsstandApp;
81 @property (nonatomic, readonly) BOOL isPlaceholder;
82 @property (nonatomic, readonly) BOOL isPurchasedReDownload;
83 @property (nonatomic, readonly) BOOL isRestricted;
84 @property (nonatomic, readonly) BOOL isWatchKitApp;
85 @property (nonatomic, readonly) NSNumber *itemID;
86 @property (nonatomic, readonly) NSString *itemName;
87 @property (nonatomic, readonly) NSString *minimumSystemVersion;
88 @property (nonatomic, readonly) BOOL missingRequiredSINF;
89 @property (nonatomic, readonly) unsigned int originalInstallType;
90 @property (nonatomic, readonly) NSArray *plugInKitPlugins;
91 @property (nonatomic, readonly) NSString *preferredArchitecture;
92 @property (nonatomic, readonly) BOOL profileValidated;
93 @property (nonatomic, readonly) NSNumber *purchaserDSID;
94 @property (nonatomic, readonly) NSDate *registeredDate;
95 @property (getter=isRemoveableSystemApp, nonatomic, readonly) BOOL removeableSystemApp;
96 @property (getter=isRemovedSystemApp, nonatomic, readonly) BOOL removedSystemApp;
97 @property (nonatomic, readonly) NSArray *requiredDeviceCapabilities;
98 @property (nonatomic, readonly) NSString *roleIdentifier;
99 @property (nonatomic, readonly) NSString *sdkVersion;
100 @property (nonatomic, readonly) NSString *shortVersionString;
101 @property (nonatomic, readonly) NSString *sourceAppIdentifier;
102 @property (nonatomic, readonly) NSNumber *staticDiskUsage;
103 @property (nonatomic, readonly) NSString *storeCohortMetadata;
104 @property (nonatomic, readonly) NSNumber *storeFront;
105 @property (nonatomic, readonly) BOOL supportsAudiobooks;
106 @property (nonatomic, readonly) BOOL supportsExternallyPlayableContent;
107 @property (nonatomic, readonly) BOOL supportsOpenInPlace;
108 @property (nonatomic, readonly) BOOL supportsPurgeableLocalStorage;
109 @property (nonatomic, readonly) NSString *teamID;
110 @property (nonatomic, readonly) NSString *vendorName;
111 @property (getter=isWhitelisted, nonatomic, readonly) BOOL whitelisted;
112
113 // Image: /System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices
114
115 + (id)applicationProxyForBundleURL:(id)arg1;
116 + (id)applicationProxyForCompanionIdentifier:(id)arg1;
117 + (id)applicationProxyForIdentifier:(id)arg1;
118 + (id)applicationProxyForIdentifier:(id)arg1 placeholder:(BOOL)arg2;
119 + (id)applicationProxyForIdentifier:(id)arg1 roleIdentifier:(id)arg2;
120 + (id)applicationProxyForItemID:(id)arg1;
121 + (id)applicationProxyWithBundleUnitID:(unsigned long)arg1;
122 + (BOOL)supportsSecureCoding;
123
124 – (id)ODRDiskUsage;
125 – (id)UIBackgroundModes;
126 – (id)*Plugins;
127 – (id)_initWithBundleUnit:(unsigned long)arg1 applicationIdentifier:(id)arg2;
128 – (id)appStoreReceiptURL;
129 – (id)appTags;
130 – (id)applicationDSID;
131 – (id)applicationIdentifier;
132 – (id)applicationType;
133 – (id)applicationVariant;
134 – (id)audioComponents;
135 – (id)betaExternalVersionIdentifier;
136 – (long)bundleModTime;
137 – (id)companionApplicationIdentifier;
138 – (void)dealloc;
139 – (id)description;
140 – (id)deviceFamily;
141 – (id)deviceIdentifierForVendor;
142 – (id)directionsModes;
143 – (id)downloaderDSID;
144 – (id)dynamicDiskUsage;
145 – (void)encodeWithCoder:(id)arg1;
146 – (id)externalAccessoryProtocols;
147 – (id)externalVersionIdentifier;
148 – (id)familyID;
149 – (BOOL)fileSharingEnabled;
150 – (id)groupContainers;
151 – (id)groupIdentifiers;
152 – (BOOL)hasMIDBasedSINF;
153 – (BOOL)hasSettingsBundle;
154 – (unsigned int)hash;
155 – (id)iconDataForVariant:(int)arg1;
156 – (BOOL)iconIsPrerendered;
157 – (id)iconStyleDomain;
158 – (id)initWithCoder:(id)arg1;
159 – (id)installProgress;
160 – (id)installProgressSync;
161 – (unsigned int)installType;
162 – (BOOL)isAdHocCodeSigned;
163 – (BOOL)isAppUpdate;
164 – (BOOL)isBetaApp;
165 – (BOOL)isEqual:(id)arg1;
166 – (BOOL)isInstalled;
167 – (BOOL)isNewsstandApp;
168 – (BOOL)isPlaceholder;
169 – (BOOL)isPurchasedReDownload;
170 – (BOOL)isRemoveableSystemApp;
171 – (BOOL)isRemovedSystemApp;
172 – (BOOL)isRestricted;
173 – (BOOL)isSystemOrInternalApp;
174 – (BOOL)isWatchKitApp;
175 – (BOOL)isWhitelisted;
176 – (id)itemID;
177 – (id)itemName;
178 – (id)localizedName;
179 – (id)localizedNameForContext:(id)arg1;
180 – (id)localizedShortName;
181 – (id)machOUUIDs;
182 – (id)minimumSystemVersion;
183 – (BOOL)missingRequiredSINF;
184 – (unsigned int)originalInstallType;
185 – (id)plugInKitPlugins;
186 – (void)populateNotificationData;
187 – (id)preferredArchitecture;
188 – (BOOL)privateDocumentIconAllowOverride;
189 – (id)privateDocumentIconNames;
190 – (id)privateDocumentTypeOwner;
191 – (id)privateIconsDictionary;
192 – (BOOL)profileValidated;
193 – (id)purchaserDSID;
194 – (id)registeredDate;
195 – (id)requiredDeviceCapabilities;
196 – (id)resourcesDirectoryURL;
197 – (id)roleIdentifier;
198 – (id)sdkVersion;
199 – (void)setPrivateDocumentIconAllowOverride:(BOOL)arg1;
200 – (void)setPrivateDocumentIconNames:(id)arg1;
201 – (void)setPrivateDocumentTypeOwner:(id)arg1;
202 – (id)shortVersionString;
203 – (id)sourceAppIdentifier;
204 – (id)staticDiskUsage;
205 – (id)storeCohortMetadata;
206 – (id)storeFront;
207 – (BOOL)supportsAudiobooks;
208 – (BOOL)supportsExternallyPlayableContent;
209 – (BOOL)supportsOpenInPlace;
210 – (BOOL)supportsPurgeableLocalStorage;
211 – (id)teamID;
212 – (id)uniqueIdentifier;
213 – (id)userActivityStringForAdvertisementData:(id)arg1;
214 – (id)vendorName;
215 – (id)versionIdentifier;
216
217 // Image: /System/Library/Frameworks/UIKit.framework/UIKit
218
219 – (struct CGSize { float x1; float x2; })_defaultStyleSize:(id)arg1;
220 – (struct { int x1; struct CGSize { float x_2_1_1; float x_2_1_2; } x2; }*)_iconVariantDefinitions:(id)arg1;
221
222 // Image: /System/Library/PrivateFrameworks/UserNotification.framework/UserNotification
223
224 + (id)un_bundleForBundleIdentifier:(id)arg1;
225
226 – (id)_un_bundle;
227 – (BOOL)_un_isReallyInstalled;
228 – (BOOL)_un_isResourceValidForPath:(id)arg1 withContainerPath:(id)arg2;
229 – (BOOL)_un_usesLocalNotification;
230 – (id)un_infoDictionary;
231 – (BOOL)un_isSystemApplication;
232 – (id)un_pathForSoundName:(id)arg1;
233 – (BOOL)un_requiresLocalNotification;
234 – (BOOL)un_shouldUseDefaultDataProvider;
235
236 @end

Appium – iOS 定位方式 iOSNsPredicate 详解

1     前言

由于使用id、className、AccessibilityId定位方式较为简单,多数情况下,在同一个页面,都不是唯一存在的,不能识别一个元素。而 xpath定位方式在 xcui 底层原生不支持,由 appium 额外支持的,定位速度很慢,而且有时候定位不到元素的情况存在。综上所述,在 iOS 的 UI 自动化中,使用原生支持的iOSNsPredicateString定位方式是*好,支持也是*好的。

2     定位方式

iOS 版本全支持,底层测试框架无论是XCUITest 或 UIAutomation,可支持元素的单个属性和多个属性定位,推荐使用。一个元素有这些属性:type、value、name、label、enabled、visible,有些元素的属性只有以上的部分属性,如下图所示,可根据这些属性进行元素定位。

%title插图%num%title插图%num

%title插图%num

3     元素属性的介绍

type:元素类型,与className作用一致,如:XCUIElementTypeStaticText
value: 一般不用
name:元素的文本内容,可用作 AccessibilityId定位方式,如:测试420班级群
label:*大多数情况下,与 name 作用一致
enabled:元素是否可点击,一般值为true或者false
visible:元素是否可见,一般值为true或者false

4     元素定位方式

元素的定位方式都是一个属性+运算符+值形式存在

1、比较运算符:>,<,==,>=,<=,!=
可用于数值和字符串的比较,
如:name>100 或name == ‘测试’

 

2、范围运算符:IN,BETWEEN
可用于数值和字符串的范围核对
如:name BETWEEN {3,10},name IN {‘Alan’,’May’}

 

3、字符串相关:CONTAINS、BEGINSWITH、ENDSWITH
包含某个字符串,如:label CONTAINS ‘测试’
以某个字符串开头,如:label BEGINSWITH ‘420’
以某个字符串结束,如:label ENDSWITH ‘班级群’
PS:在三个关键字后加上[c]不区分大小写,可用于字母的校验;[d]不区分发音符号,即没有重音符号($、#、%等);[cd]即不区分大小写,也不区分发音符号,如:name CONTAINS[c] ABcd和name CONTAINS abcd、name CONTAINS ABCD是等同的,注意后面两个没带[c]的不相等

 

4、通配符:LIKE
通配符也接受[cd],?代表一个字符,*代表多个字符
如:一个元素的label属性为

label LIKE ‘420测试班级群’

label LIKE ‘420测?班级群’

label LIKE ‘420??班级群’

label LIKE ’42?测试班?群’

label LIKE ‘*试班级群’

label LIKE ‘420测试班*’

label LIKE ’42*级群’

label LIKE ‘4*试*群’

以上这么多种文本都可以被识别为同一个元素。

 

5、正则表达式:MATCHES
如:以4开头,以群结束,

label MATCHES ‘^4.+群$’

 

5     以一种属性定位元素

可以用元素的属性:type、value、name、label、enabled、visible,进行定位:

type == XCUIElementTypeStaticText,

label CONTAINS ‘测试’

label LIKE ‘*试班级群’

enabled == true

visible == false

 

6     以两种或两种以上属性定位元素

就是以上单个属性定位用符号AND连接起来即可。如:

type == XCUIElementTypeStaticText AND labelCONTAINS ‘测试

type == XCUIElementTypeStaticText AND labelCONTAINS ‘测试’ AND enabled == true

 

7  使用方法

// 等于

MobileElement photo = driver.findElementByIosNsPredicate(“name= ‘head new‘”);

 

// 模糊匹配

MobileElement photo =driver.findElementByIosNsPredicate(“name LIKE ‘*new‘”);

 

// 正则表达式匹配

MobileElement photo = driver.findElementByIosNsPredicate(“nameMATCHES ‘^$‘”);

 

// 包含

List<IOSElement> items1 = driver.findElementsByIosNsPredicate(“nameCONTAINS ‘我的‘”);

 

// 以”我的”开始

List<IOSElement> items2 = driver.findElementsByIosNsPredicate(“nameBEGINSWITH ‘我的‘”);

 

// 以”我的”开始并且以”消息”结尾

List<IOSElement> items3 = driver.findElementsByIosNsPredicate(“nameBEGINSWITH ‘我的‘ && name ENDSWITH ‘消息‘”);

 

其中属性名参照inspector的属性字段,关键字LIKE,MATCHES,CONTAINS,BEGINSWITH,ENDSWITH必须是大写,匹配的字符需要用单引号

 

ios 里如何判断当前应用的定位服务是否可用

mark! 用到地图定位的时候,会判断定位是否可用来初始化定位服务。 但是以前的方法是判断所有应用的定位服务,无法指定到当前的应用是否开启服务。下面的就可以直接搞定这个问题。

1 if ([CLLocationManager locationServicesEnabled] &&
2                 ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized
3                 || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined)) {
4                 //定位功能可用,开始定位
5                 _locationManger = [[CLLocationManager alloc] init];
6                 locationManger.delegate = self;
7                 [locationManger startUpdatingLocation];
8             }
9             else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied){
10         NSlog(“定位功能不可用,提示用户或忽略”);
11             }

iOS开发- 相机(摄像头)获取到的图片自动旋转90度解决办法

今天要做一个读取摄像头图片功能,发现拍摄之后的图片会在拍摄水平的基础上自动发生90°旋转。通过下面的方法 确实修正了,未避免选照片也发生该案例,我觉得在选取图片的时候也要进行修正。

 //get original photo from iOS photos 
//如果该图片大于2M,会自动旋转90度;否则不旋转

网络上说:用相机拍摄出来的照片含有EXIF信息,UIImage的imageOrientation属性指的就是EXIF中的orientation信息。如果我们忽略orientation信息,而直接对照片进行像素处理或者drawInRect等操作,得到的结果是翻转或者旋转90之后的样子。这是因为我们执行像素处理或者drawInRect等操作之后,imageOrientaion信息被删除了,imageOrientaion被重设为0,造成照片内容和imageOrientaion不匹配。所以,在对照片进行处理之前,先将照片旋转到正确的方向,并且返回的imageOrientaion为0。

修正代码如下:

1 – (UIImage *)fixOrientation:(UIImage *)aImage {
2
3   // No-op if the orientation is already correct
4   if (aImage.imageOrientation == UIImageOrientationUp)
5     return aImage;
6
7   // We need to calculate the proper transformation to make the image upright.
8   // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
9   CGAffineTransform transform = CGAffineTransformIdentity;
10
11   switch (aImage.imageOrientation) {
12     case UIImageOrientationDown:
13     case UIImageOrientationDownMirrored:
14       transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
15       transform = CGAffineTransformRotate(transform, M_PI);
16       break;
17
18     case UIImageOrientationLeft:
19     case UIImageOrientationLeftMirrored:
20       transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
21       transform = CGAffineTransformRotate(transform, M_PI_2);
22       break;
23
24     case UIImageOrientationRight:
25     case UIImageOrientationRightMirrored:
26       transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
27       transform = CGAffineTransformRotate(transform, -M_PI_2);
28       break;
29     default:
30       break;
31   }
32
33   switch (aImage.imageOrientation) {
34     case UIImageOrientationUpMirrored:
35     case UIImageOrientationDownMirrored:
36       transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
37       transform = CGAffineTransformScale(transform, -1, 1);
38       break;
39
40     case UIImageOrientationLeftMirrored:
41     case UIImageOrientationRightMirrored:
42       transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
43       transform = CGAffineTransformScale(transform, -1, 1);
44       break;
45     default:
46       break;
47   }
48
49   // Now we draw the underlying CGImage into a new context, applying the transform
50   // calculated above.
51   CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
52                        CGImageGetBitsPerComponent(aImage.CGImage), 0,
53                        CGImageGetColorSpace(aImage.CGImage),
54                        CGImageGetBitmapInfo(aImage.CGImage));
55   CGContextConcatCTM(ctx, transform);
56   switch (aImage.imageOrientation) {
57     case UIImageOrientationLeft:
58     case UIImageOrientationLeftMirrored:
59     case UIImageOrientationRight:
60     case UIImageOrientationRightMirrored:
61       // Grr…
62       CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
63       break;
64
65     default:
66       CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
67       break;
68   }
69
70   // And now we just create a new UIImage from the drawing context
71   CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
72   UIImage *img = [UIImage imageWithCGImage:cgimg];
73   CGContextRelease(ctx);
74   CGImageRelease(cgimg);
75   return img;
76 }

ios图片添加文字或者水印

一般在客户端做图片处理的数量不宜太多,因为受设备性能的限制,如果批量的处理图片,将会带来交互体验性上的一些问题。首先让我们来看看在图片上添加文字的方法、

  1. -(UIImage *)addText:(UIImage *)img text:(NSString *)text1
  2. {
  3. //上下文的大小
  4. int w = img.size.width;
  5. int h = img.size.height;
  6. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();//创建颜色
  7. //创建上下文
  8. CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
  9. CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);//将img绘至context上下文中
  10. CGContextSetRGBFillColor(context, 0.0, 1.0, 1.0, 1);//设置颜色
  11. char* text = (char *)[text1 cStringUsingEncoding:NSASCIIStringEncoding];
  12. CGContextSelectFont(context, “Georgia”, 30, kCGEncodingMacRoman);//设置字体的大小
  13. CGContextSetTextDrawingMode(context, kCGTextFill);//设置字体绘制方式
  14. CGContextSetRGBFillColor(context, 255, 0, 0, 1);//设置字体绘制的颜色
  15. CGContextShowTextAtPoint(context, w/2-strlen(text)*5, h/2, text, strlen(text));//设置字体绘制的位置
  16. //Create image ref from the context
  17. CGImageRef imageMasked = CGBitmapContextCreateImage(context);//创建CGImage
  18. CGContextRelease(context);
  19. CGColorSpaceRelease(colorSpace);
  20. return [UIImage imageWithCGImage:imageMasked];//获得添加水印后的图片
  21. }

 

在上面的方法中,我们可以看到,我们可以通过将图片和文字绘制到同一个上下文中,并且重新生成图片,所获得图片就是包括图片和文字。

另外在一些项目中我们可能还回用到图片叠加,比如打水印等功能,这种功能相对上面给图片添加文字更容易,只是在上下文中,绘制两张图片,然后重新生成,以达到图片的叠加、代码如下:

  1. -(UIImage *)addImageLogo:(UIImage *)img text:(UIImage *)logo
  2. {
  3. //get image width and height
  4. int w = img.size.width;
  5. int h = img.size.height;
  6. int logoWidth = logo.size.width;
  7. int logoHeight = logo.size.height;
  8. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  9. //create a graphic context with CGBitmapContextCreate
  10. CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
  11. CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);
  12. CGContextDrawImage(context, CGRectMake(w-logoWidth, 0, logoWidth, logoHeight), [logo CGImage]);
  13. CGImageRef imageMasked = CGBitmapContextCreateImage(context);
  14. CGContextRelease(context);
  15. CGColorSpaceRelease(colorSpace);
  16. return [UIImage imageWithCGImage:imageMasked];
  17. // CGContextDrawImage(contextRef, CGRectMake(100, 50, 200, 80), [smallImg CGImage]);
  18. }

对于图片叠加文字,和图片叠加图片,基本的原理是一样的,创建绘图上下文,然后在上下文中绘制图片或者文字,然后重新生成图片,以达到我们需要的效果。

iOS 画线 绘制直线、矩形、三角形

在UIView中绘制直线、矩形、三角形

直线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- ( void )drawRect:(CGRect)rect {
     //获得处理的上下文
     CGContextRef context = UIGraphicsGetCurrentContext();
     //指定直线样式
     CGContextSetLineCap(context, kCGLineCapSquare);
     //直线宽度
     CGContextSetLineWidth(context, 2.0);
     //设置颜色
     CGContextSetRGBStrokeColor(context, 0.314, 0.486, 0.859, 1.0);
    //开始绘制
     CGContextBeginPath(context);
     //画笔移动到点(31,170)
     CGContextMoveToPoint(context, 31, 70);
     //下一点
     CGContextAddLineToPoint(context, 129, 148);
     //下一点
     CGContextAddLineToPoint(context, 159, 148);
     //绘制完成
     CGContextStrokePath(context);
}

矩形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- ( void )drawRect:(CGRect)rect{
//创建路径并获取句柄
CGMutablePathRef path = CGPathCreateMutable();
//指定矩形
CGRect rectangle = CGRectMake(10.0f, 10.0f,200.0f, 300.0f);
//将矩形添加到路径中
CGPathAddRect(path, NULL , rectangle);
//获取上下文
CGContextRef currentContext = UIGraphicsGetCurrentContext();
//将路径添加到上下文
CGContextAddPath(currentContext, path);
//设置矩形填充色
[[UIColor colorWithRed:0.20f green:0.60f blue:0.80falpha:1.0f] setFill];
//矩形边框颜色
[[UIColor brownColor] setStroke];
//边框宽度
CGContextSetLineWidth(currentContext,5.0f);
//绘制
CGContextDrawPath(currentContext, kCGPathFillStroke); 
CGPathRelease(path);
}

三角形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- ( void )drawRect:(CGRect)rect
{
     //设置背景颜色
     [[UIColor clearColor]set];
     UIRectFill([ self  bounds]);
     //拿到当前视图准备好的画板
     CGContextRef context = UIGraphicsGetCurrentContext();
     //利用path进行绘制三角形
     CGContextBeginPath(context); //标记
     CGContextMoveToPoint(context, 0, 0); //设置起点
     CGContextAddLineToPoint(context, 165, 0);
     CGContextAddLineToPoint(context, 165, 105);
     CGContextClosePath(context); //路径结束标志,不写默认封闭
     [[UIColor whiteColor] setFill];  //设置填充色
     [[UIColor whiteColor] setStroke];  //设置边框颜色
     CGContextDrawPath(context, kCGPathFillStroke); //绘制路径path
}

iOS 过滤文本特殊字符

在开发过程中,真好遇到一个问题,项目输入的文本内容禁止输入特殊字符,具体哪些需要屏蔽的可以自行删减,记录一下。

 

NSCharacterSet *doNotWant = [NSCharacterSet characterSetWithCharactersInString:@”;¥?![]{}#%^*£€•$><~|/?!@[]{}(#-%*+=)\\|~(<>$%^&*)+”];
NSArray * tempStringArray = [str componentsSeparatedByCharactersInSet: doNotWant];//拆分字符串
str=[tempStringArray componentsJoinedByString:@””];//数组拼接字符串

//这里是清理前后两端空格
NSString *tempString = [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
tempString = [tempString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet ]];
// [str stringByReplacingOccurrencesOfString:@” ” withString:@””];//替换全部空格

 

iOS – UIButton(UIEdgeInsets)/设置button上的文字和图片 在定义位置

UIEdgeInsets

[objc]
typedef struct UIEdgeInsets {
    CGFloat top, left, bottom, right;  // specify amount to inset (positive) for each of the edges. values can be negative to ‘outset’
} UIEdgeInsets;
在UIButton中有三个对EdgeInsets的设置:ContentEdgeInsets、titleEdgeInsets、imageEdgeInsets
[objc]
@property(nonatomic)          UIEdgeInsets contentEdgeInsets UI_APPEARANCE_SELECTOR; // default is UIEdgeInsetsZero
@property(nonatomic)          UIEdgeInsets titleEdgeInsets;                // default is UIEdgeInsetsZero
@property(nonatomic)          BOOL         reversesTitleShadowWhenHighlighted; // default is NO. if YES, shadow reverses to shift between engrave and emboss appearance
@property(nonatomic)          UIEdgeInsets imageEdgeInsets;                // default is UIEdgeInsetsZero
UIEdgeInsetsMake
里面的四个参数表示距离上边界、左边界、下边界、右边界的距离,默认都为零,title/image在button的正中央
[objc]
UIKIT_STATIC_INLINE UIEdgeInsets UIEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat bottom, CGFloat right) {
    UIEdgeInsets insets = {top, left, bottom, right};
    return insets;
}
[objc]
 self.view.backgroundColor = [UIColor blackColor];
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];//button的类型
    button.frame = CGRectMake(100, 100,90, 90);//button的frame
   button.backgroundColor = [UIColor cyanColor];//button的背景颜色
//    [button setBackgroundImage:[UIImage imageNamed:@”man_64.png”] forState:UIControlStateNormal];
//    在UIButton中有三个对EdgeInsets的设置:ContentEdgeInsets、titleEdgeInsets、imageEdgeInsets
    [button setImage:[UIImage imageNamed:@”[email protected]”] forState:UIControlStateNormal];//给button添加image
    button.imageEdgeInsets = UIEdgeInsetsMake(5,13,21,button.titleLabel.bounds.size.width);//设置image在button上的位置(上top,左left,下bottom,右right)这里可以写负值,对上写-5,那么image就象上移动5个像素
    [button setTitle:@”首页” forState:UIControlStateNormal];//设置button的title
    button.titleLabel.font = [UIFont systemFontOfSize:16];//title字体大小
    button.titleLabel.textAlignment = NSTextAlignmentCenter;//设置title的字体居中
    [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];//设置title在一般情况下为白色字体
    [button setTitleColor:[UIColor grayColor] forState:UIControlStateHighlighted];//设置title在button被选中情况下为灰色字体
     button.titleEdgeInsets = UIEdgeInsetsMake(71, -button.titleLabel.bounds.size.width-50, 0, 0);//设置title在button上的位置(上top,左left,下bottom,右right)
//    [button setContentEdgeInsets:UIEdgeInsetsMake(70, 0, 0, 0)];//
//   button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;//设置button的内容横向居中。。设置content是title和image一起变化
    [button addTarget:self action:@selector(tap) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
[objc]
//button相应的事件
-(void)tap {
    NSLog(@”tap a button”);
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@”hello” message:@”willingseal” delegate:self cancelButtonTitle:@”ok” otherButtonTitles: nil nil];
    [alertView show];
}

iOS按钮设置图片在上文字在下

UIButton同时设置Title和Image后,默认是图片在左文字在右,如下图1,很多情况下我们希望图片在上图片在下,如下图2,只需要简单的几行代码,即可实现。

%title插图%num
图1

%title插图%num

(1)因为需要处理多个按钮,所以将实现代码封装为一个方法,把每个UIbutton实例作为参数传入即可,代码如下:

-(void)initButton:(UIButton*)btn{
btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;//使图片和文字水平居中显示
[btn setTitleEdgeInsets:UIEdgeInsetsMake(btn.imageView.frame.size.height ,-btn.imageView.frame.size.width, 0.0,0.0)];//文字距离上边框的距离增加imageView的高度,距离左边框减少imageView的宽度,距离下边框和右边框距离不变
[btn setImageEdgeInsets:UIEdgeInsetsMake(0.0, 0.0,0.0, -btn.titleLabel.bounds.size.width)];//图片距离右边框距离减少图片的宽度,其它不边
}

(2)然后把按钮传入
[self initButton:self.btn1];
[self initButton:self.btn2];
[self initButton:self.btn3];
[self initButton:self.btn4];
[self initButton:self.btn5];
[self initButton:self.btn6];
[self initButton:self.btn7];

(3)运行查看效果。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速