参考:
- Quartz2D 编程指南(一)概览、图形上下文、路径、颜色与颜色空间
- Quartz2D 编程指南(二)变换、图案、阴影
- Quartz2D 编程指南(三)渐变、透明层 、数据管理
- Quartz2D 编程指南(四)位图与图像遮罩、CoreGraphics 绘制 Layer
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1.创建 UIWindow 实例。
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// 2.指定 UIWindow 对象的根视图控制器。
ViewController *viewController = [[ViewController alloc] init];
self.window.rootViewController = viewController;
// 3.设置背景色。
self.window.backgroundColor = [UIColor whiteColor];
// 4.设置 UIWindow 实例为主窗口并使其可见。
[self.window makeKeyAndVisible];
return YES;
}
setRootViewController
将视图控制器的视图层次结构加入应用窗口。当程序将某个视图控制器设置为 UIWindow 对象的 rootViewControl
时,UIWindow 对象会将该视图控制器的 view 作为子视图加入窗口,同时负责维护 viewController 和 view 对应的生命周期。此外,还会自动调整 view 的大小,将其设置为与窗口的大小相同。rootViewControl
的 view 需要在应用启动完毕之后就显示,所以 UIWindow 对象会在设置完 rootViewControl
后立刻加载其 view。UINavigationController 类实现了一个用于管理分层内容导航的专用视图控制器。 该导航界面可以有效地呈现您的数据,并使用户更容易浏览该内容。 您通常按照原样使用此类,但您也可以使用子类来自定义类的行为。
导航界面呈现的屏幕通常会模拟您的数据层次结构。 在层次结构的每个级别中,您提供一个适当的屏幕(由自定义视图控制器管理)来显示该级别的内容。 图 1 显示了 iOS 模拟器中 “设置” 应用程序提供的导航界面的示例。 第一个屏幕向用户显示包含首选项的应用程序列表。 选择应用程序会显示该应用程序的各个设置和设置组。 选择组会产生更多设置等等。 除了根视图之外,导航控制器提供一个后退按钮,以允许用户返回到上一层次结构。
KeyChain 推荐使用这个第三方开源库: [soffes/SAMKeychain] (https://github.com/soffes/SAMKeychain) ⭐️⭐️⭐️ 5000+
新建 KeyChain 类,用于实现向 keyChain 存储、读取和修改用户名密码。代码如下:
#import <Foundation/Foundation.h>
#import <Security/Security.h>
@interface KeyChain : NSObject
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service;
// save username and password to keychain
+ (void)save:(NSString *)service data:(id)data;
// load username and password from keychain
+ (id)load:(NSString *)service;
// delete username and password from keychain
+ (void)delete:(NSString *)serviece;
@end
#import "KeyChain.h"
@implementation KeyChain
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword,(id)kSecClass,
service, (id)kSecAttrService,
service, (id)kSecAttrAccount,
(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
nil];
}
#pragma mark 写入
+ (void)save:(NSString *)service data:(id)data {
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Delete old item before add new item
SecItemDelete((CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
}
#pragma mark 读取
+ (id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Configure the search setting
//Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
} @catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
} @finally {
}
}
if (keyData)
CFRelease(keyData);
return ret;
}
#pragma mark 删除
+ (void)delete:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery);
}
@end
#import <Foundation/Foundation.h>
/**
管理用户密码
*/
@interface PasswordManger : NSObject
+ (void)savePassword:(NSString *)password;
+ (NSString *)loadPassword;
+ (void)deletePassword;
@end
#import "PasswordManger.h"
#import "AppKeyChain.h"
static NSString *const PASSWORD = @"com.CompanyName.ProjectName.password";
@implementation PasswordManger
+ (void)savePassword:(NSString *)password {
[AppKeyChain saveData:password forKey:PASSWORD];
}
+ (NSString *)loadPassword {
NSString *password = (NSString *)[AppKeyChain loadForKey:PASSWORD];
return password;
}
+ (void)deletePassword {
[AppKeyChain deleteKey:PASSWORD];
}
@end
🎉 🎉 🎉 Surprise ! I’m back! 本期给大家带来的第三方框架是一个非常常用的导航框架 —— 分页菜单 PageMenu。该框架同时支持 Swift 和 Objective-C,使用起来也是极其方便,下面一起来跟我看看吧!
💡 这是一个 Xcode 8 插件,以下是我对官方 README.md 文档的翻译。
这是一个 Xcode 代码编辑器扩展,用于对齐属性声明。