所以我一直在研究如何将登录视图控制器作为初始视图控制器而不是splitview。
我见过的一些答案会建议加载模态视图?我不确定如何设置。
例如
How to add a login view before a UISplitViewController iPad
和
How to implement SplitViewController on second level.?
那么我在loginviewcontroller类中添加它们吗?或者在哪里?
欢迎任何建议。
谢谢!
答案 0 :(得分:3)
我通过创建两个故事板来完成此操作:一个使用(全屏)登录,另一个使用拆分视图。
要在它们之间切换,我添加了自定义协议:
#import <Foundation/Foundation.h>
@protocol RootViewControllerDelegate <NSObject>
-(void)switchToStoryboard: (UIStoryboard *) storyboad animationDirectionOrNil: (NSString *)direction;
@end
然后,AppDelegate实现了这个协议:
-(void)switchToStoryboard:(id)storyboad animationDirectionOrNil:(NSString *)direction {
UIViewController *newRoot=[storyboad instantiateInitialViewController];
if ([newRoot respondsToSelector:@selector(setRootViewControllerDelegate:)]) {
[newRoot setRootViewControllerDelegate:self];
}
self.window.rootViewController=newRoot;
if(direction){
CATransition* transition=[CATransition animation];
transition.type=kCATransitionPush;
transition.subtype=direction;
[self.window.layer addAnimation:transition forKey:@"push_transition"];
}
}
如您所见,它会尝试再次将自己设置为委托,因此其他视图控制器可以切换回另一个故事板。为了使其工作,您必须继承UISplitView:
标题强>
#import <UIKit/UIKit.h>
#import "RootViewControllerDelegate.h"
@interface MySplitViewController : UISplitViewController
@property (nonatomic, weak) id <RootViewControllerDelegate> rootViewControllerDelegate;
@end
<强>实施强>
#import "MySplitViewController.h"
@implementation MySplitViewController
@synthesize rootViewControllerDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
for (UIViewController *viewController in self.viewControllers) {
if ([viewController respondsToSelector:@selector(setRootViewControllerDelegate:)]) {
[viewController setRootViewControllerDelegate:self.rootViewControllerDelegate];
}
}
}
@end
这个简单的实现查找接受root-view-controller-delegate并将其交给的子视图控制器。因此,当您想要向某个(主要或详细信息)视图添加“显示登录”按钮时,只需创建自己的UIViewController子类,添加@property id<RootViewControllerDelegate> rootViewControllerDelegate
并将此操作与按钮相关联:
- (IBAction)loginButtonClicked:(id)sender {
UIStoryboard *mainSB=[UIStoryboard storyboardWithName:@"LoginStoryboard" bundle:nil];
NSString *animationDirection=kCATransitionFromTop;
UIDeviceOrientation currentOrientation=[[UIDevice currentDevice] orientation];
if (currentOrientation==UIDeviceOrientationLandscapeLeft) {
animationDirection=kCATransitionFromBottom;
}
[self.rootViewControllerDelegate switchToStoryboard:mainSB animationDirectionOrNil:animationDirection];
}
随意根据您的需要调整所有内容。
答案 1 :(得分:1)
这是登录.h
#import <UIKit/UIKit.h>
#import "Constants.h"
@interface LogInViewController : UIViewController<UITextFieldDelegate>
@property (nonatomic) BOOL pinValidated;
@end
这是登录.m
的代码#import "LogInViewController.h"
#import "KeychainWrapper.h"
@interface LogInViewController ()
@end
@implementation LogInViewController
@synthesize pinValidated;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
// Helper method to congregate the Name and PIN fields for validation.
- (BOOL)credentialsValidated
{
NSString *name = [[NSUserDefaults standardUserDefaults] stringForKey:USERNAME];
BOOL pin = [[NSUserDefaults standardUserDefaults] boolForKey:PIN_SAVED];
if (name && pin) {
return YES;
} else {
return NO;
}
}
- (void)presentAlertViewForPassword
{
// 1
BOOL hasPin = [[NSUserDefaults standardUserDefaults] boolForKey:PIN_SAVED];
// 2
if (hasPin) {
// 3
NSString *user = [[NSUserDefaults standardUserDefaults] stringForKey:USERNAME];
NSString *message = [NSString stringWithFormat:@"What is %@'s password?", user];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Enter Password"
message:message
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"Done", nil];
// 4
[alert setAlertViewStyle:UIAlertViewStyleSecureTextInput]; // Gives us the password field
alert.tag = kAlertTypePIN;
// 5
UITextField *pinField = [alert textFieldAtIndex:0];
pinField.delegate = self;
pinField.autocapitalizationType = UITextAutocapitalizationTypeWords;
pinField.tag = kTextFieldPIN;
[alert show];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Setup Credentials"
message:@"Enter Your information!"
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"Done", nil];
// 6
[alert setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
alert.tag = kAlertTypeSetup;
UITextField *nameField = [alert textFieldAtIndex:0];
nameField.autocapitalizationType = UITextAutocapitalizationTypeWords;
nameField.placeholder = @"Name"; // Replace the standard placeholder text with something more applicable
nameField.delegate = self;
nameField.tag = kTextFieldName;
UITextField *passwordField = [alert textFieldAtIndex:1]; // Capture the Password text field since there are 2 fields
passwordField.delegate = self;
passwordField.tag = kTextFieldPassword;
[alert show];
}
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (alertView.tag == kAlertTypePIN) {
if (buttonIndex == 1 && self.pinValidated) { // User selected "Done"
[self performSegueWithIdentifier:@"ScoutSegue" sender:self];
self.pinValidated = NO;
} else { // User selected "Cancel"
[self presentAlertViewForPassword];
}
} else if (alertView.tag == kAlertTypeSetup) {
if (buttonIndex == 1 && [self credentialsValidated]) { // User selected "Done"
[self performSegueWithIdentifier:@"ScoutSegue" sender:self];
} else { // User selected "Cancel"
[self presentAlertViewForPassword];
}
}
}
#pragma mark - Text Field + Alert View Methods
- (void)textFieldDidEndEditing:(UITextField *)textField
{
// 1
switch (textField.tag) {
case kTextFieldPIN: // We go here if this is the 2nd+ time used (we've already set a PIN at Setup).
NSLog(@"User entered PIN to validate");
if ([textField.text length] > 0) {
// 2
NSUInteger fieldHash = [textField.text hash]; // Get the hash of the entered PIN, minimize contact with the real password
// 3
if ([KeychainWrapper compareKeychainValueForMatchingPIN:fieldHash]) { // Compare them
NSLog(@"** User Authenticated!!");
self.pinValidated = YES;
} else {
NSLog(@"** Wrong Password :(");
self.pinValidated = NO;
}
}
break;
case kTextFieldName: // 1st part of the Setup flow.
NSLog(@"User entered name");
if ([textField.text length] > 0) {
[[NSUserDefaults standardUserDefaults] setValue:textField.text forKey:USERNAME];
[[NSUserDefaults standardUserDefaults] synchronize];
}
break;
case kTextFieldPassword: // 2nd half of the Setup flow.
NSLog(@"User entered PIN");
if ([textField.text length] > 0) {
NSUInteger fieldHash = [textField.text hash];
// 4
NSString *fieldString = [KeychainWrapper securedSHA256DigestHashForPIN:fieldHash];
NSLog(@"** Password Hash - %@", fieldString);
// Save PIN hash to the keychain (NEVER store the direct PIN)
if ([KeychainWrapper createKeychainValue:fieldString forIdentifier:PIN_SAVED]) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:PIN_SAVED];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(@"** Key saved successfully to Keychain!!");
}
}
break;
default:
break;
}
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.pinValidated = NO;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self presentAlertViewForPassword];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
@end
这是常量
的代码// Used for saving to NSUserDefaults that a PIN has been set, and is the unique identifier for the Keychain.
#define PIN_SAVED @"hasSavedPIN"
// Used for saving the user's name to NSUserDefaults.
#define USERNAME @"username"
// Used to specify the application used in accessing the Keychain.
#define APP_NAME [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]
// Used to help secure the PIN.
// Ideally, this is randomly generated, but to avoid the unnecessary complexity and overhead of storing the Salt separately, we will standardize on this key.
// !!KEEP IT A SECRET!!
#define SALT_HASH @"FvTivqTqZXsgLLx1v3P8TGRyVHaSOB1pvfm02wvGadj7RLHV8GrfxaZ84oGA8RsKdNRpxdAojXYg9iAj"
// Typedefs just to make it a little easier to read in code.
typedef enum {
kAlertTypePIN = 0,
kAlertTypeSetup
} AlertTypes;
typedef enum {
kTextFieldPIN = 1,
kTextFieldName,
kTextFieldPassword
} TextFieldTypes;
这是keychainwrapper
#import <Foundation/Foundation.h>
#import <Security/Security.h>
#import <CommonCrypto/CommonHMAC.h>
@interface KeychainWrapper : NSObject
// Generic exposed method to search the keychain for a given value. Limit one result per search.
+ (NSData *)searchKeychainCopyMatchingIdentifier:(NSString *)identifier;
// Calls searchKeychainCopyMatchingIdentifier: and converts to a string value.
+ (NSString *)keychainStringFromMatchingIdentifier:(NSString *)identifier;
// Simple method to compare a passed in hash value with what is stored in the keychain.
// Optionally, we could adjust this method to take in the keychain key to look up the value.
+ (BOOL)compareKeychainValueForMatchingPIN:(NSUInteger)pinHash;
// Default initializer to store a value in the keychain.
// Associated properties are handled for you - setting Data Protection Access, Company Identifer (to uniquely identify string, etc).
+ (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier;
// Updates a value in the keychain. If you try to set the value with createKeychainValue: and it already exists,
// this method is called instead to update the value in place.
+ (BOOL)updateKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier;
// Delete a value in the keychain.
+ (void)deleteItemFromKeychainWithIdentifier:(NSString *)identifier;
// Generates an SHA256 (much more secure than MD5) hash.
+ (NSString *)securedSHA256DigestHashForPIN:(NSUInteger)pinHash;
+ (NSString*)computeSHA256DigestForString:(NSString*)input;
@end
最后这里是keychainwrapper .m
的代码#import "KeychainWrapper.h"
#import "Constants.h"
@implementation KeychainWrapper
// *** NOTE *** This class is ARC compliant - any references to CF classes must be paired with a "__bridge" statement to
// cast between Objective-C and Core Foundation Classes. WWDC 2011 Video "Introduction to Automatic Reference Counting" explains this.
// *** END NOTE ***
+ (NSMutableDictionary *)setupSearchDirectoryForIdentifier:(NSString *)identifier {
// Setup dictionary to access keychain.
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
// Specify we are using a password (rather than a certificate, internet password, etc).
[searchDictionary setObject:( id)kSecClassGenericPassword forKey:( id)kSecClass];
// Uniquely identify this keychain accessor.
[searchDictionary setObject:APP_NAME forKey:( id)kSecAttrService];
// Uniquely identify the account who will be accessing the keychain.
NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
[searchDictionary setObject:encodedIdentifier forKey:( id)kSecAttrGeneric];
[searchDictionary setObject:encodedIdentifier forKey:( id)kSecAttrAccount];
return searchDictionary;
}
+ (NSData *)searchKeychainCopyMatchingIdentifier:(NSString *)identifier
{
NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier];
// Limit search results to one.
[searchDictionary setObject:( id)kSecMatchLimitOne forKey:( id)kSecMatchLimit];
// Specify we want NSData/CFData returned.
[searchDictionary setObject:( id)kCFBooleanTrue forKey:( id)kSecReturnData];
// Search.
NSData *result = nil;
CFTypeRef foundDict = NULL;
OSStatus status = SecItemCopyMatching(( CFDictionaryRef)searchDictionary, &foundDict);
if (status == noErr) {
result = ( NSData *)foundDict;
} else {
result = nil;
}
return result;
}
+ (NSString *)keychainStringFromMatchingIdentifier:(NSString *)identifier
{
NSData *valueData = [self searchKeychainCopyMatchingIdentifier:identifier];
if (valueData) {
NSString *value = [[NSString alloc] initWithData:valueData
encoding:NSUTF8StringEncoding];
return value;
} else {
return nil;
}
}
+ (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier
{
NSMutableDictionary *dictionary = [self setupSearchDirectoryForIdentifier:identifier];
NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding];
[dictionary setObject:valueData forKey:( id)kSecValueData];
// Protect the keychain entry so it's only valid when the device is unlocked.
[dictionary setObject:( id)kSecAttrAccessibleWhenUnlocked forKey:( id)kSecAttrAccessible];
// Add.
OSStatus status = SecItemAdd(( CFDictionaryRef)dictionary, NULL);
// If the addition was successful, return. Otherwise, attempt to update existing key or quit (return NO).
if (status == errSecSuccess) {
return YES;
} else if (status == errSecDuplicateItem){
return [self updateKeychainValue:value forIdentifier:identifier];
} else {
return NO;
}
}
+ (BOOL)updateKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier
{
NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier];
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding];
[updateDictionary setObject:valueData forKey:( id)kSecValueData];
// Update.
OSStatus status = SecItemUpdate(( CFDictionaryRef)searchDictionary,
( CFDictionaryRef)updateDictionary);
if (status == errSecSuccess) {
return YES;
} else {
return NO;
}
}
+ (void)deleteItemFromKeychainWithIdentifier:(NSString *)identifier
{
NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier];
CFDictionaryRef dictionary = ( CFDictionaryRef)searchDictionary;
//Delete.
SecItemDelete(dictionary);
}
+ (BOOL)compareKeychainValueForMatchingPIN:(NSUInteger)pinHash
{
if ([[self keychainStringFromMatchingIdentifier:PIN_SAVED] isEqualToString:[self securedSHA256DigestHashForPIN:pinHash]]) {
return YES;
} else {
return NO;
}
}
// This is where most of the magic happens (the rest of it happens in computeSHA256DigestForString: method below).
// Here we are passing in the hash of the PIN that the user entered so that we can avoid manually handling the PIN itself.
// Then we are extracting the username that the user supplied during setup, so that we can add another unique element to the hash.
// From there, we mash the user name, the passed-in PIN hash, and the secret key (from ChristmasConstants.h) together to create
// one long, unique string.
// Then we send that entire hash mashup into the SHA256 method below to create a "Digital Digest," which is considered
// a one-way encryption algorithm. "One-way" means that it can never be reverse-engineered, only brute-force attacked.
// The algorthim we are using is Hash = SHA256(Name + Salt + (Hash(PIN))). This is called "Digest Authentication."
+ (NSString *)securedSHA256DigestHashForPIN:(NSUInteger)pinHash
{
// 1
NSString *name = [[NSUserDefaults standardUserDefaults] stringForKey:USERNAME];
name = [name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// 2
NSString *computedHashString = [NSString stringWithFormat:@"%@%i%@", name, pinHash, SALT_HASH];
// 3
NSString *finalHash = [self computeSHA256DigestForString:computedHashString];
NSLog(@"** Computed hash: %@ for SHA256 Digest: %@", computedHashString, finalHash);
return finalHash;
}
// This is where the rest of the magic happens.
// Here we are taking in our string hash, placing that inside of a C Char Array, then parsing it through the SHA256 encryption method.
+ (NSString*)computeSHA256DigestForString:(NSString*)input
{
const char *cstr = [input cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:input.length];
uint8_t digest[CC_SHA256_DIGEST_LENGTH];
// This is an iOS5-specific method.
// It takes in the data, how much data, and then output format, which in this case is an int array.
CC_SHA256(data.bytes, data.length, digest);
// Setup our Objective-C output.
NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
// Parse through the CC_SHA256 results (stored inside of digest[]).
for(int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[output appendFormat:@"%02x", digest[i]];
}
return output;
}
@end
这是在任何其他视图天气之前创建登录视图的蓝图,它是视图控制器或任何其他视图,如标签栏等。仔细查看这组代码并根据需要修改它们。希望这可以帮助你我的朋友。代码是你所要做的就是研究它们并将它们修改为你想要的。快乐的编码。