зеркало из https://github.com/xamarin/ios-samples.git
nullability
This commit is contained in:
Родитель
8cd05dce4e
Коммит
a4088aa640
|
@ -1,7 +1,6 @@
|
|||
using LocalAuthentication;
|
||||
|
||||
namespace StoryboardTable;
|
||||
|
||||
/*
|
||||
* https://developer.apple.com/ios/human-interface-guidelines/user-interaction/authentication/
|
||||
*
|
||||
|
@ -19,137 +18,102 @@ namespace StoryboardTable;
|
|||
/// </remarks>
|
||||
partial class AuthenticationViewController : UIViewController
|
||||
{
|
||||
LAContextReplyHandler replyHandler;
|
||||
LAContextReplyHandler? replyHandler;
|
||||
/// <summary>String to use for display</summary>
|
||||
string BiometryType = "";
|
||||
string BiometryType = string.Empty;
|
||||
|
||||
public AuthenticationViewController(IntPtr handle) : base(handle)
|
||||
public AuthenticationViewController (IntPtr handle) : base (handle)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ViewWillAppear(bool animated)
|
||||
public override void ViewWillAppear (bool animated)
|
||||
{
|
||||
base.ViewWillAppear(animated);
|
||||
base.ViewWillAppear (animated);
|
||||
|
||||
// bind every time, to reflect deletion in the Detail view
|
||||
unAuthenticatedLabel.Text = "";
|
||||
unAuthenticatedLabel.Text = string.Empty;
|
||||
|
||||
var context = new LAContext();
|
||||
var buttonText = "";
|
||||
if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out var authError1))
|
||||
{ // has Biometrics (Touch or Face)
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
|
||||
{
|
||||
var context = new LAContext ();
|
||||
var buttonText = string.Empty;
|
||||
if (context.CanEvaluatePolicy (LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out var authError1)) { // has Biometrics (Touch or Face)
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion (11, 0)) {
|
||||
context.LocalizedReason = "Authorize for access to secrets"; // iOS 11
|
||||
BiometryType = context.BiometryType == LABiometryType.TouchId ? "Touch ID" : "Face ID";
|
||||
buttonText = $"Login with {BiometryType}";
|
||||
}
|
||||
else
|
||||
{ // no FaceID before iOS 11
|
||||
} else { // no FaceID before iOS 11
|
||||
buttonText = $"Login with Touch ID";
|
||||
}
|
||||
}
|
||||
else if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, out var authError2))
|
||||
{
|
||||
} else if (context.CanEvaluatePolicy (LAPolicy.DeviceOwnerAuthentication, out var authError2)) {
|
||||
buttonText = $"Login"; // with device PIN
|
||||
BiometryType = "Device PIN";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Application might choose to implement a custom username/password
|
||||
buttonText = "Use unsecured";
|
||||
BiometryType = "none";
|
||||
}
|
||||
AuthenticateButton.SetTitle(buttonText, UIControlState.Normal);
|
||||
AuthenticateButton.SetTitle (buttonText, UIControlState.Normal);
|
||||
}
|
||||
|
||||
partial void AuthenticateMe(UIButton sender)
|
||||
partial void AuthenticateMe (UIButton sender)
|
||||
{
|
||||
var context = new LAContext();
|
||||
var context = new LAContext ();
|
||||
NSError AuthError;
|
||||
var localizedReason = new NSString("To access secrets");
|
||||
var localizedReason = new NSString ("To access secrets");
|
||||
|
||||
// because LocalAuthentication APIs have been extended over time, need to check iOS version before setting some properties
|
||||
context.LocalizedFallbackTitle = "Fallback"; // iOS 8
|
||||
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
|
||||
{
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion (10, 0)) {
|
||||
context.LocalizedCancelTitle = "Cancel"; // iOS 10
|
||||
}
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
|
||||
{
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion (11, 0)) {
|
||||
context.LocalizedReason = "Authorize for access to secrets"; // iOS 11
|
||||
BiometryType = context.BiometryType == LABiometryType.TouchId ? "TouchID" : "FaceID";
|
||||
}
|
||||
|
||||
//Use canEvaluatePolicy method to test if device is TouchID or FaceID enabled
|
||||
//Use the LocalAuthentication Policy DeviceOwnerAuthenticationWithBiometrics
|
||||
if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out AuthError))
|
||||
{
|
||||
Console.WriteLine("TouchID/FaceID available/enrolled");
|
||||
replyHandler = new LAContextReplyHandler((success, error) =>
|
||||
{
|
||||
if (context.CanEvaluatePolicy (LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out AuthError)) {
|
||||
replyHandler = new LAContextReplyHandler ( (success, error) => {
|
||||
//Make sure it runs on MainThread, not in Background
|
||||
this.InvokeOnMainThread(() =>
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
Console.WriteLine($"You logged in with {BiometryType}!");
|
||||
|
||||
PerformSegue("AuthenticationSegue", this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(error.LocalizedDescription);
|
||||
this.InvokeOnMainThread ( () => {
|
||||
if (success) {
|
||||
PerformSegue ("AuthenticationSegue", this);
|
||||
} else {
|
||||
//Show fallback mechanism here
|
||||
unAuthenticatedLabel.Text = $"{BiometryType} Authentication Failed";
|
||||
//AuthenticateButton.Hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
//Use evaluatePolicy to start authentication operation and show the UI as an Alert view
|
||||
//Use the LocalAuthentication Policy DeviceOwnerAuthenticationWithBiometrics
|
||||
context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason, replyHandler);
|
||||
context.EvaluatePolicy (LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason, replyHandler);
|
||||
}
|
||||
else if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, out AuthError))
|
||||
{
|
||||
Console.WriteLine("When TouchID/FaceID aren't available or enrolled, use the device PIN");
|
||||
replyHandler = new LAContextReplyHandler((success, error) =>
|
||||
{
|
||||
else if (context.CanEvaluatePolicy (LAPolicy.DeviceOwnerAuthentication, out AuthError)) {
|
||||
replyHandler = new LAContextReplyHandler ( (success, error) => {
|
||||
//Make sure it runs on MainThread, not in Background
|
||||
this.InvokeOnMainThread(() =>
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
Console.WriteLine($"You logged in with {BiometryType}!");
|
||||
|
||||
PerformSegue("AuthenticationSegue", this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(error.LocalizedDescription);
|
||||
this.InvokeOnMainThread ( () => {
|
||||
if (success) {
|
||||
PerformSegue ("AuthenticationSegue", this);
|
||||
} else {
|
||||
//Show fallback mechanism here
|
||||
unAuthenticatedLabel.Text = "Device PIN Authentication Failed";
|
||||
AuthenticateButton.Hidden = true;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
//Use evaluatePolicy to start authentication operation and show the UI as an Alert view
|
||||
//Use the LocalAuthentication Policy DeviceOwnerAuthenticationWithBiometrics
|
||||
context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, localizedReason, replyHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.EvaluatePolicy (LAPolicy.DeviceOwnerAuthentication, localizedReason, replyHandler);
|
||||
} else {
|
||||
// User hasn't configured a PIN or any biometric auth.
|
||||
// App may implement its own login, or choose to allow open access
|
||||
unAuthenticatedLabel.Text = "No device auth configured";
|
||||
|
||||
var okCancelAlertController = UIAlertController.Create("No authentication", "This device does't have authentication configured.", UIAlertControllerStyle.Alert);
|
||||
okCancelAlertController.AddAction(UIAlertAction.Create("Use unsecured", UIAlertActionStyle.Default, alert => PerformSegue("AuthenticationSegue", this)));
|
||||
okCancelAlertController.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, alert => Console.WriteLine("Cancel was clicked")));
|
||||
PresentViewController(okCancelAlertController, true, null);
|
||||
var okCancelAlertController = UIAlertController.Create ("No authentication", "This device does't have authentication configured.", UIAlertControllerStyle.Alert);
|
||||
okCancelAlertController.AddAction(UIAlertAction.Create ("Use unsecured", UIAlertActionStyle.Default, alert => PerformSegue ("AuthenticationSegue", this)));
|
||||
okCancelAlertController.AddAction(UIAlertAction.Create ("Cancel", UIAlertActionStyle.Cancel, alert => Console.WriteLine ("Cancel was clicked")));
|
||||
PresentViewController (okCancelAlertController, true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
public partial class DetailViewController : UIViewController
|
||||
{
|
||||
object detailItem;
|
||||
object? detailItem;
|
||||
|
||||
public DetailViewController (IntPtr handle) : base (handle)
|
||||
{
|
||||
|
@ -21,7 +21,7 @@ public partial class DetailViewController : UIViewController
|
|||
void ConfigureView ()
|
||||
{
|
||||
// Update the user interface for the detail item
|
||||
if (IsViewLoaded && detailItem != null)
|
||||
if (IsViewLoaded && detailItem is not null)
|
||||
detailDescriptionLabel.Text = detailItem.ToString ();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
// This file is used by Code Analysis to maintain SuppressMessage
|
||||
// attributes that are applied to this project.
|
||||
// Project-level suppressions either have no target or are given
|
||||
// a specific target and scoped to a namespace, type, member, etc.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>", Scope = "member", Target = "~M:StoryboardTable.SecretTableSource.GetCell(UIKit.UITableView,Foundation.NSIndexPath)~UIKit.UITableViewCell")]
|
|
@ -12,6 +12,6 @@ public partial class MasterViewController : UITableViewController
|
|||
base.ViewWillAppear (animated);
|
||||
|
||||
// bind every time, to reflect deletion in the Detail view
|
||||
TableView.Source = new SecretTableSource();
|
||||
TableView.Source = new SecretTableSource ();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace StoryboardTable;
|
|||
/// <summary>
|
||||
/// Represents a Chore.
|
||||
/// </summary>
|
||||
///
|
||||
///
|
||||
public class SecretItem {
|
||||
|
||||
public SecretItem ()
|
||||
|
@ -11,7 +11,7 @@ public class SecretItem {
|
|||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Notes { get; set; } = string.Empty;
|
||||
public bool Done { get; set; }
|
||||
}
|
||||
|
|
|
@ -9,25 +9,35 @@ public class SecretTableSource : UITableViewSource {
|
|||
public SecretTableSource ()
|
||||
{
|
||||
tableItems = new List<SecretItem> {
|
||||
new SecretItem() {Name="Gift list", Notes="iPhone X, Apple Watch, HomePod", Done=false},
|
||||
new SecretItem() {Name="Reminders", Notes="buy flowers", Done=false}
|
||||
}.ToArray();
|
||||
new SecretItem () {Name="Gift list", Notes="iPhone X, Apple Watch, HomePod", Done=false},
|
||||
new SecretItem () {Name="Reminders", Notes="buy flowers", Done=false}
|
||||
}.ToArray ();
|
||||
}
|
||||
public override nint RowsInSection (UITableView tableview, nint section)
|
||||
{
|
||||
return tableItems.Length;
|
||||
}
|
||||
=> tableItems.Length;
|
||||
|
||||
public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath)
|
||||
{
|
||||
// in a Storyboard, Dequeue will ALWAYS return a cell,
|
||||
UITableViewCell cell = tableView.DequeueReusableCell (cellIdentifier);
|
||||
// now set the properties as normal
|
||||
cell.TextLabel.Text = tableItems[indexPath.Row].Name;
|
||||
cell.DetailTextLabel.Text = tableItems[indexPath.Row].Notes;
|
||||
|
||||
// Beginning in iOS 14, UITableViewCell has to use cell.ContentConfiguration
|
||||
// to add text
|
||||
if (UIDevice.CurrentDevice.CheckSystemVersion(14, 0)) {
|
||||
var content = cell.DefaultContentConfiguration;
|
||||
content.Text = tableItems[indexPath.Row].Name;
|
||||
content.SecondaryText = tableItems[indexPath.Row].Notes;
|
||||
cell.ContentConfiguration = content;
|
||||
} else {
|
||||
cell.TextLabel.Text = tableItems[indexPath.Row].Name;
|
||||
cell.DetailTextLabel.Text = tableItems[indexPath.Row].Notes;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
public override void RowSelected(UITableView tableView, Foundation.NSIndexPath indexPath)
|
||||
public override void RowSelected (UITableView tableView, Foundation.NSIndexPath indexPath)
|
||||
{
|
||||
tableView.DeselectRow(indexPath, true);
|
||||
tableView.DeselectRow (indexPath, true);
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче