Xamarin iOS使用按钮接接收用户输入
按钮用户交互基础控件即使iPhone或者iPad用户使用 *** 作通触摸实现点击点击 *** 作控件往往按钮控件般使用UIButton类实现按钮本节主要讲解按钮相关内容
Xamarin iOS使用代码添加按钮
由于按钮拖放式比较简单所再介绍直接讲解代码何添加按钮使用代码主视图添加按钮式222节讲解步骤首先需要使用UIButton类实例化按钮象设置位置使用AddSubview()按钮象添加主视图(由于视图添加式都面省略使用代码添加视图块内容)
示例2-5使用代码主视图添加青色按钮代码:
using System;
using SystemDrawing;
using MonoTouchFoundation;
using MonoTouchUIKit;
namespace Application
{
public partial class __16ViewController : UIViewController
{
…… //省略视图控制器构造析构
#region View lifecycle
public override void ViewDidLoad ()
{
baseViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib
UIButton button = new UIButton (); //实例化按钮象
buttonFrame = new RectangleF (120, 261, 80, 30); //设置按钮象位置
buttonBackgroundColor = UIColorCyan; //设置按钮象背景颜色
thisViewAddSubview (button); //按钮象添加主视图
}
…… //省略视图加载卸载前些
#endregion
}
}
运行效图213所示
图213 运行效
注意:由于按钮视图继承UIView类所继承UIView类属性
Xamarin iOS按钮格式化设置
图213看明明添加按钮添加空白视图让按钮空白视图区别需要按钮进行些设置
1设置按钮外观
外观直接区别按钮其视图手段使用Interface Builder添加按钮外观设置式两种种直接打属性界面按钮外观进行设置图214所示
图214 按钮设置
另种使用代码按钮外观进行设置种式适用于使用代码添加按钮表2-2列用些外观设置属性
表2-2 用属性
示例2-6面主视图添加按钮按钮标题I am button标题颜色黑色代码:
using System;
using SystemDrawing;
using MonoTouchFoundation;
using MonoTouchUIKit;
namespace Application
{
public partial class __18ViewController : UIViewController
{
…… //省略视图控制器构造析构
#region View lifecycle
public override void ViewDidLoad ()
{
baseViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib
UIButton button = new UIButton ();
buttonFrame = new RectangleF (107, 269, 120, 30);
buttonSetTitle ("I am button", UIControlStateNormal); //设置按钮标题
buttonSetTitleColor (UIColorBlack, UIControlStateNormal); //设置按钮标题颜色
thisViewAddSubview (button);
}
…… //省略视图加载卸载前些
#endregion
}
}
运行效图215所示
图215 运行效
2设置按钮状态
示例2-6设置按钮标题颜色需要按钮状态进行设置表示按钮某状态标题标题颜色例UIControlStateNormal表示按钮种状态于像按钮类视图即接受用户输入视图称控件些控件都自状态表2-3发者详细介绍控件状态
表2-3 控件状态
3设置按钮类型
按钮形式种例通讯录添加新联系按钮加号;查看电详细信息叹号等些按钮实现实例化按钮象使用UIButtonType实现UIButtonType内容表2-4所示
表2-4 UIButtonType内容
示例2-7代码设置两同风格按钮代码:
using System;
using SystemDrawing;
using MonoTouchFoundation;
using MonoTouchUIKit;
namespace Application
{
public partial class __19ViewController : UIViewController
{
…… //省略视图控制器构造析构
#region View lifecycle
public override void ViewDidLoad ()
{
baseViewDidLoad ();
// Perform any additional setup after loading the view, typically from a nib
//实例化按钮象并设置按钮类型
UIButton button1 = new UIButton (UIButtonTypeDetailDisclosure);
button1Center = new PointF (160, 150); //设置按钮位置
thisViewAddSubview (button1);
//实例化按钮象并设置按钮类型
UIButton button2 = new UIButton (UIButtonTypeContactAdd);
button2Center = new PointF (160, 350); //设置按钮位置
thisViewAddSubview (button2);
}
…… //省略视图加载卸载前些
#endregion
}
}
由于ios系统对用户隐私的控制,第三方应用程序只能通过苹果官方接口调用系统通讯录,不能像android那样直接 *** 作通讯录数据库。
一般地,使用系统自带通讯录的方法有两种,一种是直接将整个通讯录引入到应用程序,另一种是逐条读取通讯录中的每一条联系人信息。下面我们就一一详解。
1 直接引用整个通讯录
使用的类:ABPeoplePickerNavigationController
方法:
在LocalAddressBookControllerh文件中
#import <UIKit/UIKith>
#import <AddressBook/AddressBookh>
#import <AddressBookUI/AddressBookUIh>
@interface LocalAddressBookController : UIViewController<ABPersonViewControllerDelegate,ABPeoplePickerNavigationControllerDelegate,ABNewPersonViewControllerDelegate>
{
ABPeoplePickerNavigationController picker;
ABNewPersonViewController personViewController;
}
@end
在LocalAddressBookControllerm文件中
#import "LocalAddressBookControllerh"
#import "PublicHeaderh"
@implementation LocalAddressBookController
- (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
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
picker = [[ABPeoplePickerNavigationController alloc]init];
pickerviewframe = CGRectMake(0, 0, Screen_width, Screen_height-48);
pickerpeoplePickerDelegate = self;
pickerdelegate = self; //为了隐藏右上角的“取消”按钮
[picker setHidesBottomBarWhenPushed:YES];
[picker setNavigationBarHidden:NO animated:NO];//显示上方的NavigationBar和搜索框
[selfview addSubview:pickerview];
}
#pragma mark UINavigationControllerDelegate methods
//隐藏“取消”按钮
- (void)navigationController:(UINavigationController )navigationController willShowViewController:(UIViewController )viewController animated:(BOOL)animated
{
UIView custom = [[UIView alloc] initWithFrame:CGRectMake(00f,00f,00f,00f)];
UIBarButtonItem btn = [[UIBarButtonItem alloc] initWithCustomView:custom];
[viewControllernavigationItem setRightBarButtonItem:btn animated:NO];
[btn release];
[custom release];
}
#pragma mark - peoplePickerDelegate Methods
-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController )peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
// [picker setNavigationBarHidden:NO animated:NO];
NSLog(@"%@", (NSString)ABRecordCopyCompositeName(person));
return YES;
}
-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController )peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
return YES;
}
-(BOOL)personViewController:(ABPersonViewController )personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
return YES;
}
//“取消”按钮的委托响应方法
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController )peoplePicker
{
//assigning control back to the main controller
[picker dismissModalViewControllerAnimated:YES];
}
//……
@end
利用观察者模式打造的事件总线的优点不必多说(当然也有很多缺点),如 EventBus 和 RxBus 用的好的话能起到很好的解耦作用,使整个程序架构更加清晰,不至于到处传递各种 Callback。但是他们都缺少了对 View 层(Activity、Fragment 等)的生命周期的感知能力,需要在生命周期结束时手动解除观察者,手动管理生命周期十分繁琐且很容易出错。
而 Google 推出的 Lifecycle 库就是为了解决这一问题,其中的 LiveData 就是一个具有生命周期感知能力的观察者,如果用它来打造一个能够感知生命周期的事件总线,岂不美哉!
在随着对 LiveData 的运用和理解的逐渐深入,特别是对它的「生命周期感知能力」有了更深的理解,慢慢发现这样用的一些坑,借此机会就跟大家分享探讨一下。而且我平时也有把 LiveData 纯粹当作事件传递来用,特别是列表 *** 作(比如涉及 IO 的增删 *** 作,View 层需要知道哪个数据改动了,以及 *** 作是否成功等,只能以事件的形式传递)。
在我的前一篇文章中也提到过,大家也可以直接看源码。postValue 只是把传进来的数据先存到 mPendingData,然后往主线程抛一个 Runnable,在这个 Runnable 里面再调用 setValue 来把存起来的值真正设置上去,并回调观察者们。而如果在这个 Runnable 执行前多次 postValue,其实只是改变暂存的值 mPendingData,并不会再次抛另一个 Runnable。这就会出现后设置的值把前面的值覆盖掉的问题,会导致事件丢失。
LiveData 的生命周期感知能力就体现在这里,它不会回调处于「非激活状态」(即 onStart 之后到 onPause 之前)的观察者,因为这时更新 View 没有意义,而且比较危险,它会等到观察者激活之后再把新的值回调给他。
但是如果我传了多个数据(假设都是用 setValue 保证不会被覆盖),那些处于非激活状态的观察者是毫不知情的,他们在激活的时候只会收到最后一个数据。这对于事件传递来说,就表现为事件丢失,中间传的任何数据都无法收到,那也就失去了事件传递的意义。
从上面两点也可以看出, LiveData (或者说它的观察者) 在观察者激活之前并不关心中间经历过多少次数据变更,它只会在某个观察者激活时,传递给他最新的值,中间的值都不会起作用。
当然 LiveData 的设计也不是为了传递事件的,它是为了反应 View 当前状态的, View 层只需要根据当前 LiveData 的值去渲染数据就行,非激活状态时 View 都不可见,就算更新了也没意义。
我最开始也是觉得 LiveData 用到了观察者模式,而且可以进行一些不同 Fragment 之间数据通讯,就想到了事件总线,现在想想当时还是 too young too naive。
当然,也不是说 LiveData 也不是没救了,有谷歌给我们提供了强大的 Lifecycle 库,我们完全可以自己造一个不会丢事件的 LiveData 。
LiveData 的其他功能做的很完善,只是会丢事件,我们要改造就要就针对上面的问题逐个击破。
对于 postValue 的问题,既然它最后也是调用的 setValue,丢数据是因为只抛了一次 Runable,那我们就自己每次都往主线程抛一个 Runable 就能解决这个问题
其实我觉得这个问题的主要「责任」并不在 LiveData,而是在它的观察者,「是你告诉我你非激活的呀,那我怎么给你发数据呢,我发给你,万一你出问题了呢,那到底谁负责?」。
我们常用的观察者其实是 LifecycleBoundObserver,在调用 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer< super T> observer) 会自动帮我们封装一个这样的观察者,而它会根据 LifecycleOwner 的生命周期呈现出「激活」和「非激活」状态。
LiveData 默认的还有另外一种观察者 AlwaysActiveObserver,它是我们在调用 public void observeForever(@NonNull Observer< super T> observer) 时生成的,顾名思义它会一直处于激活状态,LiveData 当然也不会替我们管理这样观察者的生命周期,我们需要在不使用时手动调用 public void removeObserver(@NonNull final Observer< super T> observer) 移除观察者,否则可能会内存泄漏。
这个 AlwaysActiveObserver 看样子能够解决我们的问题,他一直处于激活状态,那所有的事件都会回调给他,但是需要我们自己管理生命周期。这不是开历史倒车吗?好不容易有生命周期感知了,现在又要自己手动搞?
手动管理生命周期是绝对不能忍的,AlwaysActiveObserver 可以解决刚才说的问题,那我们就造一个新的观察者来管理 observeForever 和 removeObserver 的问题。既然要造,那就造个好用的,首先事件一定不能丢,要不就没意义了;而且生命周期要观察者自己管理,不能只是简单的 observeForever 和 removeObserver,非激活状态之类的也要考虑进去。
既然要管理生命周期,那就不得不用到 LifecycleOwner、Lifecycle,然后自己观察 LifecycleOwner 的 Lifecycle。
Lifecycle 对外只给了这个接口,并不含有任何回调,我们需要用注释里说的 OnLifecycleEvent 注解来标记相应的函数,Lifecycle 会通过反射拿到标记的函数,然后生成对应的适配器,感兴趣的可以看下 LifecyclinggetCallback 函数。比如我们可以这样用
拿到生命周期后,我们就可以在一开始 observeForever ,在 LifecycleEventON_DESTROY 时 removeObserver 。
接下来就要考虑激活和非激活的状态了,既然用了 observeForever ,那每次事件都会有回调,这时候如果 Lifecycle 处于激活状态,那可以直接把事件发出去。但如果非激活,不能直接把事件发出去,又不能丢,那我们就需要先把事件存起来,然后在 Lifecycle 变为激活状态时再把存起来的事件发送出去。简单画了下流程图。
31 也说过要自己处理 postValue,其次要保证用我们自己定义的观察者,需要重写 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer< super T> observer)。
这个工具包里还有其他一些我平时常用的小工具,这里简单分享下:
以上就是关于使用 PreviewView 来展示相机预览全部的内容,包括:使用 PreviewView 来展示相机预览、如何更新及替换ViewPager中的Fragment、VS2017 Xamarin IOS设置等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)