XLForm-extension-PhotoView 3.3.0

XLForm-extension-PhotoView 3.3.0

测试已测试
Lang语言 Obj-CObjective C
许可证 MIT
发布时间最后发布2017年10月

felixyin 维护。



  • 尹彬

XLForm

XMARTLABS 提供。


如果您正在寻找 Swift 原生实现,我们最近创建了 Eureka,这是在 Swift 中对 XLForm 的全新设计。 不要担心,我们仍然会维护和改进 XLForm,Objective-C 仍然很棒!

目的

XLForm 是最灵活和强大的 iOS 库,用于创建动态表视图表单。该库的目标是通过手动表单获得相同的功能,同时只需消耗 1/10 的时间。

XLForm 提供了一种非常强大的 DSL(特定领域语言),用于创建表单。它在运行时跟踪此规范,并动态更新 UI。

#####让我们看看使用 XLForm 创建的 iOS 日历事件表单

Screenshot of native Calendar Event Example

XLForm 做了什么

  • 根据声明性 表单定义 加载表单。
  • 跟踪运行时定义更改以相应地更新表单界面。有关更多详细信息,请参阅本说明书的 动态表单 部分。
  • 支持多值部分,允许我们创建、删除或重新排列行。有关更多详细信息,请参阅下面的 多值部分 部分。
  • 支持 自定义行定义
  • 支持自定义选择器。有关如何定义您自己的选择器的详细信息,请参阅下面的 自定义选择器 部分。
  • 提供几个内联选择器,例如日期选择器和内联选择器,并为创建自定义内联选择器提供了一种方法。
  • 根据表单定义进行表单数据验证。
  • 能够在行之间轻松导航,并且完全可定制。
  • 如果有需要,可以显示输入辅助视图。默认情况下,显示导航输入辅助视图。
  • 对于特定行或整个表单,提供了只读模式。
  • 根据其他行的值,可以隐藏或显示行。这可以通过声明性使用 NSPredicates 来完成。(请参阅 根据其他行的值隐藏或显示行或部分

如何创建表单

创建 XLFormViewController 的实例

Swift
class CalendarEventFormViewController : XLFormViewController {

  required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.initializeForm()
  }


  override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    self.initializeForm()
  }

  func initializeForm() {
    // Implementation details covered in the next section.
  }

}
Objective-C
#import "XLFormViewController.h"

@interface CalendarEventFormViewController: XLFormViewController

@end
@interface ExamplesFormViewController ()

@end

@implementation ExamplesFormViewController

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self){
        [self initializeForm];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self){
        [self initializeForm];
    }
    return self;
}

- (void)initializeForm {
  // Implementation details covered in the next section.
}

@end
实现 initializeForm 方法

要创建表单,我们应该通过 XLFormDescriptor 实例声明它,并将其分配给 XLFormViewController 实例。如我们所言,XLForm 基于一个 DSL,它隐藏了复杂和样板代码,同时保留了手动表单的强大功能和灵活性。

定义表单时,我们使用 3 个类

  • XLFormDescriptor
  • XLFormSectionDescriptor
  • XLFormRowDescriptor

表单定义是一个包含一个或多个部分(XLFormSectionDescriptor实例)的XLFormDescriptor实例,每个部分包含多个行(XLFormRowDescriptor实例)。如您所注意到的,DSL结构类似于UITableView的结构(表 -->> 部分 -- >> 行)。生成的表格视图表单的结构(部分的顺序)与定义的结构相匹配。

让我们看一个initializeForm的示例实现,用于定义iOS日历事件表单
- (void)initializeForm {
  XLFormDescriptor * form;
  XLFormSectionDescriptor * section;
  XLFormRowDescriptor * row;

  form = [XLFormDescriptor formDescriptorWithTitle:@"Add Event"];

  // First section
  section = [XLFormSectionDescriptor formSection];
  [form addFormSection:section];

  // Title
  row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
  [row.cellConfigAtConfigure setObject:@"Title" forKey:@"textField.placeholder"];
  [section addFormRow:row];

  // Location
  row = [XLFormRowDescriptor formRowDescriptorWithTag:@"location" rowType:XLFormRowDescriptorTypeText];
  [row.cellConfigAtConfigure setObject:@"Location" forKey:@"textField.placeholder"];
  [section addFormRow:row];

  // Second Section
  section = [XLFormSectionDescriptor formSection];
  [form addFormSection:section];

  // All-day
  row = [XLFormRowDescriptor formRowDescriptorWithTag:@"all-day" rowType:XLFormRowDescriptorTypeBooleanSwitch title:@"All-day"];
  [section addFormRow:row];

  // Starts
  row = [XLFormRowDescriptor formRowDescriptorWithTag:@"starts" rowType:XLFormRowDescriptorTypeDateTimeInline title:@"Starts"];
  row.value = [NSDate dateWithTimeIntervalSinceNow:60*60*24];
  [section addFormRow:row];
  
  self.form = form;
}

XLForm将根据之前解释的定义加载表格视图表单。最有意思的部分是,它将根据表单定义的修改更新表格视图表单。
这意味着我们可以在运行时对表格视图表单进行修改,添加或删除部分定义或行定义,并且您不再需要关心NSIndexPathUITableViewDelegateUITableViewDataSource或其他复杂性。

要查看更复杂的表单定义,请查看此存储库的Examples文件夹中的示例应用程序。如果您愿意,还可以在您的设备上运行这些示例。 XLForm 没有依赖其他库,但示例项目使用一些cocoapods来演示高级XLForm功能。

在Storyboard中使用XLForm

  • 执行如何创建表单中的步骤
  • 在Interface Builder(IB)中,将一个UIViewController拖拽到Storyboard上
  • 使用Identity Inspector将您自定义的表单类关联到UIViewController

如何运行XLForm示例

  1. 克隆存储库 [email protected]:xmartlabs/XLForm.git。可选地,您也可以将该存储库分叉,并通过您自己的github账户克隆它,如果您想做出贡献,这种方法会更好。
  2. 转到Objective-c或Swift 示例文件夹
  3. 安装示例项目cocoapod依赖。在Objective-c或Swift示例文件夹中运行pod install
  4. 使用XCode打开XLForm或SwiftExample工作空间并运行项目。享受吧!

####输入行

Screenshot of Input Examples

输入行允许用户输入文本值。基本上,它们使用UITextFieldUITextView控件。不同输入行类型的区别在于keyboardTypeautocorrectionTypeautocapitalizationType配置。

static NSString *const XLFormRowDescriptorTypeText = @"text";

将由一个具有UITextAutocorrectionTypeDefaultUITextAutocapitalizationTypeSentencesUIKeyboardTypeDefaultUITextField表示。

static NSString *const XLFormRowDescriptorTypeName = @"name";

将由具有UITextAutocorrectionTypeNoUITextAutocapitalizationTypeWordsUIKeyboardTypeDefaultUITextField表示。

static NSString *const XLFormRowDescriptorTypeURL = @"url";

将由具有UITextAutocorrectionTypeNoUITextAutocapitalizationTypeNoneUIKeyboardTypeURLUITextField表示。

static NSString *const XLFormRowDescriptorTypeEmail = @"email";

将由具有UITextAutocorrectionTypeNoUITextAutocapitalizationTypeNoneUIKeyboardTypeEmailAddressUITextField表示。

static NSString *const XLFormRowDescriptorTypePassword = @"password";

将由具有UITextAutocorrectionTypeNoUITextAutocapitalizationTypeNoneUIKeyboardTypeASCIICapableUITextField表示。
此行类型还将secureTextEntry设置为YES,以隐藏用户输入的内容。

static NSString *const XLFormRowDescriptorTypeNumber = @"number";

将由具有UITextAutocorrectionTypeNoUITextAutocapitalizationTypeNoneUIKeyboardTypeNumbersAndPunctuationUITextField表示。

static NSString *const XLFormRowDescriptorTypePhone = @"phone";

将由具有UIKeyboardTypePhonePadUITextField表示。

static NSString *const XLFormRowDescriptorTypeTwitter = @"twitter";

将由具有UITextAutocorrectionTypeNoUITextAutocapitalizationTypeNoneUIKeyboardTypeTwitterUITextField表示。

static NSString *const XLFormRowDescriptorTypeAccount = @"account";

将由具有UITextAutocorrectionTypeNoUITextAutocapitalizationTypeNoneUIKeyboardTypeDefaultUITextField表示。

static NSString *const XLFormRowDescriptorTypeInteger = @"integer";

将由具有UIKeyboardTypeNumberPadUITextField表示。

static NSString *const XLFormRowDescriptorTypeDecimal = @"decimal";

将用具有 UIKeyboardTypeDecimalPadUITextField 表示。

static NSString *const XLFormRowDescriptorTypeTextView = @"textView";

将用具有 UITextViewUITextAutocorrectionTypeDefaultUITextAutocapitalizationTypeSentencesUIKeyboardTypeDefault 的表示。

####选择行

选择行允许我们从列表中选择一个或多个值。XLForm自带支持8种选择类型。

Screenshot of native Calendar Event Example

static NSString *const XLFormRowDescriptorTypeSelectorPush = @"selectorPush";
static NSString *const XLFormRowDescriptorTypeSelectorActionSheet = @"selectorActionSheet";
static NSString *const XLFormRowDescriptorTypeSelectorAlertView = @"selectorAlertView";
static NSString *const XLFormRowDescriptorTypeSelectorLeftRight = @"selectorLeftRight";
static NSString *const XLFormRowDescriptorTypeSelectorPickerView = @"selectorPickerView";
static NSString *const XLFormRowDescriptorTypeSelectorPickerViewInline = @"selectorPickerViewInline";
static NSString *const XLFormRowDescriptorTypeSelectorSegmentedControl = @"selectorSegmentedControl";
static NSString *const XLFormRowDescriptorTypeMultipleSelector = @"multipleSelector";

通常,我们将有一组对象可供选择(这些对象应该有一个用于显示它们的字符串和一个用于序列化的值),XLForm需要能够显示这些对象。

XLForm显示对象遵循以下规则

  1. 如果 XLFormRowDescriptor 对象的值为 nil,XLForm 使用 noValueDisplayText 行属性作为显示文本。
  2. 如果 XLFormRowDescriptor 实例具有 valueTransformer 属性值。XLForm 使用 NSValueTransformer 将选定的对象转换为 NSString。
  3. 如果对象是 NSStringNSNumber,则使用对象的 description 属性。
  4. 如果对象符合协议 XLFormOptionObject,XLForm 从 formDisplayText 方法获取显示值。
  5. 否则返回 nil。这意味着您应该符合协议 :)

您可能会通过设置 noValueDisplayTextvalueTransformer 属性或使选择选项对象符合 XLFormOptionObject 协议来更改显示文本。

这是协议声明

@protocol XLFormOptionObject <NSObject>

@required
-(NSString *)formDisplayText;
-(id)formValue;

@end

####日期和时间行

XLForms 支持三种日期类型:DateDateTimeTimeCountdown Timer,并且能够以两种方式显示 UIDatePicker 控件:行内和非行内。

Screenshot of native Calendar Event Example

static NSString *const XLFormRowDescriptorTypeDateInline = @"dateInline";
static NSString *const XLFormRowDescriptorTypeDateTimeInline = @"datetimeInline";
static NSString *const XLFormRowDescriptorTypeTimeInline = @"timeInline";
static NSString *const XLFormRowDescriptorTypeCountDownTimerInline = @"countDownTimerInline";
static NSString *const XLFormRowDescriptorTypeDate = @"date";
static NSString *const XLFormRowDescriptorTypeDateTime = @"datetime";
static NSString *const XLFormRowDescriptorTypeTime = @"time";
static NSString *const XLFormRowDescriptorTypeCountDownTimer = @"countDownTimer";

以下是如何定义这些行类型的示例

Objective C

XLFormDescriptor * form;
XLFormSectionDescriptor * section;
XLFormRowDescriptor * row;

form = [XLFormDescriptor formDescriptorWithTitle:@"Dates"];

section = [XLFormSectionDescriptor formSectionWithTitle:@"Inline Dates"];
[form addFormSection:section];

// Date
row = [XLFormRowDescriptor formRowDescriptorWithTag:kDateInline rowType:XLFormRowDescriptorTypeDateInline title:@"Date"];
row.value = [NSDate new];
[section addFormRow:row];

// Time
row = [XLFormRowDescriptor formRowDescriptorWithTag:kTimeInline rowType:XLFormRowDescriptorTypeTimeInline title:@"Time"];
row.value = [NSDate new];
[section addFormRow:row];

// DateTime
row = [XLFormRowDescriptor formRowDescriptorWithTag:kDateTimeInline rowType:XLFormRowDescriptorTypeDateTimeInline title:@"Date Time"];
row.value = [NSDate new];
[section addFormRow:row];

// CountDownTimer
row = [XLFormRowDescriptor formRowDescriptorWithTag:kCountDownTimerInline rowType:XLFormRowDescriptorTypeCountDownTimerInline title:@"Countdown Timer"];
row.value = [NSDate new];
[section addFormRow:row];

Swift

static let dateTime = "dateTime"
static let date = "date"
static let time = "time"

var form : XLFormDescriptor
var section : XLFormSectionDescriptor
var row : XLFormRowDescriptor

form = XLFormDescriptor(title: "Dates") as XLFormDescriptor

section = XLFormSectionDescriptor.formSectionWithTitle("Inline Dates") as XLFormSectionDescriptor
form.addFormSection(section)

// Date
row = XLFormRowDescriptor(tag: tag.date, rowType: XLFormRowDescriptorTypeDateInline, title:"Date")
row.value = NSDate()
section.addFormRow(row)

// Time
row = XLFormRowDescriptor(tag: tag.time, rowType: XLFormRowDescriptorTypeTimeInline, title: "Time")
row.value = NSDate()
section.addFormRow(row)

// DateTime
row = XLFormRowDescriptor(tag: tag.dateTime, rowType: XLFormRowDescriptorTypeDateTimeInline, title: "Date Time")
row.value = NSDate()
section.addFormRow(row)

self.form = form;

####布尔行

XLForms 支持两种布尔控件

Screenshot of native Calendar Event Example

static NSString *const XLFormRowDescriptorTypeBooleanCheck = @"booleanCheck";
static NSString *const XLFormRowDescriptorTypeBooleanSwitch = @"booleanSwitch";

我们还可以使用在 选择行部分 介绍的任何选择行类型来模拟其他类型的布尔行。

####其他行

#####步进器

XLForms 通过 UIStepper 控件支持计数。

Screenshot of native Calendar Event Example

static NSString *const XLFormRowDescriptorTypeStepCounter = @"stepCounter";

您可以轻松设置步进器参数

	row = [XLFormRowDescriptor formRowDescriptorWithTag:kStepCounter rowType:XLFormRowDescriptorTypeStepCounter title:@"Step counter"];
	row.value = @50;
	[row.cellConfigAtConfigure setObject:@YES forKey:@"stepControl.wraps"];
	[row.cellConfigAtConfigure setObject:@10 forKey:@"stepControl.stepValue"];
	[row.cellConfigAtConfigure setObject:@10 forKey:@"stepControl.minimumValue"];
	[row.cellConfigAtConfigure setObject:@100 forKey:@"stepControl.maximumValue"];

#####滑块

XLForms 通过 UISlider 控件支持计数。

static NSString *const XLFormRowDescriptorTypeSlider = @"slider";

您可以根据自己的兴趣轻松调整滑块

	row = [XLFormRowDescriptor formRowDescriptorWithTag:kSlider rowType:XLFormRowDescriptorTypeSlider title:@"Slider"];
	row.value = @(30);
	[row.cellConfigAtConfigure setObject:@(100) forKey:@"slider.maximumValue"];
	[row.cellConfigAtConfigure setObject:@(10) forKey:@"slider.minimumValue"];
	[row.cellConfigAtConfigure setObject:@(4) forKey:@"steps"];

steps 设置为 @(0) 以禁用步骤功能。

#####信息

有时我们的应用程序需要显示无法编辑的数据。XLForm 通过 XLFormRowDescriptorTypeInfo 行类型为我们提供显示不可编辑信息的选项。使用示例可以在应用程序设置的“应用程序版本”部分中显示应用程序版本。

#####按钮

除了数据输入行、不可编辑行和选择器之外,XLForm还有一个按钮行 XLFormRowDescriptorTypeButton,允许我们在选择时执行任何操作。它可以通过块(闭包)、选择器、segue标识符、segue类或指定要显示的视图控制器来配置。视图控制器指定可以通过设置视图控制器类、视图控制器Storyboard Id或nib名称来完成。nib名称必须与视图控制器类名称匹配。

多值部分(插入、删除、重新排列行)

任何 XLFormSectionDescriptor 对象都可以设置以支持行插入、删除或重新排序。可以启用这些模式之一、组合或全部启用。
多值部分只是一个支持上述任一模式的部分。

多值 XLFormSectionDescriptor 最有趣的部分是它支持在 Rows 部分显示的所有类型以及自定义行。

Screenshot of Multivalued Section Example

如何设置多值部分

创建多值部分非常简单,只需使用以下便捷的 XLFormSectionDescriptor 初始化程序之一即可

+(id)formSectionWithTitle:(NSString *)title
		   sectionOptions:(XLFormSectionOptions)sectionOptions;
+(id)formSectionWithTitle:(NSString *)title
		   sectionOptions:(XLFormSectionOptions)sectionOptions
		sectionInsertMode:(XLFormSectionInsertMode)sectionInsertMode;

sectionOptions 是一个位枚举参数,用于选择多值部分类型(插入、删除、重新排序)。可用选项包括 XLFormSectionOptionCanInsertXLFormSectionOptionCanDeleteXLFormSectionOptionCanReorder。默认值为 XLFormSectionOptionNone

sectionInsertMode 用于选择插入模式的外观。XLform 默认提供两种插入模式:XLFormSectionInsertModeLastRowXLFormSectionInsertModeButton。默认值为 XLFormSectionInsertModeLastRow

让我们看看如何创建一个多值部分

XLFormDescriptor * form;
XLFormSectionDescriptor * section;
XLFormRowDescriptor * row;

NSArray * nameList = @[@"family", @"male", @"female", @"client"];

form = [XLFormDescriptor formDescriptorWithTitle:@"Multivalued examples"];

// Enable Insertion, Deletion, Reordering
section = [XLFormSectionDescriptor formSectionWithTitle:@"MultiValued TextField"
										  sectionOptions:XLFormSectionOptionCanReorder | XLFormSectionOptionCanInsert | XLFormSectionOptionCanDelete];
section.multivaluedTag = @"textFieldRow";
[form addFormSection:section];

for (NSString * tag in nameList) {
	// add a row to the section, each row will represent a name of the name list array.
	row = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeText title:nil];
	[[row cellConfig] setObject:@"Add a new tag" forKey:@"textField.placeholder"];
	row.value = [tag copy];
	[section addFormRow:row];
}
// add an empty row to the section.
row = [XLFormRowDescriptor formRowDescriptorWithTag:nil rowType:XLFormRowDescriptorTypeText title:nil];
[[row cellConfig] setObject:@"Add a new tag" forKey:@"textField.placeholder"];
[section addFormRow:row];

表单值

formValues

可以通过调用 -(NSDictionary *)formValues; 来获取所有表单值,这个方法可以来自 XLFormViewController 实例或 XLFormDescriptor 实例。

返回的 NSDictionary 是根据以下规则创建的

XLFormXLFormSectionDescriptor 中每个没有设置 multivaluedTag 值的 XLFormRowDescriptor 添加一个值。字典的键是 XLFormRowDescriptortag 属性值。

对于具有 multivaluedTag 值的每个部分,XLForm 添加一个以 NSArray 作为值的项目,数组中的每个值是该部分中的每行的值,键是 multivaluedTag

例如,如果我们有一个 multivaluedTag 属性值等于 tags 的部分和包含在行中的以下值:'family'、'male'、'female'、'client',生成的值将是 tags: ['family', 'male', 'female', 'client']

httpParameters

在某些情况下,我们需要的表单值可能与 XLFormRowDescriptor 实例的值不同。这通常是选择器行的情况,当我们需要将表单值发送到一些端点时,所选值可能是一个核心数据对象或其他对象。在这种情况下,XLForm 需要知道如何获取所选对象的价值及其描述。

当使用 -(NSDictionary *)httpParameters 方法时,XLForm 以下规则获取 XLFormRowDescriptor

  1. 如果对象是 NSStringNSNumberNSDate,则值是对象本身。
  2. 如果对象符合 XLFormOptionObject 协议,XLForm 从 formValue 方法获取值。
  3. 否则返回 nil。

multivaluedTagformValues 方法中的方式相同。

如何创建自定义行

要创建自定义单元格,需要创建从 XLFormBaseCell 扩展的 UITableViewCellXLFormBaseCell 符合 XLFormDescriptorCell 协议。

您可以实施 XLFormDescriptorCell 方法来更改单元格的行为。

@protocol XLFormDescriptorCell <NSObject>

@required

@property (nonatomic, weak) XLFormRowDescriptor * rowDescriptor;

// initialise all objects such as Arrays, UIControls etc...
-(void)configure;
// update cell when it about to be presented
-(void)update;

@optional

// height of the cell
+(CGFloat)formDescriptorCellHeightForRowDescriptor:(XLFormRowDescriptor *)rowDescriptor;
// called to check if cell can became first responder
-(BOOL)formDescriptorCellCanBecomeFirstResponder;
// called to ask cell to assign first responder to relevant UIView.
-(BOOL)formDescriptorCellBecomeFirstResponder;
// called when cell is selected
-(void)formDescriptorCellDidSelectedWithFormController:(XLFormViewController *)controller;
// http parameter name used for network request
-(NSString *)formDescriptorHttpParameterName;

// is invoked when cell becomes firstResponder, could be used for change how the cell looks like when it's the forst responder.
-(void)highlight;
// is invoked when cell resign firstResponder
-(void)unhighlight;


@end

创建自定义单元格后,需要让 XLForm 了解此单元格,通过将行定义添加到 cellClassesForRowDescriptorTypes 字典中来实现。

[[XLFormViewController cellClassesForRowDescriptorTypes] setObject:[MYCustomCellClass class] forKey:kMyAppCustomCellType];

或者,如果我们已使用 nib 文件来定义 XLBaseDescriptorCell

[[XLFormViewController cellClassesForRowDescriptorTypes] setObject:@"nibNameWithoutNibExtension" forKey:kMyAppCustomCellType];

这样做后,XLForm 将在使用 kMyAppCustomCellType 行类型时实例化适当的单元格类。

自定义选择器 - 带自定义选择器视图控制器的选择器行

基本上,基本的允许用户从推送视图控制器中选择一个或多个项目的选择器就足够我们使用了,但有时我们需要更多的灵活性,以便向用户提供更好的用户体验或执行不支持默认的操作。

假设您的应用程序用户需要选择地图坐标或需要从服务器端点选择值。我们如何轻松实现?

Screenshot of Map Custom Selector

定义上面的选择器行就像 ...

row = [XLFormRowDescriptor formRowDescriptorWithTag:kSelectorMap rowType:XLFormRowDescriptorTypeSelectorPush title:@"Coordinate"];
// set up the selector controller class
row.action.viewControllerClass = [MapViewController class];
// or
//row.action.viewControllerStoryboardId = @"MapViewControllerStoryboardId";
// or
//row.action.viewControllerNibName = @"MapViewControllerNibName";

// Set up a NSValueTransformer to convert CLLocation to NSString, it's used to show the select value description (text).  
row.valueTransformer = [CLLocationValueTrasformer class];
// Set up the default value
row.value = [[CLLocation alloc] initWithLatitude:-33 longitude:-56];

action.viewControllerClass 控制器类应符合 XLFormRowDescriptorViewController 协议。

在上面的示例中,MapViewController 符合 XLFormRowDescriptorViewController

@protocol XLFormRowDescriptorViewController <NSObject>

@required
@property (nonatomic) XLFormRowDescriptor * rowDescriptor;

@end

XLForm 使用属于选择器行的 XLFormRowDescriptor 实例设置了 rowDescriptor 属性。

开发者负责使用rowDescriptor值更新视图,以及从自定义选择器视图控制器中设置选中值。

注意:属性viewControllerClassviewControllerNibNameviewControllerStoryboardId是互斥的,由XLFormButtonCellXLFormSelectorCell使用。如果你创建了一个自定义单元格,那么你需要负责使用它们。

另一个例子

Screenshot of Dynamic Custom Selector

row = [XLFormRowDescriptor formRowDescriptorWithTag:kSelectorUser rowType:XLFormRowDescriptorTypeSelectorPush title:@"User"];
row.action.viewControllerClass = [UsersTableViewController class];

你可以在示例存储库中找到这些示例的详细信息,如下所示:Examples/Objective-C/Examples/Selectors/CustomSelectors/Examples/Objective-C/Examples/Selectors/DynamicSelector

动态表单 - 如何在运行时动态更改表单

XLFormDescriptor所做的任何更改都将反映在XLFormViewController的tableView上。这意味着当添加或删除某个部分或行时,XLForm将相应地动画化该部分或行。

我们不需要处理NSIndexPath或添加、删除UITableViewCell。随着时间的变化,特定的UITableViewCellNSIndexPath会发生改变,这使得很难追踪每个UITableViewCellNSIndexPath

XLForm的每个XLFormRowDescriptor行都有一个在其构造函数中设置的tag属性。XLFormDescriptor包含其他辅助器中的一个,用于从tag获取XLFormRowDescriptor
使用标签管理XLFormRowDescriptor要容易得多,标签应该是唯一的,并且在添加、修改或删除tableview时不会改变。

要注意的是,所有对UITableView的表单修改都必须使用描述符执行,而不是直接在UITableView上执行修改。

通常,你可能希望在值更改或行或部分添加或移除时更改表单。为此,你可以设置行或部分的disabledhidden属性。有关更多详细信息,请参阅根据其他行的值使行或部分不可见

为了与表单描述符的修改保持同步,你的XLFormViewController子类应该覆盖'XLFormViewController'的XLFormDescriptorDelegate方法。

注意:当覆盖此委托的方法时,始终要调用[super ...]方法。

@protocol XLFormDescriptorDelegate <NSObject>

@required

-(void)formSectionHasBeenRemoved:(XLFormSectionDescriptor *)formSection atIndex:(NSUInteger)index;
-(void)formSectionHasBeenAdded:(XLFormSectionDescriptor *)formSection atIndex:(NSUInteger)index;
-(void)formRowHasBeenAdded:(XLFormRowDescriptor *)formRow atIndexPath:(NSIndexPath *)indexPath;
-(void)formRowHasBeenRemoved:(XLFormRowDescriptor *)formRow atIndexPath:(NSIndexPath *)indexPath;
-(void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)formRow oldValue:(id)oldValue newValue:(id)newValue;
-(void)formRowDescriptorPredicateHasChanged:(XLFormRowDescriptor *)formRow
                                   oldValue:(id)oldValue
                                   newValue:(id)newValue
                              predicateType:(XLPredicateType)predicateType;

@end

例如,如果我们想根据另一行的值显示或隐藏一行

-(void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)rowDescriptor oldValue:(id)oldValue newValue:(id)newValue
{
	// super implmentation MUST be called
	[super formRowDescriptorValueHasChanged:rowDescriptor oldValue:oldValue newValue:newValue];
    if ([rowDescriptor.tag isEqualToString:@"alert"]){
        if ([[rowDescriptor.value valueData] isEqualToNumber:@(0)] == NO && [[oldValue valueData] isEqualToNumber:@(0)]){
            XLFormRowDescriptor * newRow = [rowDescriptor copy];
            [newRow setTag:@"secondAlert"];
            newRow.title = @"Second Alert";
            [self.form addFormRow:newRow afterRow:rowDescriptor];
        }
        else if ([[oldValue valueData] isEqualToNumber:@(0)] == NO && [[newValue valueData] isEqualToNumber:@(0)]){
            [self.form removeFormRowWithTag:@"secondAlert"];
        }
    }

根据其他行的值使行或部分不可见

###总结

XLForm允许你定义行之间的依赖关系,以便如果一行值更改,另一行的行为将自动更改。例如,你可能有一个询问用户是否有宠物的表单。如果答案为“是”,你可能想问他们的名字。
因此,你可以根据其他行的值使一行不可见并再次可见。部分也是如此。
看看下面的例子

Screenshot of hiding rows

当然,你也可以通过手动观察某些行的值,相应地删除行和添加行来完成此操作,但这将是一项大量工作,而这种工作已经完成。

###工作原理

为了使行和部分的显示和消失自动,每个描述符都有一个属性

@property id hidden;

此id对象通常是一个NSPredicate或包含BOOL的NSNumber。它可以使用任何它们之一或最终从其中创建NSPredicate的NSString设置。为了使这有效,字符串在语法上必须是正确的。

例如,你可以将以下字符串设置为一行(second),当前一行(first)包含“隐藏”值时使其消失。

second.hidden = [NSString stringWithFormat:@"$%@ contains[c] 'hide'", first];

这将在此处的'$'之后插入第一个标签,当然您也可以手动完成。当进行谓词评估时,每个标签变量都会被对应的行描述符替换。

当参数是NSString时,除非标签后面跟'.isHidden'或'.isDisabled',否则每个标签都会附加'.value'。这意味着一行(或一个部分)可能取决于另一行的valuehiddendisabled属性。当直接使用NSPredicate设置属性时,其formatString不会被更改(因此如果您想引用其值,则您必须在每个变量之后附加'.value')。设置NSString是最简单的方法,但对于某些复杂的谓词可能不会工作,因此对于这些谓词,您应该直接设置一个NSPredicate。

您还可以使用布尔对象设置此属性,这意味着除非手动设置,否则属性的值不会改变。

要获取评估后的布尔值,应调用isHidden方法。每次调用它时,不会重新评估谓词,但仅当它依赖的行的值(或隐藏/禁用状态)发生变化时才评估。当发生这种情况并且返回值发生变化时,它将自动反映这种变化,因此不需要调用其他任何方法。

这里有一个更复杂的例子

Screenshot of hiding rows

禁用行(设置为只读模式)

可以禁用行,这样用户就无法更改它们。默认情况下,禁用的行的文本颜色为灰色。要禁用行,只需设置其禁用属性即可。

@property id disabled;

此属性期望一个包含BOOL、NSString或NSPredicate的NSNumber。布尔值将静态地禁用(或启用)行。其他两个属性与上面章节中解释的隐藏属性工作原理相同。这意味着行可以根据其他行的值禁用或启用。当设置NSString时,将根据字符串作为格式字符串生成NSPredicate,因此必须对此目的保持一致。

与隐藏属性的不同之处在于,检查行禁用状态不会自动将该值反映在表单上。因此,应调用XLFormViewController的updateFormRow方法。

验证

我们可以使用XLForm验证支持来验证表单数据。

每个XLFormRowDescriptor实例都包含一个验证器列表。我们可以通过这些方法添加验证器、移除验证器并验证特定行。

-(void)addValidator:(id<XLFormValidatorProtocol>)validator;
-(void)removeValidator:(id<XLFormValidatorProtocol>)validator;
-(XLFormValidationStatus *)doValidation;

我们可以定义自己的自定义验证器,只需定义一个符合XLFormValidatorProtocol的对象。

@protocol XLFormValidatorProtocol <NSObject>

@required

-(XLFormValidationStatus *)isValid:(XLFormRowDescriptor *)row;

@end

XLFormRegexValidator是我们可以创建的验证器的一个例子。

一个非常常见的验证是确保值不为空或nil。XLFom公开了required XLFormRowDescriptor属性来指定必填行。

要获取所有行的验证错误,我们可以调用以下XLFormViewController方法

-(NSArray *)formValidationErrors;

行的额外配置

XLFormRowDescriptor允许我们配置UITableViewCell的通用方面,例如:rowTypelabelvalue(默认值)、单元格是否requiredhiddendisabled等。

您可能想设置单元格的另一些属性。要设置其他属性,XL使用键值编码允许开发者通过keyPath设置单元格属性。

您只需要将属性添加到cellConfigcellConfigAtConfigure字典属性中的XLFormRowDescriptor
cellConfigcellConfigAtConfigure 这两个属性之间的主要区别在于属性设置的时机。cellConfig 属性是在单元格准备显示的每次都进行设置的。cellConfigAtConfigure 则是在单元格的 init 方法调用之后立即设置,并且只设置一次。

从版本 3.3.0 开始,您还可以使用 cellConfigForSelector 来配置在选择行显示时 XLFormOptionsViewController 的单元格外观。

例如,如果您要设置占位符,可以这样做

row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
[row.cellConfigAtConfigure setObject:@"Title" forKey:@"textField.placeholder"];
[section addFormRow:row];

让我们来看看如何更改单元格标签的颜色

Objective C

row = [XLFormRowDescriptor formRowDescriptorWithTag:@"title" rowType:XLFormRowDescriptorTypeText];
[row.cellConfig setObject:[UIColor redColor] forKey:@"textLabel.textColor"];
[section addFormRow:row];

Swift

row = XLFormRowDescriptor(tag: "title", rowType: XLFormRowDescriptorTypeText, title: "title")
row.cellConfig.setObject(UIColor.blackColor(), forKey: "backgroundColor")
row.cellConfig.setObject(UIColor.whiteColor(), forKey: "textLabel.textColor")
section.addFormRow(row)

常见问题解答

如何在表单出现时分配第一个响应者

将表单显示时设置为第一个响应者是非常简单的,只需要将属性 assignFirstResponderOnShow 设置为 YES。默认情况下,属性的值是 NO

@property (nonatomic) BOOL assignFirstResponderOnShow;

如何为行设置值。

您应该设置相关的 XLFormRowDescriptor 实例的 value 属性。

@property (nonatomic) id value;

您可能会注意到 value 属性的类型是 id,并且您负责设置具有正确类型的值。例如,您应该将 NSString 类型的值设置到 XLFormRowDescriptorTypeTextXLFormRowDescriptor 实例中。

如果行已经显示,则可能需要更新单元格以查看界面更改。
XLFormViewController 提供的 -(void)reloadFormRow:(XLFormRowDescriptor *)formRow 方法可用于执行此操作。

如何设置行的默认值。

您应该像如何设置行值中描述的那样执行。

如何设置选择行的选项。

XLForm 有几种选择行类型。几乎所有这些类型都需要知道将要选择的值。对于特定的 XLFormRowDescriptor 实例,您可以设置这些值,将 NSArray 实例设置到 selectorOptions 属性中。

@property NSArray * selectorOptions;

如何获取表单值

如果您想获取原始表单值,应该调用 XLFormDescriptorformValues 方法。这样做您将获得一个包含所有表单值的字典。
每行的 tag 属性值用作字典键。只有非空 tag 值的 XLFormROwDescriptor 值被添加到字典中。

您可能对表单值感兴趣,将其用作端点参数。在这种情况下,httpParameters 可能很有用。

如果需要其他不同的内容,您可以遍历每一行...

Objective C

 NSMutableDictionary * result = [NSMutableDictionary dictionary];
 for (XLFormSectionDescriptor * section in self.form.formSections) {
     if (!section.isMultivaluedSection){
         for (XLFormRowDescriptor * row in section.formRows) {
             if (row.tag && ![row.tag isEqualToString:@""]){
                 [result setObject:(row.value ?: [NSNull null]) forKey:row.tag];
             }
         }
     }
     else{
         NSMutableArray * multiValuedValuesArray = [NSMutableArray new];
         for (XLFormRowDescriptor * row in section.formRows) {
             if (row.value){
                 [multiValuedValuesArray addObject:row.value];
             }
         }
         [result setObject:multiValuedValuesArray forKey:section.multivaluedTag];
     }
 }
 return result;

Swift

var results = [String:String]()
if let fullName = form.formRowWithTag(tag.fullName).value as? String {
    results[tag.fullName] = fullName
}

如何更改 UITextField 长度

您可以使用 cellConfigAtConfigure 字典属性更改 UITextField 的长度。此值相对于表格视图单元格的百分比。

Objective C

[row.cellConfigAtConfigure setObject:[NSNumber numberWithFloat:0.7] forKey:XLFormTextFieldLengthPercentage];

Swift

row.cellConfigAtConfigure.setObject(0.7, forKey:XLFormTextFieldLengthPercentage)

**注意:**当使用 XLFormRowDescriptorTypeTextView 时,这也可以应用于 UITextView;只需为键 XLFormTextViewLengthPercentage 设置您的百分比。

如何更改 UITableViewCell 字体

您可以使用 cellConfig 字典属性更改字体或表视图单元格的任何其他属性。XLForm 将在表格视图单元格即将显示时设置 cellConfig 字典的值。

Objective C

[row.cellConfig setObject:[UIColor greenColor] forKey:@"textLabel.textColor"];
[row.cellConfig setObject:[UIFont fontWithName:FONT_LATO_REGULAR size:12.0] forKey:@"textLabel.font"];
[row.cellConfig setObject:[UIFont fontWithName:FONT_LATO_REGULAR size:12.0] forKey:@"detailTextLabel.font"];

Swift

row.cellConfig.setObject(UIColor.whiteColor(), forKey: "self.tintColor")
row.cellConfig.setObject(UIFont(name: "AppleSDGothicNeo-Regular", size: 17)!, forKey: "textLabel.font")
row.cellConfig.setObject(UIColor.whiteColor(), forKey: "textField.textColor")
row.cellConfig.setObject(UIFont(name: "AppleSDGothicNeo-Regular", size: 17)!, forKey: "textField.font")

有关更多详细信息,请参阅UICustomizationFormViewController.m 示例。

####如何为日期单元格设置最小/最大值?

每个 XLFormDateCell 都有一个 minimumDatemaximumDate 属性。要将日期行设置为未来三天内的值,您会这样做:

Objective C

[row.cellConfigAtConfigure setObject:[NSDate new] forKey:@"minimumDate"];
[row.cellConfigAtConfigure setObject:[NSDate dateWithTimeIntervalSinceNow:(60*60*24*3)] forKey:@"maximumDate"];

Swift

row.cellConfig.setObject(NSDate(), forKey: "maximumDate")

如何禁用整个表单(只读模式)。

可以使用 disable XLFormDescriptor 属性来禁用整个表单。为了使显示的单元格生效,我们应该重新加载可见单元格([self.tableView reloadData])。
当在设置了 disable 属性为 YES 的表单后添加任何其他行,这些行将自动反映禁用模式(无需重新加载表视图)。

如何在其他行的值变化时隐藏一行或一个部分。

要隐藏一行或一个部分,你应该设置其隐藏属性。最简单的方法是将一个 NSString 设置给它。比如说,你想隐藏一个部分,如果其前一行(是一个布尔开关)设置为 1(或 YES),你可以这样做:

section.hidden = [NSString stringWithFormat:@"$%@ == 1", previousRow];

就是这样!

我需要做什么才能从版本 2.2.0 迁移到 3.0.0?

与旧版本不兼容的唯一问题是,XLFormRowDescriptordisabled 属性现在是一个 id。所以,你只需在其值的设置前添加一个 @,如下所示:

row.disabled = @YES; // before: row.disabled = YES;

如何更改输入辅助视图(导航视图)

重写 inputAccessoryViewForRowDescriptor:XLFormViewController 方法。
如果你想要完全禁用它,你可以返回 nil。但你也可以在这里自定义其整个外观。

- (UIView *)inputAccessoryViewForRowDescriptor:(XLFormRowDescriptor *)rowDescriptor
{
      return nil; //will hide it completely
      // You can use the rowDescriptor parameter to hide/customize the accessory view for a particular rowDescriptor type.
}

如何设置一个推送视图控制器?

将被推送的视图控制器必须遵守 XLFormRowDescriptorViewController 协议,该协议包含以下属性

@property (nonatomic) XLFormRowDescriptor * rowDescriptor;

此行描述符引用前一个视图控制器中选中的行,将在转换到新控制器之前设置,以便在示例中,例如在它的 viewDidLoad 方法中可访问。这就是该视图控制器应该设置的地方。

如何更改特定单元格的默认外观

最好的方法是扩展该单元格的类并重写其更新和/或配置方法。为了使这起作用,你还应该更新 XLFormViewController 的子类中的 cellClassesForRowDescriptorTypes 字典,将您的自定义类而不是要更改的单元格的类设置为您的自定义类。

如何更改单元格的 returnKeyType

要更改单元格的 returnKeyType,您可以设置 returnKeyTypenextReturnKeyType 属性。前者在没有导航启用或在该行之后没有行时使用。在另一种情况下,使用后者。
如果您创建了一个自定义单元格并想使用它们,您应该遵守 XLFormReturnKeyProtocol 协议。
以下是如何设置它们的方法

[row.cellConfigAtConfigure setObject:@(UIReturnKeyGo) forKey:@"nextReturnKeyType"];

如何更改单个单元格的高度

如果您想更改某类所有单元格的高度,则应子类化该单元格并重写类方法 formDescriptorCellHeightForRowDescriptor
如果您想更改单个单元格的高度,则可以将该高度设置为 XLFormRowDescripto 的 height 属性,如下所示:

XLFormRowDescriptor* row = ... 
row.height = 55;

如何更改选择视图控制器(XLFormOptionsViewController)中单元格的外观

要更改 XLFormOptionsViewController 中单元格的外观,您可以在行描述符中使用 cellConfigForSelector 属性。
示例

[row.cellConfigForSelector setObject:[UIColor redColor] forKey:@"textLabel.textColor"];

如何限制 XLFormTextFieldCell 或 XLFormTextViewCell 的字符数

您可以使用 textFieldMaxNumberOfCharacterstextViewMaxNumberOfCharacters 来实现这一点。

[row.cellConfigAtConfigure setObject:@(20) forKey:@"textViewMaxNumberOfCharacters"];

安装

如何使用 master 分支

通常 master 分支包含最新的功能和最新修正。但同时,这些功能尚未完全测试,master 上的更改可能随时发生。出于上述原因,我强烈建议您分叉存储库并自行管理从 master 上的更新,按需进行适当的拉取。

要使用 xmartlabs 的 master 分支.....

pod 'XLForm', :git => 'https://github.com/xmartlabs/XLForm.git'

如果您愿意,可以将存储库 URL 替换为您的分叉版本 URL。

如何在 Swift 文件中使用 XLForm

如果您已经通过 CocoaPods 安装了 XLForm 并在 Podfile 中设置了 use_frameworks!,则您可以在任何 Swift 文件中添加 import XLForm

如果您正在使用 CocoaPods 但未在 Podfile 中设置 use_frameworks!,请将 #import <XLForm/XLForm.h> 添加到您的桥接头文件中。

有关创建和配置桥接头文件的更多详细信息,请访问 将 Objective-C 导入 Swift

使用 git 子模块

  • 从您的项目根 git 文件夹执行以下命令,将 XLForm 作为 git 子模块 克隆。
$ git submodule add https://github.com/xmartlabs/XLForm.git
  • 打开由前一个 git 子模块命令创建的 XLForm 文件夹,将其拖放到您的应用程序的 Xcode 项目的项目导航器中。

  • 在项目导航器中选择 XLForm.xcodeproj,并验证部署目标与您的应用程序部署目标匹配。

  • 在 Xcode 导航中选择您的项目,然后在侧边栏中选择您的应用程序目标。接下来选择“通用”选项卡,然后在“已嵌入的二进制文件”部分下点击 + 按钮。

  • 选择 XLForm.framework,我们会完成!

要求

  • ARC
  • iOS 7.0 及以上版本
  • XCode 6.3+

版本说明

查看 CHANGELOG

作者

Martin Barreto (@mtnBarreto)

联系

有任何建议或问题?请创建一个 Github 问题或与我们联系。

xmartlabs.com (@xmartlabs)