Repository: MaricleZhang/LMForm
Branch: master
Commit: 01001a8594c4
Files: 63
Total size: 104.2 KB
Directory structure:
gitextract_2l3nopf2/
├── .gitignore
├── .travis.yml
├── Example/
│ ├── .gitignore
│ ├── LMForm/
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Images.xcassets/
│ │ │ └── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── LMAppDelegate.h
│ │ ├── LMAppDelegate.m
│ │ ├── LMCustomCell.h
│ │ ├── LMCustomCell.m
│ │ ├── LMForm-Info.plist
│ │ ├── LMForm-Prefix.pch
│ │ ├── LMViewController.h
│ │ ├── LMViewController.m
│ │ ├── LMWindowHud.h
│ │ ├── LMWindowHud.m
│ │ ├── en.lproj/
│ │ │ └── InfoPlist.strings
│ │ └── main.m
│ ├── LMForm.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── LMForm-Example.xcscheme
│ └── Podfile
├── LICENSE
├── LMForm/
│ ├── Assets/
│ │ └── .gitkeep
│ └── Classes/
│ ├── .gitkeep
│ ├── Category/
│ │ ├── UIImage+Bundle.h
│ │ ├── UIImage+Bundle.m
│ │ ├── UITextField+LimitLength.h
│ │ ├── UITextField+LimitLength.m
│ │ ├── UITextView+AutoHeight.h
│ │ ├── UITextView+AutoHeight.m
│ │ ├── UIView+Extension.h
│ │ └── UIView+Extension.m
│ ├── Cell/
│ │ ├── LMAddressCell.h
│ │ ├── LMAddressCell.m
│ │ ├── LMDateCell.h
│ │ ├── LMDateCell.m
│ │ ├── LMFormAddressInputCell.h
│ │ ├── LMFormAddressInputCell.m
│ │ ├── LMFormCell.h
│ │ ├── LMFormCell.m
│ │ ├── LMFormInputCell.h
│ │ ├── LMFormInputCell.m
│ │ ├── LMFormSelectorCell.h
│ │ ├── LMFormSelectorCell.m
│ │ ├── LMFormTextViewCell.h
│ │ └── LMFormTextViewCell.m
│ ├── LMForm.h
│ ├── LMFormCellProtocol.h
│ ├── LMMarco.h
│ ├── Manager/
│ │ ├── LMFormTypeManager.h
│ │ └── LMFormTypeManager.m
│ ├── Model/
│ │ ├── LMFormModel.h
│ │ ├── LMFormModel.m
│ │ ├── LMFormValidator.h
│ │ └── LMFormValidator.m
│ └── View/
│ ├── LMDefaultPickerView.h
│ ├── LMDefaultPickerView.m
│ ├── LMFormTableView.h
│ ├── LMFormTableView.m
│ ├── LMPopupView.h
│ └── LMPopupView.m
├── LMForm.podspec
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# OS X
.DS_Store
# Xcode
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
*.xccheckout
profile
*.moved-aside
DerivedData
*.hmap
*.ipa
# Bundler
.bundle
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
# Note: if you ignore the Pods directory, make sure to uncomment
# `pod install` in .travis.yml
#
# Pods/
project.xcworkspace
LMForm.xcworkspace
/Pods
================================================
FILE: .travis.yml
================================================
# references:
# * https://www.objc.io/issues/6-build-tools/travis-ci/
# * https://github.com/supermarin/xcpretty#usage
osx_image: xcode7.3
language: objective-c
# cache: cocoapods
# podfile: Example/Podfile
# before_install:
# - gem install cocoapods # Since Travis is not always on latest version
# - pod install --project-directory=Example
script:
- set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/LMForm.xcworkspace -scheme LMForm-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty
- pod lib lint
================================================
FILE: Example/.gitignore
================================================
# OS X
.DS_Store
# Xcode
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
*.xccheckout
profile
*.moved-aside
DerivedData
*.hmap
*.ipa
# Bundler
.bundle
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
# Note: if you ignore the Pods directory, make sure to uncomment
# `pod install` in .travis.yml
#
# Pods/
project.xcworkspace
LMForm.xcworkspace
/Pods
================================================
FILE: Example/LMForm/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
================================================
FILE: Example/LMForm/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="whP-gf-Uak">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="wQg-tq-qST">
<objects>
<viewController id="whP-gf-Uak" customClass="LMViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="uEw-UM-LJ8"/>
<viewControllerLayoutGuide type="bottom" id="Mvr-aV-6Um"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="TpU-gO-2f1">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="tc2-Qw-aMS" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="305" y="433"/>
</scene>
</scenes>
</document>
================================================
FILE: Example/LMForm/Images.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Example/LMForm/LMAppDelegate.h
================================================
//
// LMAppDelegate.h
// LMForm
//
// Created by MaricleZhang on 05/23/2019.
// Copyright (c) 2019 MaricleZhang. All rights reserved.
//
@import UIKit;
@interface LMAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
================================================
FILE: Example/LMForm/LMAppDelegate.m
================================================
//
// LMAppDelegate.m
// LMForm
//
// Created by MaricleZhang on 05/23/2019.
// Copyright (c) 2019 MaricleZhang. All rights reserved.
//
#import "LMAppDelegate.h"
#import "LMViewController.h"
@implementation LMAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
LMViewController *vc = [[LMViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
self.window.rootViewController = nav;
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end
================================================
FILE: Example/LMForm/LMCustomCell.h
================================================
//
// LMCustomCell.h
// LMForm_Example
//
// Created by Zhang on 2019/5/27.
// Copyright © 2019 MaricleZhang. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "LMFormCellProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface LMCustomCell : UITableViewCell<LMFormCellProtocol>
@end
NS_ASSUME_NONNULL_END
================================================
FILE: Example/LMForm/LMCustomCell.m
================================================
//
// LMCustomCell.m
// LMForm_Example
//
// Created by Zhang on 2019/5/27.
// Copyright © 2019 MaricleZhang. All rights reserved.
//
#import "LMCustomCell.h"
#import "LMFormModel.h"
@interface LMCustomCell ()
@property (nonatomic, strong) UIImageView *imgView;
@end
@implementation LMCustomCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])
{
[self createUI];
}
return self;
}
- (void)createUI
{
[self.contentView addSubview:self.imgView];
}
- (void)configModel:(LMFormModel *)model
{
self.textLabel.text = model.title;
}
- (UIImageView *)imgView
{
if (!_imgView)
{
_imgView = [[UIImageView alloc] init];
_imgView.frame = CGRectMake(150, 0, 200, 50);
_imgView.backgroundColor = [UIColor redColor];
}
return _imgView;
}
@end
================================================
FILE: Example/LMForm/LMForm-Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>zh_CN</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
================================================
FILE: Example/LMForm/LMForm-Prefix.pch
================================================
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#import <Availability.h>
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
#ifdef __OBJC__
@import UIKit;
@import Foundation;
#endif
================================================
FILE: Example/LMForm/LMViewController.h
================================================
//
// LMViewController.h
// LMForm
//
// Created by MaricleZhang on 05/23/2019.
// Copyright (c) 2019 MaricleZhang. All rights reserved.
//
@import UIKit;
@interface LMViewController : UIViewController
@end
================================================
FILE: Example/LMForm/LMViewController.m
================================================
//
// LMViewController.m
// LMForm
//
// Created by MaricleZhang on 05/23/2019.
// Copyright (c) 2019 MaricleZhang. All rights reserved.
//
#import "LMViewController.h"
#import "LMForm.h"
#import "LMCustomCell.h"
#import "LMWindowHud.h"
#define kCustomCell @"custom"
@interface LMViewController ()
@property (nonatomic, strong) LMFormTableView *tableView;
@property (nonatomic, copy) NSMutableArray<LMFormModel *> *dataArray;
@end
@implementation LMViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self createUI];
[self.dataArray addObject:[self loadText]];
[self.dataArray addObject:[self loadInput]];
[self.dataArray addObject:[self loadSelector]];
[self.dataArray addObject:[self loadDateSelector]];
[self.dataArray addObject:[self loadAdressInput]];
[self.dataArray addObject:[self loadTextView]];
// [self.dataArray addObject:[self loadCustom]];
self.tableView.dataArray = self.dataArray;
}
- (void)createUI
{
self.navigationItem.title = @"Demo";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"保存" style:UIBarButtonItemStylePlain target:self action:@selector(tapSaveAction)];
[self.view addSubview:self.tableView];
self.tableView.frame = self.view.frame;
}
// 不可编辑文本
- (LMFormModel *)loadText
{
LMFormModel *model = [LMFormModel new];
model.formType = kFormTypeText;
model.title = @"姓名";
model.key = @"name";
model.value = @"Tom";
model.height = LM_XX_6(50);
return model;
}
// 输入框
- (LMFormModel *)loadInput
{
LMFormModel *model = [LMFormModel new];
model.formType = kFormTypeInput;
model.title = @"手机号";
model.key = @"mobile";
model.value = @"";
model.placeholder = @"请输入手机号";
model.height = LM_XX_6(50);
model.message = @"请输入正确的手机号";
model.limitLength = 11;
model.validateBlock = ^BOOL(LMFormModel * _Nullable model) {
if (![LMFormValidator isMobile:model.value])
{
[LMWindowHud showHud:model.message];
return NO;
}
return YES;
};
return model;
}
// 选择器
- (LMFormModel *)loadSelector
{
LMFormModel *model = [LMFormModel new];
model.formType = kFormTypeSelector;
model.title = @"性别";
model.key = @"gender";
model.value = @"";
model.placeholder = @"请选择性别";
model.height = LM_XX_6(50);
model.message = @"请选择性别";
model.selectList = @[@"男",@"女"];
model.validateBlock = ^BOOL(LMFormModel * _Nullable model) {
if ([LMFormValidator isEmptyValue:model.value])
{
[LMWindowHud showHud:model.message];
return NO;
}
return YES;
};
model.valueDidChangedBlock = ^(NSString * _Nullable vlaue) {
NSLog(@"vlaue:%@",vlaue);
};
return model;
}
// 日期选择器
- (LMFormModel *)loadDateSelector
{
LMFormModel *model = [LMFormModel new];
model.formType = kFormTypeDate;
model.title = @"出生日期";
model.key = @"birth";
model.value = @"";
model.placeholder = @"请选择出生日期";
model.height = LM_XX_6(50);
model.message = @"请选择出生日期";
model.datePickerMode = UIDatePickerModeDate;
model.dateFormat = @"yyyy-MM-dd";
return model;
}
// 地址输入框
- (LMFormModel *)loadAdressInput
{
LMFormModel *model = [LMFormModel new];
model.formType = kFormTypeAddressInput;
model.title = @"详细地址";
model.key = @"detailAdress";
model.value = @"";
model.placeholder = @"请输入详细地址";
model.height = LM_XX_6(68);
model.message = @"详细地址不得少于5位";
model.validateBlock = ^BOOL(LMFormModel * _Nullable model) {
if (model.value.length < 5)
{
[LMWindowHud showHud:model.message];
return NO;
}
return YES;
};
return model;
}
// textView cell高度动态变化
- (LMFormModel *)loadTextView
{
LMFormModel *model = [LMFormModel new];
model.formType = kFormTypeTextView;
model.title = @"高度可变输入框";
model.key = @"detailAdress";
model.value = @"detailAdressdetailAdressdetailAdressdetailAdressdetailAdressdetailAdressdetailAdressdetailAdressdetailAdressdetailAdressdetailAdressdetailAdressdetailAdress";
model.placeholder = @"请输入详细地址";
model.height = LM_XX_6(68);
model.message = @"详细地址不得少于5位";
model.validateBlock = ^BOOL(LMFormModel * _Nullable model) {
if (model.value.length < 5)
{
[LMWindowHud showHud:model.message];
return NO;
}
return YES;
};
return model;
}
// 自定义cell
- (LMFormModel *)loadCustom
{
[[LMFormTypeManager manager] registerCellClass:[LMCustomCell class] forKey:kCustomCell];
LMFormModel *model = [LMFormModel new];
model.formType = kCustomCell;
model.title = @"custom cell";
return model;
}
#pragma mark - Responce
- (void)tapSaveAction
{
[self.dataArray enumerateObjectsUsingBlock:^(LMFormModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%@:%@",obj.title,obj.value);
}];
// 提交数据之前,对数据校验。如果不写则不校验。
BOOL isValidateSuccess = [LMFormValidator validateDataArray:self.dataArray];
if (isValidateSuccess)
{
NSLog(@"数据校验完成+++");
}
else
{
NSLog(@"数据校验失败---");
}
}
#pragma mark - Setter/Getter
- (LMFormTableView *)tableView
{
if (!_tableView)
{
_tableView = [[LMFormTableView alloc] init];
}
return _tableView;
}
- (NSMutableArray<LMFormModel *> *)dataArray
{
if (!_dataArray)
{
_dataArray = [NSMutableArray array];
}
return _dataArray;
}
@end
================================================
FILE: Example/LMForm/LMWindowHud.h
================================================
//
// LMWindowHud.h
// LMForm
//
// Created by Zhang on 2019/5/20.
// Copyright © 2019 Maricle. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface LMWindowHud : NSObject
+ (void)showHud:(NSString *)text;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: Example/LMForm/LMWindowHud.m
================================================
//
// LMWindowHud.m
// LMForm
//
// Created by Zhang on 2019/5/20.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMWindowHud.h"
#import "UIView+Extension.h"
UILabel *hudLabel;
@implementation LMWindowHud
+ (void)showHud:(NSString *)text
{
if (hudLabel) return;
hudLabel = [[UILabel alloc] init];
hudLabel.backgroundColor = [UIColor colorWithWhite:0.3 alpha:0.7];
hudLabel.textAlignment = NSTextAlignmentCenter;
hudLabel.textColor = [UIColor whiteColor];
hudLabel.text = text;
hudLabel.font = [UIFont systemFontOfSize:14];
hudLabel.layer.cornerRadius = 3;
hudLabel.layer.masksToBounds = YES;
UIWindow *window = [UIApplication sharedApplication].keyWindow;
[window addSubview:hudLabel];
[hudLabel sizeToFit];
hudLabel.width += 20;
hudLabel.height += 10;
hudLabel.center = window.center;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[hudLabel removeFromSuperview];
hudLabel = nil;
});
}
@end
================================================
FILE: Example/LMForm/en.lproj/InfoPlist.strings
================================================
/* Localized versions of Info.plist keys */
================================================
FILE: Example/LMForm/main.m
================================================
//
// main.m
// LMForm
//
// Created by MaricleZhang on 05/23/2019.
// Copyright (c) 2019 MaricleZhang. All rights reserved.
//
@import UIKit;
#import "LMAppDelegate.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([LMAppDelegate class]));
}
}
================================================
FILE: Example/LMForm.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
0290F680229BBB5D0049BB3D /* LMCustomCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0290F67F229BBB5D0049BB3D /* LMCustomCell.m */; };
0290F683229BD1860049BB3D /* LMWindowHud.m in Sources */ = {isa = PBXBuildFile; fileRef = 0290F681229BD1860049BB3D /* LMWindowHud.m */; };
6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; };
6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; };
6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; };
6003F59E195388D20070C39A /* LMAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* LMAppDelegate.m */; };
6003F5A7195388D20070C39A /* LMViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* LMViewController.m */; };
6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; };
71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; };
873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; };
D4E5E60C734DBC25F33FFAA1 /* libPods-LMForm_Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 130B7CB0C780E63591D52682 /* libPods-LMForm_Example.a */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
0290F67E229BBB5D0049BB3D /* LMCustomCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMCustomCell.h; sourceTree = "<group>"; };
0290F67F229BBB5D0049BB3D /* LMCustomCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMCustomCell.m; sourceTree = "<group>"; };
0290F681229BD1860049BB3D /* LMWindowHud.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LMWindowHud.m; sourceTree = "<group>"; };
0290F682229BD1860049BB3D /* LMWindowHud.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LMWindowHud.h; sourceTree = "<group>"; };
0D4C6199DD09C3E38CB26ECB /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
130B7CB0C780E63591D52682 /* libPods-LMForm_Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LMForm_Example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
211717D807EA485D82D97973 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
2D0D33DCB0410227D4AEBD23 /* Pods-LMForm_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LMForm_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-LMForm_Example/Pods-LMForm_Example.release.xcconfig"; sourceTree = "<group>"; };
4FC47A9DA5415BF6A8BC5E15 /* Pods-LMForm_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LMForm_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LMForm_Example/Pods-LMForm_Example.debug.xcconfig"; sourceTree = "<group>"; };
5F26DB12D707D492BA7A2BFD /* Pods-LMForm_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LMForm_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-LMForm_Tests/Pods-LMForm_Tests.release.xcconfig"; sourceTree = "<group>"; };
6003F58A195388D20070C39A /* LMForm_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LMForm_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
6003F595195388D20070C39A /* LMForm-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "LMForm-Info.plist"; sourceTree = "<group>"; };
6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
6003F59B195388D20070C39A /* LMForm-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LMForm-Prefix.pch"; sourceTree = "<group>"; };
6003F59C195388D20070C39A /* LMAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMAppDelegate.h; sourceTree = "<group>"; };
6003F59D195388D20070C39A /* LMAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMAppDelegate.m; sourceTree = "<group>"; };
6003F5A5195388D20070C39A /* LMViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMViewController.h; sourceTree = "<group>"; };
6003F5A6195388D20070C39A /* LMViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMViewController.m; sourceTree = "<group>"; };
6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
7A2D7B9635BBC172BC97DCD7 /* Pods_LMForm_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LMForm_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
E4CE1BCDB9DB2F87947F238C /* LMForm.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LMForm.podspec; path = ../LMForm.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
F1BC81E44B7356D0939A2A1C /* Pods-LMForm_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LMForm_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LMForm_Tests/Pods-LMForm_Tests.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
6003F587195388D20070C39A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */,
6003F592195388D20070C39A /* UIKit.framework in Frameworks */,
6003F58E195388D20070C39A /* Foundation.framework in Frameworks */,
D4E5E60C734DBC25F33FFAA1 /* libPods-LMForm_Example.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0290F67D229BB9F80049BB3D /* CustomCell */ = {
isa = PBXGroup;
children = (
0290F67E229BBB5D0049BB3D /* LMCustomCell.h */,
0290F67F229BBB5D0049BB3D /* LMCustomCell.m */,
);
name = CustomCell;
sourceTree = "<group>";
};
0290F684229BD18F0049BB3D /* Hud */ = {
isa = PBXGroup;
children = (
0290F682229BD1860049BB3D /* LMWindowHud.h */,
0290F681229BD1860049BB3D /* LMWindowHud.m */,
);
name = Hud;
sourceTree = "<group>";
};
073ADEEAAC91DB8FFCBB5F32 /* Pods */ = {
isa = PBXGroup;
children = (
4FC47A9DA5415BF6A8BC5E15 /* Pods-LMForm_Example.debug.xcconfig */,
2D0D33DCB0410227D4AEBD23 /* Pods-LMForm_Example.release.xcconfig */,
F1BC81E44B7356D0939A2A1C /* Pods-LMForm_Tests.debug.xcconfig */,
5F26DB12D707D492BA7A2BFD /* Pods-LMForm_Tests.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
6003F581195388D10070C39A = {
isa = PBXGroup;
children = (
60FF7A9C1954A5C5007DD14C /* Podspec Metadata */,
6003F593195388D20070C39A /* Example for LMForm */,
6003F58C195388D20070C39A /* Frameworks */,
6003F58B195388D20070C39A /* Products */,
073ADEEAAC91DB8FFCBB5F32 /* Pods */,
);
sourceTree = "<group>";
};
6003F58B195388D20070C39A /* Products */ = {
isa = PBXGroup;
children = (
6003F58A195388D20070C39A /* LMForm_Example.app */,
);
name = Products;
sourceTree = "<group>";
};
6003F58C195388D20070C39A /* Frameworks */ = {
isa = PBXGroup;
children = (
6003F58D195388D20070C39A /* Foundation.framework */,
6003F58F195388D20070C39A /* CoreGraphics.framework */,
6003F591195388D20070C39A /* UIKit.framework */,
6003F5AF195388D20070C39A /* XCTest.framework */,
7A2D7B9635BBC172BC97DCD7 /* Pods_LMForm_Tests.framework */,
130B7CB0C780E63591D52682 /* libPods-LMForm_Example.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
6003F593195388D20070C39A /* Example for LMForm */ = {
isa = PBXGroup;
children = (
6003F59C195388D20070C39A /* LMAppDelegate.h */,
6003F59D195388D20070C39A /* LMAppDelegate.m */,
873B8AEA1B1F5CCA007FD442 /* Main.storyboard */,
6003F5A5195388D20070C39A /* LMViewController.h */,
6003F5A6195388D20070C39A /* LMViewController.m */,
0290F684229BD18F0049BB3D /* Hud */,
0290F67D229BB9F80049BB3D /* CustomCell */,
71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */,
6003F5A8195388D20070C39A /* Images.xcassets */,
6003F594195388D20070C39A /* Supporting Files */,
);
name = "Example for LMForm";
path = LMForm;
sourceTree = "<group>";
};
6003F594195388D20070C39A /* Supporting Files */ = {
isa = PBXGroup;
children = (
6003F595195388D20070C39A /* LMForm-Info.plist */,
6003F596195388D20070C39A /* InfoPlist.strings */,
6003F599195388D20070C39A /* main.m */,
6003F59B195388D20070C39A /* LMForm-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = {
isa = PBXGroup;
children = (
E4CE1BCDB9DB2F87947F238C /* LMForm.podspec */,
211717D807EA485D82D97973 /* README.md */,
0D4C6199DD09C3E38CB26ECB /* LICENSE */,
);
name = "Podspec Metadata";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
6003F589195388D20070C39A /* LMForm_Example */ = {
isa = PBXNativeTarget;
buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "LMForm_Example" */;
buildPhases = (
64605036FF160E73941F4226 /* [CP] Check Pods Manifest.lock */,
6003F586195388D20070C39A /* Sources */,
6003F587195388D20070C39A /* Frameworks */,
6003F588195388D20070C39A /* Resources */,
40E5790E141601A14760FAEA /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = LMForm_Example;
productName = LMForm;
productReference = 6003F58A195388D20070C39A /* LMForm_Example.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
6003F582195388D10070C39A /* Project object */ = {
isa = PBXProject;
attributes = {
CLASSPREFIX = LM;
LastUpgradeCheck = 1010;
ORGANIZATIONNAME = MaricleZhang;
TargetAttributes = {
6003F589195388D20070C39A = {
DevelopmentTeam = C69KXLDX73;
};
};
};
buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "LMForm" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
mainGroup = 6003F581195388D10070C39A;
productRefGroup = 6003F58B195388D20070C39A /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
6003F589195388D20070C39A /* LMForm_Example */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
6003F588195388D20070C39A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */,
71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */,
6003F5A9195388D20070C39A /* Images.xcassets in Resources */,
6003F598195388D20070C39A /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
40E5790E141601A14760FAEA /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-LMForm_Example/Pods-LMForm_Example-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/LMForm/LMForm.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LMForm.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LMForm_Example/Pods-LMForm_Example-resources.sh\"\n";
showEnvVarsInLog = 0;
};
64605036FF160E73941F4226 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-LMForm_Example-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
6003F586195388D20070C39A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6003F59E195388D20070C39A /* LMAppDelegate.m in Sources */,
0290F680229BBB5D0049BB3D /* LMCustomCell.m in Sources */,
6003F5A7195388D20070C39A /* LMViewController.m in Sources */,
6003F59A195388D20070C39A /* main.m in Sources */,
0290F683229BD1860049BB3D /* LMWindowHud.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
6003F596195388D20070C39A /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
6003F597195388D20070C39A /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
71719F9E1E33DC2100824A3D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
6003F5BD195388D20070C39A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
6003F5BE195388D20070C39A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
6003F5C0195388D20070C39A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4FC47A9DA5415BF6A8BC5E15 /* Pods-LMForm_Example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = C69KXLDX73;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "LMForm/LMForm-Prefix.pch";
INFOPLIST_FILE = "LMForm/LMForm-Info.plist";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.LMForm-Exampl";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Debug;
};
6003F5C1195388D20070C39A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 2D0D33DCB0410227D4AEBD23 /* Pods-LMForm_Example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = 88X46KN5Q5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "LMForm/LMForm-Prefix.pch";
INFOPLIST_FILE = "LMForm/LMForm-Info.plist";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
6003F585195388D10070C39A /* Build configuration list for PBXProject "LMForm" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6003F5BD195388D20070C39A /* Debug */,
6003F5BE195388D20070C39A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "LMForm_Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6003F5C0195388D20070C39A /* Debug */,
6003F5C1195388D20070C39A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 6003F582195388D10070C39A /* Project object */;
}
================================================
FILE: Example/LMForm.xcodeproj/xcshareddata/xcschemes/LMForm-Example.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "LMForm_Example.app"
BlueprintName = "LMForm_Example"
ReferencedContainer = "container:LMForm.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F5AD195388D20070C39A"
BuildableName = "LMForm_Tests.xctest"
BlueprintName = "LMForm_Tests"
ReferencedContainer = "container:LMForm.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "LMForm_Example.app"
BlueprintName = "LMForm_Example"
ReferencedContainer = "container:LMForm.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "LMForm_Example.app"
BlueprintName = "LMForm_Example"
ReferencedContainer = "container:LMForm.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "LMForm_Example.app"
BlueprintName = "LMForm_Example"
ReferencedContainer = "container:LMForm.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: Example/Podfile
================================================
# use_frameworks!
platform :ios, '9.0'
target 'LMForm_Example' do
pod 'LMForm', :path => '../'
end
================================================
FILE: LICENSE
================================================
Copyright (c) 2019 MaricleZhang <jian.zhang@qianli.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: LMForm/Assets/.gitkeep
================================================
================================================
FILE: LMForm/Classes/.gitkeep
================================================
================================================
FILE: LMForm/Classes/Category/UIImage+Bundle.h
================================================
//
// UIImage+Bundle.h
// LMForm
//
// Created by Zhang on 2019/5/23.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIImage (Bundle)
+ (UIImage *)bundleImageWithNamed:(NSString *)imageName;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Category/UIImage+Bundle.m
================================================
//
// UIImage+Bundle.m
// LMForm
//
// Created by Zhang on 2019/5/23.
//
#import "UIImage+Bundle.h"
@implementation UIImage (Bundle)
+ (UIImage *)bundleImageWithNamed:(NSString *)imageName
{
NSBundle *resouceBundle = [self bundleWithResourceName:@"LMForm"];
return [UIImage imageNamed:imageName inBundle:resouceBundle compatibleWithTraitCollection:nil];
}
+ (NSBundle *)bundleWithResourceName:(NSString *)resourceName
{
NSURL *url = [[NSBundle mainBundle] URLForResource:resourceName withExtension:@"bundle"];
NSBundle *resouceBundle = [NSBundle bundleWithURL:url];
return resouceBundle;
}
@end
================================================
FILE: LMForm/Classes/Category/UITextField+LimitLength.h
================================================
//
// UITextField+LimitLength.h
// CategoryDemo
//
// Created by MaricleZhang on 2019/3/20.
// Copyright © 2019 MaricleZhang. All rights reserved.
//
/** TextField 输入长度限制 */
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^TextLengthMoreThanBlock)(void);
@interface UITextField (LimitLength)
/** 输入限制长度 */
@property (nonatomic, strong) NSNumber *limitLength;
/** 输入长度超过限制回调 */
@property (nonatomic, copy) TextLengthMoreThanBlock lenghtBlock;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Category/UITextField+LimitLength.m
================================================
//
// UITextField+LimitLength.m
// CategoryDemo
//
// Created by MaricleZhang on 2019/3/20.
// Copyright © 2019 MaricleZhang. All rights reserved.
//
#import "UITextField+LimitLength.h"
#import <objc/runtime.h>
#import <objc/message.h>
static const void *limitLengthKey = &limitLengthKey;
static const void *textLengthMoreThanBlockKey = &textLengthMoreThanBlockKey;
@implementation UITextField (LimitLength)
#pragma mark - Setter/Getter
- (NSNumber *)limitLength
{
return objc_getAssociatedObject(self, limitLengthKey);
}
- (void)setLimitLength:(NSNumber *)limitLength
{
objc_setAssociatedObject(self, limitLengthKey, limitLength, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self addTarget:self action:@selector(textFieldTextDidChange) forControlEvents:UIControlEventEditingChanged];
}
- (TextLengthMoreThanBlock)lenghtBlock
{
return objc_getAssociatedObject(self, textLengthMoreThanBlockKey);
}
- (void)setLenghtBlock:(TextLengthMoreThanBlock)lenghtBlock
{
objc_setAssociatedObject(self, textLengthMoreThanBlockKey, lenghtBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (void)textFieldTextDidChange
{
if (self.text.length >= self.limitLength.intValue)
{
self.text = [self.text substringToIndex:self.limitLength.intValue];
if (self.lenghtBlock)
{
self.lenghtBlock();
}
}
}
+ (void)load
{
Method origMethod = class_getInstanceMethod([self class], NSSelectorFromString(@"dealloc"));
Method newMethod = class_getInstanceMethod([self class], @selector(my_dealloc));
method_exchangeImplementations(origMethod, newMethod);
}
- (void)my_dealloc
{
// do your logic here
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:self];
//this calls original dealloc method
[self my_dealloc];
}
@end
================================================
FILE: LMForm/Classes/Category/UITextView+AutoHeight.h
================================================
//
// UITextView+AutoHeight.h
// LMForm
//
// Created by 张建 on 2019/6/5.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UITextView (AutoHeight)
@property (nonatomic,copy) NSString *lm_placeholder;
@property (nonatomic,copy) UIColor *lm_placeholderColor;
@property (nonatomic,strong) UIFont *lm_placeholderFont;
@property (nonatomic,assign) NSUInteger lm_maxNumberOfLines;
@property (nonatomic,assign) CGFloat originalHeight;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Category/UITextView+AutoHeight.m
================================================
//
// UITextView+AutoHeight.m
// LMForm
//
// Created by 张建 on 2019/6/5.
//
#import "UITextView+AutoHeight.h"
#import <objc/runtime.h>
#import "LMMarco.h"
@interface UITextView ()
@property (nonatomic,strong) UITextView *placeHolderTextView;
@end
@implementation UITextView (AutoHeight)
+ (void)load
{
[super load];
method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"dealloc")),class_getInstanceMethod(self.class,@selector(exchanged_dealloc)));
}
- (void)exchanged_dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
UITextView *textView = objc_getAssociatedObject(self, @selector(placeHolderTextView));
if (textView)
{
for (NSString *key in self.class.observedKeys)
{
@try
{
[self removeObserver:self forKeyPath:key];
}
@catch (NSException *exception)
{
}
}
}
[self exchanged_dealloc];
}
+ (NSArray *)observedKeys
{
return @[@"attributedText",@"bounds",@"font",@"frame",@"text",@"textAlignment",@"textContainerInset",@"textContainer.exclusionPaths"];
}
- (CGFloat)originalHeight
{
static CGFloat originalHeight;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self.superview layoutIfNeeded];
originalHeight = self.bounds.size.height;
});
return originalHeight;
}
- (NSString *)lm_placeholder
{
return objc_getAssociatedObject(self, @selector(lm_placeholder));
}
- (void)setLm_placeholder:(NSString *)lm_placeholder
{
objc_setAssociatedObject(self, @selector(lm_placeholder), lm_placeholder, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self placeHolderTextView];
}
- (UIColor *)lm_placeholderColor
{
return objc_getAssociatedObject(self, @selector(lm_placeholderColor));
}
- (void)setLm_placeholderColor:(UIColor *)lm_placeholderColor
{
objc_setAssociatedObject(self, @selector(lm_placeholderColor), lm_placeholderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self textViewValueChanged];
}
- (UIFont *)lm_placeholderFont
{
return objc_getAssociatedObject(self, @selector(lm_placeholderFont));
}
- (void)setLm_placeholderFont:(UIFont *)lm_placeholderFont
{
objc_setAssociatedObject(self, @selector(lm_placeholderFont), lm_placeholderFont, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self textViewValueChanged];
}
- (NSUInteger)lm_maxNumberOfLines
{
return [objc_getAssociatedObject(self, @selector(lm_maxNumberOfLines)) integerValue];
}
- (void)setLm_maxNumberOfLines:(NSUInteger)lm_maxNumberOfLines
{
objc_setAssociatedObject(self, @selector(lm_maxNumberOfLines), @(lm_maxNumberOfLines), OBJC_ASSOCIATION_ASSIGN);
[self textViewValueChanged];
}
- (BOOL)lm_autoLineBreak
{
return [objc_getAssociatedObject(self, @selector(lm_autoLineBreak)) boolValue];
}
- (void)setLm_autoLineBreak:(BOOL)lm_autoLineBreak
{
objc_setAssociatedObject(self, @selector(lm_autoLineBreak), @(lm_autoLineBreak), OBJC_ASSOCIATION_ASSIGN);
[self textViewValueChanged];
}
- (UITextView *)placeHolderTextView
{
UITextView *placeHolderTextView = objc_getAssociatedObject(self, @selector(placeHolderTextView));
if (!placeHolderTextView)
{
self.text = @"";
placeHolderTextView = [UITextView new];
placeHolderTextView.userInteractionEnabled = NO;
placeHolderTextView.backgroundColor = [UIColor colorWithWhite:1 alpha:0];
objc_setAssociatedObject(self, @selector(placeHolderTextView), placeHolderTextView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self insertSubview:placeHolderTextView atIndex:0];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewValueChanged) name:UITextViewTextDidChangeNotification object:self];
for (NSString *key in self.class.observedKeys)
{
[self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew| NSKeyValueObservingOptionOld context:nil];
}
[self textViewValueChanged];
}
return placeHolderTextView;
}
- (void)updateHight
{
self.placeHolderTextView.hidden = (self.text.length > 0);
CGFloat maxHeight = ceil(self.font.lineHeight * self.lm_maxNumberOfLines + self.textContainerInset.top + self.textContainerInset.bottom);
NSInteger height = self.text.length ? ceil([self sizeThatFits:CGSizeMake(self.frame.size.width, MAXFLOAT)].height) : self.originalHeight;
if (!self.lm_maxNumberOfLines && height > self.originalHeight)
{
CGRect newFrame = self.frame;
newFrame.size.height = height;
self.frame = newFrame;
}
self.scrollEnabled = height > maxHeight && self.lm_maxNumberOfLines;
if (maxHeight >= height && height >= self.originalHeight)
{
CGRect newFrame = self.frame;
newFrame.size.height = height;
self.frame = newFrame;
}
}
- (void)textViewValueChanged
{
self.placeHolderTextView.hidden = self.text.length;
if(!self.text.length)
{
self.placeHolderTextView.text = self.lm_placeholder;
self.placeHolderTextView.textColor = self.lm_placeholderColor ?: LM_UIColorFromHEX(0xC0C0C0);
self.placeHolderTextView.font = self.lm_placeholderFont?:self.font;
self.placeHolderTextView.textContainer.exclusionPaths = self.textContainer.exclusionPaths;
self.placeHolderTextView.textAlignment = self.textAlignment;
self.placeHolderTextView.frame = self.bounds;
}
[self updateHight];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if (object != self.placeHolderTextView && [keyPath isEqualToString:@"text"])
{
[self updateHight];
}
}
@end
================================================
FILE: LMForm/Classes/Category/UIView+Extension.h
================================================
//
// UIView+Extension.h
// LMForm
//
// Created by Zhang on 2019/5/20.
// Copyright © 2019 Maricle. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (Extension)
@property (nonatomic, assign) CGFloat x;
@property (nonatomic, assign) CGFloat y;
@property (nonatomic, assign) CGFloat maxX;
@property (nonatomic, assign) CGFloat maxY;
@property (nonatomic, assign) CGFloat centerX;
@property (nonatomic, assign) CGFloat centerY;
@property (nonatomic, assign) CGFloat width;
@property (nonatomic, assign) CGFloat height;
@property (nonatomic, assign) CGSize size;
@property (nonatomic, assign) CGPoint origin;
@property (nonatomic, assign) CGFloat bottom;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Category/UIView+Extension.m
================================================
//
// UIView+Extension.m
// LMForm
//
// Created by Zhang on 2019/5/20.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "UIView+Extension.h"
@implementation UIView (Extension)
#pragma mark - x
- (void)setX:(CGFloat)x {
CGRect frame = self.frame;
frame.origin.x = x;
self.frame = frame;
}
- (CGFloat)x {
return CGRectGetMinX(self.frame);
}
#pragma mark - y
- (void)setY:(CGFloat)y {
CGRect frame = self.frame;
frame.origin.y = y;
self.frame = frame;
}
- (CGFloat)y {
return CGRectGetMinY(self.frame);
}
#pragma mark - maxX
- (void)setMaxX:(CGFloat)maxX {
self.x = maxX - self.width;
}
- (CGFloat)maxX {
return CGRectGetMaxX(self.frame);
}
#pragma mark - maxY
- (void)setMaxY:(CGFloat)maxY {
self.y = maxY - self.height;
}
- (CGFloat)maxY {
return CGRectGetMaxY(self.frame);
}
#pragma mark - centerX
- (void)setCenterX:(CGFloat)centerX {
CGPoint center = self.center;
center.x = centerX;
self.center = center;
}
- (CGFloat)centerX {
return self.center.x;
}
#pragma mark - centerY
- (void)setCenterY:(CGFloat)centerY {
CGPoint center = self.center;
center.y = centerY;
self.center = center;
}
- (CGFloat)centerY {
return self.center.y;
}
#pragma mark - width
- (void)setWidth:(CGFloat)width {
CGRect frame = self.frame;
frame.size.width = width;
self.frame = frame;
}
- (CGFloat)width {
return CGRectGetWidth(self.frame);
}
#pragma mark - height
- (void)setHeight:(CGFloat)height {
CGRect frame = self.frame;
frame.size.height = height;
self.frame = frame;
}
- (CGFloat)height {
return CGRectGetHeight(self.frame);
}
#pragma mark - size
- (void)setSize:(CGSize)size {
CGRect frame = self.frame;
frame.size = size;
self.frame = frame;
}
- (CGSize)size {
return self.frame.size;
}
#pragma mark - orgin
- (void)setOrigin:(CGPoint)origin {
CGRect frame = self.frame;
frame.origin = origin;
self.frame = frame;
}
- (CGPoint)origin {
return self.frame.origin;
}
#pragma mark - bottom
- (void)setBottom:(CGFloat)bottom {
CGRect frame = self.frame;
frame.origin.y = bottom - frame.size.height;
self.frame = frame;
}
- (CGFloat)bottom {
CGRect frame = self.frame;
return CGRectGetMinY(frame) + CGRectGetHeight(frame);
}
@end
================================================
FILE: LMForm/Classes/Cell/LMAddressCell.h
================================================
//
// LMAddressCell.h
// LMForm
//
// Created by Zhang on 2019/5/8.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface LMAddressCell : LMFormCell
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Cell/LMAddressCell.m
================================================
//
// LMAddressCell.m
// LMForm
//
// Created by Zhang on 2019/5/8.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMAddressCell.h"
#import "UIImage+Bundle.h"
@interface LMAddressCell ()
@property (nonatomic, strong) UIImageView *arrowImgView;
@end
@implementation LMAddressCell
- (void)createUI
{
[super createUI];
[self.contentView addSubview:self.arrowImgView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapSelectedAction)];
[self.contentView addGestureRecognizer:tap];
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.textField.frame = CGRectMake(LM_Screen_Width - LM_XX_6(52) - LM_Screen_Width / 2 , 0, LM_Screen_Width / 2, self.contentView.height);
self.arrowImgView.frame = CGRectMake(LM_Screen_Width - LM_DefautMargin - LM_DefautMargin, 0, LM_DefautMargin, LM_DefautMargin);
self.arrowImgView.centerY = self.contentView.height / 2;
}
- (NSString *)fetchTextWithSelectID:(NSString *)selectedID
{
__block NSString *text = @"";
// [self.model.selectList enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// if ([obj isEqualToString:[NSString class]])
// {
// text = obj;
// }
// }];
return text;
}
#pragma mark - Responce
- (void)tapSelectedAction
{
// @weakify(self)
// LMPickerView *pick = [[LMPickerView alloc] initWithDataSource:self.model.selectList block:^(LMSelectModel *selectModel) {
// @strongify(self)
// self.model.value = selectModel.selectedID;
// self.textField.text = selectModel.selectedText;
// if (self.model.addressCellDidSelectedBlock)
// {
// self.model.addressCellDidSelectedBlock(selectModel.selectedID,self.model.key);
// }
// }];
// pick.type = LMPickerViewTypeDefault;
}
#pragma mark - Setter/Getter
- (UIImageView *)arrowImgView
{
if (!_arrowImgView)
{
_arrowImgView = [[UIImageView alloc] initWithImage:[UIImage bundleImageWithNamed:@"lm_common_arrow"]];
}
return _arrowImgView;
}
#pragma mark - LMFormCellProtocol
- (void)configModel:(LMFormModel *)model
{
[super configModel:model];
self.textField.text = [self fetchTextWithSelectID:model.value];
self.textField.placeholder = model.placeholder;
}
@end
================================================
FILE: LMForm/Classes/Cell/LMDateCell.h
================================================
//
// LMDateCell.h
// LMForm
//
// Created by Zhang on 2019/5/8.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface LMDateCell : LMFormCell
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Cell/LMDateCell.m
================================================
//
// LMDateCell.m
// LMForm
//
// Created by Zhang on 2019/5/8.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMDateCell.h"
#import "LMPopupView.h"
#import "UIImage+Bundle.h"
@interface LMDateCell ()
@property (nonatomic, strong) UIImageView *arrowImgView;
@property (nonatomic, strong) UIDatePicker *datePicker;
@end
@implementation LMDateCell
- (void)createUI
{
[super createUI];
[self.contentView addSubview:self.arrowImgView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapSelectedAction)];
[self.contentView addGestureRecognizer:tap];
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.textField.frame = CGRectMake(self.titleLabel.maxX + LM_DefaultSpace , 0, LM_Screen_Width - self.titleLabel.maxX - LM_ObjDefault(self.model.margin, LM_DefautMargin) - LM_RightArrowWidth - LM_DefaultSpace , self.contentView.height);
self.arrowImgView.frame = CGRectMake(LM_Screen_Width - LM_ObjDefault(self.model.margin, LM_DefautMargin) - LM_RightArrowWidth, 0, LM_RightArrowWidth, LM_RightArrowWidth);
self.arrowImgView.centerY = self.contentView.height / 2;
}
#pragma mark - Responce
- (void)tapSelectedAction
{
@weakify(self)
[LMPopupView showPopupViewWithPickView:self.datePicker title:self.model.placeholder confirmBlock:^{
@strongify(self)
NSDate *date = self.datePicker.date;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:self.model.dateFormat ?: @"yyyy-MM-dd"];
NSString *text = [formatter stringFromDate:date];
self.model.value = text;
self.textField.text = text;
} cancelBlock:^{
}];
}
#pragma mark - Setter/Getter
- (UIImageView *)arrowImgView
{
if (!_arrowImgView)
{
_arrowImgView = [[UIImageView alloc] initWithImage:[UIImage bundleImageWithNamed:@"lm_common_arrow"]];
}
return _arrowImgView;
}
- (UIDatePicker *)datePicker
{
if (!_datePicker)
{
_datePicker = [[UIDatePicker alloc] init];
_datePicker.datePickerMode = UIDatePickerModeDate;
if (@available(iOS 13.4, *)) {
_datePicker.preferredDatePickerStyle = UIDatePickerStyleWheels;
}
_datePicker.backgroundColor = [UIColor clearColor];
_datePicker.date = [NSDate date];
_datePicker.maximumDate = [NSDate dateWithTimeIntervalSinceNow:0];
}
return _datePicker;
}
#pragma mark - LMFormCellProtocol
- (void)configModel:(LMFormModel *)model
{
[super configModel:model];
self.datePicker.datePickerMode = model.datePickerMode;
}
@end
================================================
FILE: LMForm/Classes/Cell/LMFormAddressInputCell.h
================================================
//
// LMFormAddressInputCell.h
// LMForm
//
// Created by Zhang on 2019/5/5.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface LMFormAddressInputCell : LMFormCell
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Cell/LMFormAddressInputCell.m
================================================
//
// LMFormAddressInputCell.m
// LMForm
//
// Created by Zhang on 2019/5/5.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormAddressInputCell.h"
@implementation LMFormAddressInputCell
- (void)layoutSubviews
{
[super layoutSubviews];
self.titleLabel.y = LM_XX_6(15);
self.textField.frame = CGRectMake(LM_ObjDefault(self.model.margin, LM_DefautMargin) , LM_XX_6(30), LM_Screen_Width - 2 * LM_ObjDefault(self.model.margin, LM_DefautMargin), LM_XX_6(40));
}
#pragma mark - LMFormCellProtocol
- (void)configModel:(LMFormModel *)model
{
[super configModel:model];
self.textField.userInteractionEnabled = YES;
self.textField.textAlignment = NSTextAlignmentLeft;
}
@end
================================================
FILE: LMForm/Classes/Cell/LMFormCell.h
================================================
//
// LMFormCell.h
// LMForm
//
// Created by Zhang on 2019/4/30.
// Copyright © 2019 Maricle. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "LMFormModel.h"
#import "LMMarco.h"
#import "UIView+Extension.h"
#import "LMFormCellProtocol.h"
NS_ASSUME_NONNULL_BEGIN
@interface LMFormCell : UITableViewCell<LMFormCellProtocol>
@property (nonatomic, strong) LMFormModel *model;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UITextField *textField;
- (void)createUI;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Cell/LMFormCell.m
================================================
//
// LMFormCell.m
// LMForm
//
// Created by Zhang on 2019/4/30.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormCell.h"
#import "UITextField+LimitLength.h"
@interface LMFormCell ()
@property (nonatomic, strong) UIView *line;
@end
@implementation LMFormCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])
{
[self createUI];
}
return self;
}
- (void)createUI
{
[self.contentView addSubview:self.titleLabel];
[self.contentView addSubview:self.textField];
[self.contentView addSubview:self.line];
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self.titleLabel sizeToFit];
self.titleLabel.x = LM_ObjDefault(self.model.margin, LM_DefautMargin);
self.titleLabel.centerY = self.contentView.height / 2;
self.textField.frame = CGRectMake(self.titleLabel.maxX + LM_DefaultSpace , 0, LM_Screen_Width - self.titleLabel.maxX - LM_ObjDefault(self.model.margin, LM_DefautMargin) - LM_DefaultSpace , self.contentView.height);
self.line.frame = CGRectMake(LM_ObjDefault(self.model.margin, LM_DefautMargin), self.contentView.height - 0.5, self.contentView.width - LM_ObjDefault(self.model.margin, LM_DefautMargin) * 2, 0.5);
}
#pragma mark - Responce
- (void)textDidChanged:(UITextField *)textField
{
self.model.value = textField.text;
if (self.model.valueDidChangedBlock)
{
self.model.valueDidChangedBlock(textField.text);
}
}
#pragma mark - Setter/Getter
- (UILabel *)titleLabel
{
if (!_titleLabel)
{
_titleLabel = [[UILabel alloc] init];
_titleLabel.textColor = LM_UIColorFromHEX(0x666666);
_titleLabel.font = [UIFont systemFontOfSize:LM_XX_6(14)];
}
return _titleLabel;
}
- (UITextField *)textField
{
if (!_textField)
{
_textField = [[UITextField alloc] init];
_textField.userInteractionEnabled = NO;
_textField.textAlignment = NSTextAlignmentRight;
_textField.textColor = LM_UIColorFromHEX(0x333333);
_textField.font = [UIFont systemFontOfSize:LM_XX_6(14)];
[_textField addTarget:self action:@selector(textDidChanged:) forControlEvents:UIControlEventEditingChanged];
}
return _textField;
}
- (UIView *)line
{
if (!_line)
{
_line = [[UIView alloc] init];
_line.backgroundColor = LM_UIColorFromHEX(0xF4F4F4);
}
return _line;
}
#pragma mark - LMFormCellProtocol
- (void)configModel:(LMFormModel *)model
{
self.model = model;
// data
self.titleLabel.text = model.title;
self.textField.placeholder = model.placeholder;
self.textField.text = model.value;
if (model.limitLength)
{
self.textField.limitLength = @(model.limitLength);
}
// UI
self.line.hidden = model.hiddenLine;
self.line.backgroundColor = LM_ObjDefault(model.separatorLineColor, LM_UIColorFromHEX(0xF4F4F4));
self.titleLabel.textColor = LM_ObjDefault(model.leftTextColor, LM_UIColorFromHEX(0x666666));
self.textField.textColor = LM_ObjDefault(model.rightTextColor, LM_UIColorFromHEX(0x333333));
self.titleLabel.font = LM_ObjDefault(model.leftLabelFont, [UIFont systemFontOfSize:LM_XX_6(14)]);
self.textField.font = LM_ObjDefault(model.rightLabelFont, [UIFont systemFontOfSize:LM_XX_6(14)]);
}
@end
================================================
FILE: LMForm/Classes/Cell/LMFormInputCell.h
================================================
//
// LMFormInputCell.h
// LMForm
//
// Created by Zhang on 2019/4/30.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface LMFormInputCell : LMFormCell
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Cell/LMFormInputCell.m
================================================
//
// LMFormInputCell.m
// LMForm
//
// Created by Zhang on 2019/4/30.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormInputCell.h"
@implementation LMFormInputCell
- (void)layoutSubviews
{
[super layoutSubviews];
self.textField.frame = CGRectMake(self.titleLabel.maxX + LM_DefaultSpace , 0, LM_Screen_Width - self.titleLabel.maxX - LM_ObjDefault(self.model.margin, LM_DefautMargin) - LM_DefaultSpace , self.contentView.height);
}
#pragma mark - LMFormCellProtocol
- (void)configModel:(LMFormModel *)model
{
[super configModel:model];
self.textField.userInteractionEnabled = YES;
}
@end
================================================
FILE: LMForm/Classes/Cell/LMFormSelectorCell.h
================================================
//
// LMFormSelectorCell.h
// LMForm
//
// Created by Zhang on 2019/4/30.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface LMFormSelectorCell : LMFormCell
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Cell/LMFormSelectorCell.m
================================================
//
// LMFormSelectorCell.m
// LMForm
//
// Created by Zhang on 2019/4/30.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormSelectorCell.h"
#import "LMPopupView.h"
#import "LMDefaultPickerView.h"
#import "UIImage+Bundle.h"
@interface LMFormSelectorCell ()
@property (nonatomic, strong) UIImageView *arrowImgView;
@end
@implementation LMFormSelectorCell
- (void)createUI
{
[super createUI];
[self.contentView addSubview:self.arrowImgView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapSelectedAction)];
[self.contentView addGestureRecognizer:tap];
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.textField.frame = CGRectMake(self.titleLabel.maxX + LM_DefaultSpace , 0, LM_Screen_Width - self.titleLabel.maxX - LM_ObjDefault(self.model.margin, LM_DefautMargin) - LM_RightArrowWidth - LM_DefaultSpace , self.contentView.height);
self.arrowImgView.frame = CGRectMake(LM_Screen_Width - LM_ObjDefault(self.model.margin, LM_DefautMargin) - LM_RightArrowWidth, 0, LM_RightArrowWidth, LM_RightArrowWidth);
self.arrowImgView.centerY = self.contentView.height / 2;
}
#pragma mark - Responce
- (void)tapSelectedAction
{
LMDefaultPickerView *pickView = [[LMDefaultPickerView alloc] initWithDataArray:self.model.selectList];
@weakify(self)
[LMPopupView showPopupViewWithPickView:pickView title:self.model.placeholder confirmBlock:^{
@strongify(self)
NSString *text = self.model.selectList[pickView.selectIndex];
self.model.value = text;
self.textField.text = text;
if (self.model.valueDidChangedBlock)
{
self.model.valueDidChangedBlock(text);
}
} cancelBlock:^{
}];
}
#pragma mark - Setter/Getter
- (UIImageView *)arrowImgView
{
if (!_arrowImgView)
{
_arrowImgView = [[UIImageView alloc] initWithImage:[UIImage bundleImageWithNamed:@"lm_common_arrow"]];
}
return _arrowImgView;
}
@end
================================================
FILE: LMForm/Classes/Cell/LMFormTextViewCell.h
================================================
//
// LMFormTextViewCell.h
// LMForm
//
// Created by 张建 on 2019/6/5.
//
#import "LMFormCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface LMFormTextViewCell : LMFormCell
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Cell/LMFormTextViewCell.m
================================================
//
// LMFormTextViewCell.m
// LMForm
//
// Created by 张建 on 2019/6/5.
//
#import "LMFormTextViewCell.h"
#import "UITextView+AutoHeight.h"
@interface LMFormTextViewCell ()<UITextViewDelegate>
@property (nonatomic, strong) UITextView *textView;
//@property (nonatomic, strong) UITextView *placeHolderTextView;
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, assign) CGFloat defaultHegiht;
@end
@implementation LMFormTextViewCell
- (void)createUI
{
[super createUI];
[self.textField removeFromSuperview];
[self.contentView addSubview:self.textView];
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.titleLabel.y = LM_XX_6(15);
}
#pragma mark - UITextViewDelegate
- (void)textViewDidChange:(UITextView *)textView
{
self.model.value = textView.text;
self.model.height = MAX(self.textView.height + LM_XX_6(30) + 1, self.textView.originalHeight);
@weakify(self)
[UIView performWithoutAnimation:^{
@strongify(self)
[self.tableView beginUpdates];
[self.tableView endUpdates];
}];
}
#pragma mark - Setter/Getter
- (UITextView *)textView
{
if (!_textView)
{
_textView = [[UITextView alloc] init];
_textView.frame = CGRectMake(LM_DefautMargin,LM_XX_6(30), LM_Screen_Width - 2 * LM_DefautMargin , self.model.height - LM_XX_6(30) - 1);
_textView.delegate = self;
_textView.scrollEnabled = NO;
}
return _textView;
}
- (UITableView *)tableView
{
UIView *tableView = self.superview;
while (![tableView isKindOfClass:[UITableView class]] && tableView)
{
tableView = tableView.superview;
}
return (UITableView *)tableView;
}
#pragma mark - LMFormCellProtocol
- (void)configModel:(LMFormModel *)model
{
[super configModel:model];
self.textView.textColor = LM_ObjDefault(model.rightTextColor, LM_UIColorFromHEX(0x333333));
self.textView.font = LM_ObjDefault(model.rightLabelFont, [UIFont systemFontOfSize:LM_XX_6(14)]);
self.textView.lm_placeholder = model.placeholder;
self.textView.lm_maxNumberOfLines = model.lm_maxNumberOfLines;
self.textView.text = model.value;
self.textView.frame = CGRectMake(LM_ObjDefault(self.model.margin, LM_DefautMargin) ,LM_XX_6(30), LM_Screen_Width - 2 * LM_ObjDefault(self.model.margin, LM_DefautMargin) , self.model.height - LM_XX_6(30) - 1);
}
@end
================================================
FILE: LMForm/Classes/LMForm.h
================================================
//
// Header.h
// LMForm
//
// Created by Zhang on 2019/5/23.
// Copyright © 2019 Maricle. All rights reserved.
//
#ifndef LMForm_h
#define LMForm_h
#import "LMFormTypeManager.h"
#import "LMFormCellProtocol.h"
#import "LMFormModel.h"
#import "LMMarco.h"
#import "LMFormTableView.h"
#import "LMFormValidator.h"
#endif /* Header_h */
================================================
FILE: LMForm/Classes/LMFormCellProtocol.h
================================================
//
// LMFormCellProtocol.h
// LMForm
//
// Created by Zhang on 2019/5/20.
// Copyright © 2019 Maricle. All rights reserved.
//
#import <Foundation/Foundation.h>
@class LMFormModel;
@protocol LMFormCellProtocol <NSObject>
/** 根据model 配置对应的cell */
- (void)configModel:(LMFormModel *)model;
@end
================================================
FILE: LMForm/Classes/LMMarco.h
================================================
//
// LMMarco.h
// LMForm
//
// Created by Zhang on 2019/5/20.
// Copyright © 2019 Maricle. All rights reserved.
//
#ifndef LMMarco_h
#define LMMarco_h
#define LM_Screen_Width ([UIScreen mainScreen].bounds.size.width)
#define LM_Screen_Height ([UIScreen mainScreen].bounds.size.height)
// 物理点 为单位
#define LM_XX_6(value) (1.0 * (value) * LM_Screen_Width / 375.0)
#define LM_UIColorFromHEX_Alpha(rgbValue, a) \
[UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 \
alpha:(a)]
#define LM_UIColorFromHEX(rgbValue) LM_UIColorFromHEX_Alpha(rgbValue, 1.0)
#define LM_DefautMargin LM_XX_6(20)
#define LM_ObjDefault(obj,default) (obj ?: default)
#define LM_RightArrowWidth LM_XX_6(24)
#define LM_DefaultSpace LM_XX_6(10)
/**
合成弱引用/强引用
Example:
@weakify(self)
[self doSomething^{
@strongify(self)
if (!self) return;
...
}];
*/
#ifndef weakify
#if DEBUG
#if __has_feature(objc_arc)
#define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
#endif
#else
#if __has_feature(objc_arc)
#define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
#endif
#endif
#endif
#ifndef strongify
#if DEBUG
#if __has_feature(objc_arc)
#define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
#endif
#else
#if __has_feature(objc_arc)
#define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
#endif
#endif
#endif
#endif /* LMMarco_h */
================================================
FILE: LMForm/Classes/Manager/LMFormTypeManager.h
================================================
//
// LMFormTypeManager.h
// LMForm
//
// Created by Zhang on 2019/5/22.
// Copyright © 2019 Maricle. All rights reserved.
//
#import <Foundation/Foundation.h>
/** 这里是pod 中已经集成的cell 类型 */
#define kFormTypeText @"text"
#define kFormTypeInput @"input"
#define kFormTypeSelector @"selector"
#define kFormTypeDate @"date"
#define kFormTypeAddress @"address"
#define kFormTypeAddressInput @"addressInput"
#define kFormTypeTextView @"textView"
NS_ASSUME_NONNULL_BEGIN
@interface LMFormTypeManager : NSObject
+ (LMFormTypeManager *)manager;
/**
自定义cell时注册方法,同一种cell只需要注册一次。cls 需继承UITableViewCell
@param cls cell class
@param key key
*/
- (void)registerCellClass:(Class)cls forKey:(NSString *)key;
/**
根据key获取对应的cell class
@param key key
@return cell class
*/
- (Class)getClassWithKey:(NSString *)key;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Manager/LMFormTypeManager.m
================================================
//
// LMFormTypeManager.m
// LMForm
//
// Created by Zhang on 2019/5/22.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormTypeManager.h"
#import "LMFormInputCell.h"
#import "LMFormSelectorCell.h"
#import "LMFormAddressInputCell.h"
#import "LMDateCell.h"
#import "LMAddressCell.h"
#import "IQKeyboardManager.h"
#import "LMFormAddressInputCell.h"
#import "LMFormTextViewCell.h"
@interface LMFormTypeManager ()
@property (nonatomic, strong) NSMutableDictionary *keyCellTypes;
@end
@implementation LMFormTypeManager
+ (instancetype)manager
{
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [[self alloc] init];
});
return instance;
}
- (instancetype)init
{
self = [super init];
if (self)
{
[self registerAllCell];
/*** 键盘 ***/
[IQKeyboardManager sharedManager].enableAutoToolbar = NO;
[IQKeyboardManager sharedManager].shouldResignOnTouchOutside = YES;
}
return self;
}
- (void)registerAllCell
{
[self.keyCellTypes setObject:[LMFormCell class] forKey:kFormTypeText];
[self.keyCellTypes setObject:[LMFormInputCell class] forKey:kFormTypeInput];
[self.keyCellTypes setObject:[LMFormSelectorCell class] forKey:kFormTypeSelector];
[self.keyCellTypes setObject:[LMDateCell class] forKey:kFormTypeDate];
[self.keyCellTypes setObject:[LMAddressCell class] forKey:kFormTypeAddress];
[self.keyCellTypes setObject:[LMFormAddressInputCell class] forKey:kFormTypeAddressInput];
[self.keyCellTypes setObject:[LMFormTextViewCell class] forKey:kFormTypeTextView];
}
- (void)registerCellClass:(Class)cls forKey:(NSString *)key
{
if (cls && key)
{
[self.keyCellTypes setObject:cls forKey:key];
}
}
- (Class)getClassWithKey:(NSString *)key
{
return [self.keyCellTypes objectForKey:key];
}
- (NSMutableDictionary *)keyCellTypes
{
if (!_keyCellTypes)
{
_keyCellTypes = [NSMutableDictionary dictionary];
}
return _keyCellTypes;
}
@end
================================================
FILE: LMForm/Classes/Model/LMFormModel.h
================================================
//
// LMFormModel.h
// LMForm
//
// Created by Zhang on 2019/4/30.
// Copyright © 2019 Maricle. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "LMFormValidator.h"
@class LMFormModel;
typedef void(^LMValueDidChangedBlock)(NSString * _Nullable vlaue);
typedef void(^LMAddressCellDidSelectedBlock)(NSString * _Nullable vlaue,NSString * _Nullable key);
typedef BOOL(^LMValidateBlock)(LMFormModel * _Nullable model);
NS_ASSUME_NONNULL_BEGIN
@interface LMFormModel : NSObject
@property (nonatomic, copy) NSString *title;//左边标题
@property (nonatomic, copy) NSString *key;//提交信息的入参
@property (nonatomic, copy) NSString *value;//输入或者选择的值
@property (nonatomic, copy) NSString *message;//输入错误提示信息
@property (nonatomic, copy) NSString *formType;
@property (nonatomic, strong) Class cellClass;// 对应的cell class
@property (nonatomic, copy) LMValueDidChangedBlock valueDidChangedBlock; //输入或者选择完成回调
@property (nonatomic, copy) LMValidateBlock validateBlock;
/** UI 样式,可根据需求更改 */
@property (nonatomic, assign) CGFloat height;//cell 高度
@property (nonatomic, assign) BOOL hiddenLine;// 是否隐藏分割线,默认NO
@property (nonatomic, assign) CGFloat margin;// 分割线与两边的边距,左右的边距相等。
@property (nonatomic, strong) UIColor *separatorLineColor;// 分割线颜色
@property (nonatomic, strong) UIColor *leftTextColor;// 左边文本颜色
@property (nonatomic, strong) UIColor *rightTextColor;// 右边边文本颜色
@property (nonatomic, strong) UIFont *leftLabelFont;// 左边标题字体
@property (nonatomic, strong) UIFont *rightLabelFont;// 右边标题字体
// LMFormInputCell
@property (nonatomic, copy) NSString *placeholder; //textField placeholder
@property (nonatomic, assign) NSInteger limitLength;//textfield 最大输入长度
// LMFormSelectorCell
@property (nonatomic, copy) NSArray<NSString *> *selectList;
// LMDateCell
@property (nonatomic, assign) UIDatePickerMode datePickerMode;
@property (nonatomic, copy) NSString *dateFormat;//日期格式,eg:yyyy-MM-dd,MM/dd
// LMAddressCell
@property (nonatomic, copy) LMAddressCellDidSelectedBlock addressCellDidSelectedBlock;
@property (nonatomic, assign) NSString *addressType;
// LMFormTextViewCell
@property (nonatomic, assign) NSInteger lm_maxNumberOfLines;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Model/LMFormModel.m
================================================
//
// LMFormModel.m
// LMForm
//
// Created by Zhang on 2019/4/30.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormModel.h"
@implementation LMFormModel
@end
================================================
FILE: LMForm/Classes/Model/LMFormValidator.h
================================================
//
// LMFormValidator.h
// LMForm
//
// Created by Zhang on 2019/5/5.
// Copyright © 2019 Maricle. All rights reserved.
//
#import <Foundation/Foundation.h>
@class LMFormModel;
NS_ASSUME_NONNULL_BEGIN
@interface LMFormValidator : NSObject
/**
正则校验
@param value 校验的字符串
@param regex 校验的正则表达式
@return BOOL
*/
+ (BOOL)validate:(NSString *)value WithRegex:(NSString *)regex;
/**
校验手机号
@param value 校验的字符串
@return BOOL
*/
+ (BOOL)isMobile:(NSString *)value;
/**
对数据源校验
@param dataArray 数据源
@return 全部校验通过返回YES,否则返回NO。
*/
+ (BOOL)validateDataArray:(NSArray<LMFormModel *> *)dataArray;
/**
对输入或者选择的数据非空校验
@param value 校验的字符串
@return BOOL 为空返回YES
*/
+ (BOOL)isEmptyValue:(NSString *)value;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/Model/LMFormValidator.m
================================================
//
// LMFormValidator.m
// LMForm
//
// Created by Zhang on 2019/5/5.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormValidator.h"
#import "LMFormModel.h"
@implementation LMFormValidator
+ (BOOL)validate:(NSString *)value WithRegex:(NSString *)regex
{
return [[NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex] evaluateWithObject:[value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
}
+ (BOOL)isMobile:(NSString *)value
{
return [self validate:value WithRegex:@"^(1[3-9])\\d{9}$"];
}
+ (BOOL)validateDataArray:(NSArray<LMFormModel *> *)dataArray
{
for (LMFormModel * _Nonnull obj in dataArray)
{
if (obj.validateBlock)
{
if (!obj.validateBlock(obj)) return NO;
}
}
return YES;
}
+ (BOOL)isEmptyValue:(NSString *)value
{
if (!value) return YES;
if ([value isKindOfClass:[NSNull class]])
{
return YES;
}
if ([value isKindOfClass:[NSString class]])
{
if (value.length == 0)
{
return YES;
}
}
return NO;
}
@end
================================================
FILE: LMForm/Classes/View/LMDefaultPickerView.h
================================================
//
// LMDefaultPickerView.h
// LMForm
//
// Created by Zhang on 2019/5/21.
// Copyright © 2019 Maricle. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "LMFormModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface LMDefaultPickerView : UIPickerView
@property (nonatomic, strong) NSArray *dataArray;
@property (nonatomic, assign) NSInteger selectIndex;
- (instancetype)initWithDataArray:(NSArray<NSString *> *)dataArray;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/View/LMDefaultPickerView.m
================================================
//
// LMDefaultPickerView.m
// LMForm
//
// Created by Zhang on 2019/5/21.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMDefaultPickerView.h"
@interface LMDefaultPickerView()<UIPickerViewDelegate,UIPickerViewDataSource>
@end
@implementation LMDefaultPickerView
- (instancetype)initWithDataArray:(NSArray<NSString *> *)dataArray
{
self = [super init];
if (self)
{
self.delegate = self;
self.dataSource = self;
self.dataArray = dataArray;
self.showsSelectionIndicator = YES;
}
return self;
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return self.dataArray.count;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
if (row < self.dataArray.count)
{
NSString *text = [self.dataArray objectAtIndex:row];
return text;
}
return @"";
}
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component
{
return self.frame.size.width;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component
{
return 35.0;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
self.selectIndex = row;
}
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
UILabel *pickerLabel = (UILabel *)view;
if (!pickerLabel)
{
pickerLabel = [[UILabel alloc] init];
pickerLabel.adjustsFontSizeToFitWidth = YES;
[pickerLabel setTextAlignment:NSTextAlignmentCenter];
[pickerLabel setBackgroundColor:[UIColor clearColor]];
[pickerLabel setFont:[UIFont systemFontOfSize:20]];
}
pickerLabel.text = [self pickerView:pickerView titleForRow:row forComponent:component];
return pickerLabel;
}
@end
================================================
FILE: LMForm/Classes/View/LMFormTableView.h
================================================
//
// LMFormTableView.h
// LMForm
//
// Created by Zhang on 2019/4/30.
// Copyright © 2019 Maricle. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "LMFormModel.h"
typedef void(^LMFormSubmitActionBlock)(void);
NS_ASSUME_NONNULL_BEGIN
@interface LMFormTableView : UITableView
@property (nonatomic, strong) NSArray<LMFormModel *> *dataArray;
@property (nonatomic, copy) LMFormSubmitActionBlock actionBlock;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/View/LMFormTableView.m
================================================
//
// LMFormTableView.m
// LMForm
//
// Created by Zhang on 2019/4/30.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMFormTableView.h"
#import "LMFormCell.h"
#import "LMFormTypeManager.h"
@interface LMFormTableView ()<UITableViewDataSource, UITableViewDelegate>
@end
@implementation LMFormTableView
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
{
self = [super initWithFrame:frame style:style];
if (self)
{
[self initData];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self initData];
}
return self;
}
- (instancetype)init
{
self = [super init];
if (self)
{
[self initData];
}
return self;
}
- (void)initData
{
self.delegate = self;
self.dataSource = self;
self.backgroundColor = [UIColor whiteColor];
self.separatorStyle = UITableViewCellSeparatorStyleNone;
self.showsVerticalScrollIndicator = NO;
[self registerClass:[LMFormCell class] forCellReuseIdentifier:NSStringFromClass([LMFormCell class])];
}
#pragma mark - UITableViewSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.dataArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
LMFormModel *model = [self.dataArray objectAtIndex:indexPath.row];
Class cls = [[LMFormTypeManager manager] getClassWithKey:model.formType];
LMFormCell *cell = [tableView dequeueReusableCellWithIdentifier: NSStringFromClass(cls)];
if (!cell)
{
cell = [tableView dequeueReusableCellWithIdentifier: NSStringFromClass([LMFormCell class])];
}
if ([cell conformsToProtocol:@protocol(LMFormCellProtocol)])
{
[cell configModel:model];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
LMFormModel *model = [self.dataArray objectAtIndex:indexPath.row];
return (model.height > 0) ? model.height : LM_XX_6(50);
}
- (void)registerClassWithDataArray:(NSArray<LMFormModel *> *)dataArray
{
NSMutableSet *set = [NSMutableSet set];
for (LMFormModel *model in self.dataArray)
{
Class cls = [[LMFormTypeManager manager] getClassWithKey:model.formType];
[set addObject:cls];
}
for (Class cls in set)
{
[self registerClass:cls forCellReuseIdentifier:NSStringFromClass(cls)];
}
}
#pragma mark - Setter/Getter
- (void)setDataArray:(NSArray<LMFormModel *> *)dataArray
{
_dataArray = dataArray;
[self registerClassWithDataArray:dataArray];
[self reloadData];
}
@end
================================================
FILE: LMForm/Classes/View/LMPopupView.h
================================================
//
// LMPopupView.h
// LMForm
//
// Created by Zhang on 2019/5/21.
// Copyright © 2019 Maricle. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "LMMarco.h"
NS_ASSUME_NONNULL_BEGIN
typedef void(^LMPopupViewConfirmBlock)(void);
typedef void(^LMPopupViewCancelBlock)(void);
@interface LMPopupView : UIView
+ (void)showPopupViewWithPickView:(UIView *)pickView title:(NSString *)title confirmBlock:(LMPopupViewConfirmBlock)confirmBlock cancelBlock:(LMPopupViewCancelBlock)cancelBlock;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: LMForm/Classes/View/LMPopupView.m
================================================
//
// LMPopupView.m
// LMForm
//
// Created by Zhang on 2019/5/21.
// Copyright © 2019 Maricle. All rights reserved.
//
#import "LMPopupView.h"
#define kLMPopupViewHeight 240
#define kLMTopViewHeight 44
#define kLMButtonWidth 65
@interface LMPopupView ()
@property (nonatomic, strong) UIView *popupView;
@property (nonatomic, strong) UIView *topView;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *cancelButton;
@property (nonatomic, strong) UIButton *confirmButton;
@property (nonatomic, strong) UIView *line;
@property (nonatomic, strong) UIView *pickView;
@property (nonatomic, copy) LMPopupViewConfirmBlock confirmBlock;
@property (nonatomic, copy) LMPopupViewConfirmBlock cancelBlock;
@end
@implementation LMPopupView
+ (void)showPopupViewWithPickView:(UIView *)pickView title:(NSString *)title confirmBlock:(LMPopupViewConfirmBlock)confirmBlock cancelBlock:(LMPopupViewCancelBlock)cancelBlock
{
LMPopupView *popview = [[LMPopupView alloc] initPopupViewWithPickView:pickView title:title confirmBlock:confirmBlock cancelBlock:confirmBlock];
[popview show];
}
- (instancetype)initPopupViewWithPickView:(UIView *)pickView title:(NSString *)title confirmBlock:(LMPopupViewConfirmBlock)confirmBlock cancelBlock:(LMPopupViewCancelBlock)cancelBlock
{
self = [super init];
if (self)
{
self.confirmBlock = confirmBlock;
self.cancelBlock = cancelBlock;
[self createUI];
[self.popupView addSubview:pickView];
self.titleLabel.text = title;
pickView.frame = CGRectMake(0, kLMTopViewHeight, self.frame.size.width, kLMPopupViewHeight - kLMTopViewHeight);
[[UIApplication sharedApplication].keyWindow endEditing:YES];
}
return self;
}
- (void)createUI
{
self.frame = CGRectMake(0, 0, LM_Screen_Width, LM_Screen_Height);
self.backgroundColor = [UIColor clearColor];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(removeSelfView)];
[self addGestureRecognizer:tapGesture];
[[UIApplication sharedApplication].keyWindow addSubview:self];
[self addSubview:self.popupView];
[self.popupView addSubview:self.topView];
[self.topView addSubview:self.cancelButton];
[self.topView addSubview:self.confirmButton];
[self.topView addSubview:self.titleLabel];
[self.topView addSubview:self.line];
}
- (void)show
{
[UIView animateWithDuration:0.3 animations:^{
CGRect rect = self.popupView.frame;
rect.origin.y -= kLMPopupViewHeight;
self.popupView.frame = rect;
}];
}
- (void)removeSelfView
{
[UIView animateWithDuration:0.3 animations:^{
CGRect rect = self.popupView.frame;
rect.origin.y += kLMPopupViewHeight;
self.popupView.frame = rect;
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
#pragma mark - Responce
- (void)cancelButtonAction
{
[self removeSelfView];
if (!self.cancelBlock) return;
self.cancelBlock();
}
- (void)confirmButtonAction
{
[self removeSelfView];
if (!self.confirmBlock) return;
self.confirmBlock();
}
#pragma mark - Setter/Getter
- (UIView *)popupView
{
if (!_popupView)
{
_popupView = [[UIView alloc] initWithFrame:CGRectMake(0, LM_Screen_Height, LM_Screen_Width, kLMPopupViewHeight)];
_popupView.backgroundColor = LM_UIColorFromHEX(0xf9f9fa);
}
return _popupView;
}
- (UIView *)topView
{
if (!_topView)
{
_topView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, kLMTopViewHeight)];
_topView.backgroundColor = LM_UIColorFromHEX(0xFDFDFD);
}
return _topView;
}
- (UIButton *)cancelButton
{
if (!_cancelButton)
{
_cancelButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, kLMButtonWidth, kLMTopViewHeight)];
_cancelButton.backgroundColor = [UIColor clearColor];
[_cancelButton setTitle:@"取消" forState:UIControlStateNormal];
[_cancelButton setTitleColor:LM_UIColorFromHEX(0x464646) forState:UIControlStateNormal];
_cancelButton.titleLabel.font = [UIFont systemFontOfSize:15];
[_cancelButton addTarget:self action:@selector(cancelButtonAction) forControlEvents:UIControlEventTouchUpInside];
}
return _cancelButton;
}
- (UIButton *)confirmButton
{
if (!_confirmButton)
{
_confirmButton = [[UIButton alloc] initWithFrame:CGRectMake((self.frame.size.width - kLMButtonWidth), 0, kLMButtonWidth, kLMTopViewHeight)];
_confirmButton.backgroundColor = [UIColor clearColor];
[_confirmButton setTitle:@"确定" forState:UIControlStateNormal];
[_confirmButton setTitleColor:LM_UIColorFromHEX(0x464646) forState:UIControlStateNormal];
_confirmButton.titleLabel.font = [UIFont systemFontOfSize:15];
[_confirmButton addTarget:self action:@selector(confirmButtonAction) forControlEvents:UIControlEventTouchUpInside];
}
return _confirmButton;
}
- (UILabel *)titleLabel
{
if (!_titleLabel)
{
_titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(kLMButtonWidth + 5, 0, self.frame.size.width - 2 * (kLMButtonWidth + 5) , kLMTopViewHeight)];
_titleLabel.textColor = LM_UIColorFromHEX(0x464646);
_titleLabel.font = [UIFont systemFontOfSize:15];
_titleLabel.textAlignment = NSTextAlignmentCenter;
}
return _titleLabel;
}
- (UIView *)line
{
if (!_line)
{
_line = [[UIView alloc] initWithFrame:CGRectMake(0, kLMTopViewHeight - 0.5, self.frame.size.width, 0.5)];
_line.backgroundColor = LM_UIColorFromHEX(0xf1f1f1);
}
return _line;
}
@end
================================================
FILE: LMForm.podspec
================================================
#
# Be sure to run `pod lib lint LMForm.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'LMForm'
s.version = '0.0.4'
s.summary = 'LMForm 动态配置表单解决方案'
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = <<-DESC
动态配置表单解决方案,支持文本,输入框,选择器,日期选择,输入限制与校验。
DESC
s.homepage = 'https://github.com/MaricleZhang/LMForm'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'MaricleZhang' => 'maricle.zhang@gmail.com' }
s.source = { :git => 'https://github.com/MaricleZhang/LMForm.git', :tag => s.version.to_s }
s.ios.deployment_target = '9.0'
s.source_files = 'LMForm/Classes/**/*'
s.resource_bundles = {
'LMForm' => ['LMForm/Assets/*.png']
}
s.public_header_files = 'LMForm/Classes/**/*.h'
s.dependency 'IQKeyboardManager'
end
================================================
FILE: README.md
================================================
# 更新日志
## 2020-10-22
1.兼容iOS 14.0
2.设置UItextField设置_placeholderLabel.textColor 崩溃处理
3._datePicker.preferredDatePickerStyle 支持13.4
# LMForm
## 背景
通常,将一个页面需要编辑/录入多项信息的页面称为**表单**。iOS 实现表单大多数基于TableView,麻烦的是需要在```UITableViewDataSource```或者```UITableViewDelegate```的代理方法中写很多```if-else```,与cell耦合严重,不易获取用户已编辑的数据。如果表单页面的配置数据从服务端返回,不易实现。
## 1. 介绍
LMForm 是基于MVVM轻量级表单配置框架,把数据和事件整合为一个model,实现cell与model的绑定,只需操作model便可配置表单。项目地址:https://github.com/MaricleZhang/LMForm.git
## 2. 结构

* LMFormTypeManager : 负责管理类型与cellClass
* LMFormModel:cell 的配置信息,事件回调,数据校验回调
* Cell:遵循LMFormCellProtocol协议,实现不同的cell
* LMFormTableView :注册cell类型,通过model,渲染表单
* LMFormValidator:负责对数据的校验
## 3. 功能
1. 支持动态配置model来实现表单。
2. 支持配置文本、输入框、选择器、日期选择、地址选择等。
3. 支持快速提取数据。
4. 支持数据校验,可自定义校验格式。
5. 支持完全自定义cell类型。
## 4. 预览

## 5. 安装
### CocoaPods
在 Podfile 中进行如下导入:
```
pod 'LMForm'
```
安装
```
pod install
```
## 6. 使用
在项目中导入```#import "LMForm.h" ```
### 1. 配置数据源
目前项目中集成以下类型:
|类型 |宏定义|Cell Class |
| ------ | ------ | ------ |
| 文本 | kFormTypeText | LMFormCell |
|输入框 | kFormTypeInput | LMFormInputCell |
|选择器 | kFormTypeSelector | LMFormSelectorCell |
|日期选择器 | kFormTypeDate | LMDateCell |
|地址输入框 | kFormTypeAddressInput | LMFormAddressInputCell |
|可变输入框 | kFormTypeTextView | LMFormTextViewCell |
可根据需求来选择对应的类型,例如输入框的model配置代码如下:
```
// 输入框
- (LMFormModel *)loadInput
{
LMFormModel *model = [LMFormModel new];
model.formType = kFormTypeInput;
model.title = @"手机号";
model.key = @"mobile";
model.value = @"";
model.placeholder = @"请输入手机号";
model.height = LM_XX_6(50);
model.message = @"请输入正确的手机号";
model.limitLength = 11;
model.validateBlock = ^BOOL(LMFormModel * _Nullable model) {
if (![LMFormValidator isMobile:model.value])
{
[LMWindowHud showHud:model.message];
return NO;
}
return YES;
};
return model;
}
```
可根据需要设置UI,输入限制长度,自定义校验等。
### 2. 创建tableview
创建基于LMFormTableView 的tableview,并且赋值渲染数据,表单的创建完成。
```
self.tableView.dataArray = self.dataArray;
```
### 3. 校验数据
提交时,对数据源进行自定义校验,校验的逻辑是对```model.validateBlock```遍历回调。
```
/**
对数据源校验
@param dataArray 数据源
@return 全部校验通过返回YES,否则返回NO。
*/
+ (BOOL)validateDataArray:(NSArray<LMFormModel *> *)dataArray
{
for (LMFormModel * _Nonnull obj in dataArray)
{
if (obj.validateBlock)
{
if (!obj.validateBlock(obj)) return NO;
}
}
return YES;
}
```
### 4. 获取数据源
因为LMFormTableView中的cell与model绑定,只需遍历获取```value```即可。
## 7. Cell 类型的介绍
### 1. LMFormCell
基类cell:其他类型cell继承该cell,主要功能显示文本,不可编辑。可根据需求配置相应UI和数据。
```
- (void)configModel:(LMFormModel *)model
{
self.model = model;
// data
self.titleLabel.text = model.title;
self.textField.placeholder = model.placeholder;
self.textField.text = model.value;
if (model.limitLength)
{
self.textField.limitLength = @(model.limitLength);
}
// UI
self.line.hidden = model.hiddenLine;
self.line.backgroundColor = LM_ObjDefault(model.separatorLineColor, LM_UIColorFromHEX(0xF4F4F4));
self.titleLabel.textColor = LM_ObjDefault(model.leftTextColor, LM_UIColorFromHEX(0x666666));
self.textField.textColor = LM_ObjDefault(model.rightTextColor, LM_UIColorFromHEX(0x333333));
self.titleLabel.font = LM_ObjDefault(model.leftLabelFont, [UIFont systemFontOfSize:LM_XX_6(14)]);
self.titleLabel.font = LM_ObjDefault(model.rightLabelFont, [UIFont systemFontOfSize:LM_XX_6(14)]);
}
```
### 2. LMFormInputCell
输入Cell:可以编辑,可以对输入长度限制,输入的text更新为model的value。
```
- (void)textDidChanged:(UITextField *)textField
{
self.model.value = textField.text;
if (self.model.valueDidChangedBlock)
{
self.model.valueDidChangedBlock(textField.text);
}
}
```
### 3. LMFormSelectorCell
选择器Cell:可以选择对应的item,需要配置数据```NSArray<NSString *> *selectList```,点击cell底部弹窗pickerView
```
- (void)tapSelectedAction
{
LMDefaultPickerView *pickView = [[LMDefaultPickerView alloc] initWithDataArray:self.model.selectList];
@weakify(self)
[LMPopupView showPopupViewWithPickView:pickView title:self.model.placeholder confirmBlock:^{
@strongify(self)
NSString *text = self.model.selectList[pickView.selectIndex];
self.model.value = text;
self.textField.text = text;
if (self.model.valueDidChangedBlock)
{
self.model.valueDidChangedBlock(text);
}
} cancelBlock:^{
}];
}
```
### 4. LMDateCell
选择日期Cell:与选择cell类似,底部弹窗datePickerView。目前只支持年月日。
```
- (void)tapSelectedAction
{
@weakify(self)
[LMPopupView showPopupViewWithPickView:self.datePicker title:self.model.placeholder confirmBlock:^{
@strongify(self)
NSDate *date = self.datePicker.date;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:self.model.dateFormat ?: @"yyyy-MM-dd"];
NSString *text = [formatter stringFromDate:date];
self.model.value = text;
self.textField.text = text;
} cancelBlock:^{
}];
}
```
### 5. LMFormAddressInputCell
地址输入cell:主要用来输入较多数字的信息,分两行显示。
### 6. LMFormTextViewCell
可变输入框cell:cell高度可随textview 高度变化而变化
### 7.自定义cell
#### 创建
LMForm 支持cell的完全自定义,创建的自定义cell需要遵循协议```LMFormCellProtocol```,
协议的方法必须实现,在自定义的cell中实现配置数据。如果LMFormModel中的属性不能满足需求,可以创建LMFormModel分类添加或者继承。个人比较推荐分类的做法。
```
/** 根据model 配置对应的cell */
- (void)configModel:(LMFormModel *)model;
```
#### 注册
LMForm 维护一张注册表建立key与cellClass的一一对应关系,单例```LMFormTypeManager```中的```keyCellTypes```就是这个注册表。注册方法为
```
/**
自定义cell时注册方法,同一种cell只需要注册一次。cls 需继承UITableViewCell
@param cls cell class
@param key key
*/
- (void)registerCellClass:(Class)cls forKey:(NSString *)key;
```
需要注意的是注册表是个字典,**key的定义不能重复**。
## 8. 项目地址:[Demo](https://github.com/MaricleZhang/LMForm.git)
## 9.联系
如有问题请邮件:maricle.zhang@gmail.com
gitextract_2l3nopf2/ ├── .gitignore ├── .travis.yml ├── Example/ │ ├── .gitignore │ ├── LMForm/ │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Images.xcassets/ │ │ │ └── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── LMAppDelegate.h │ │ ├── LMAppDelegate.m │ │ ├── LMCustomCell.h │ │ ├── LMCustomCell.m │ │ ├── LMForm-Info.plist │ │ ├── LMForm-Prefix.pch │ │ ├── LMViewController.h │ │ ├── LMViewController.m │ │ ├── LMWindowHud.h │ │ ├── LMWindowHud.m │ │ ├── en.lproj/ │ │ │ └── InfoPlist.strings │ │ └── main.m │ ├── LMForm.xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── LMForm-Example.xcscheme │ └── Podfile ├── LICENSE ├── LMForm/ │ ├── Assets/ │ │ └── .gitkeep │ └── Classes/ │ ├── .gitkeep │ ├── Category/ │ │ ├── UIImage+Bundle.h │ │ ├── UIImage+Bundle.m │ │ ├── UITextField+LimitLength.h │ │ ├── UITextField+LimitLength.m │ │ ├── UITextView+AutoHeight.h │ │ ├── UITextView+AutoHeight.m │ │ ├── UIView+Extension.h │ │ └── UIView+Extension.m │ ├── Cell/ │ │ ├── LMAddressCell.h │ │ ├── LMAddressCell.m │ │ ├── LMDateCell.h │ │ ├── LMDateCell.m │ │ ├── LMFormAddressInputCell.h │ │ ├── LMFormAddressInputCell.m │ │ ├── LMFormCell.h │ │ ├── LMFormCell.m │ │ ├── LMFormInputCell.h │ │ ├── LMFormInputCell.m │ │ ├── LMFormSelectorCell.h │ │ ├── LMFormSelectorCell.m │ │ ├── LMFormTextViewCell.h │ │ └── LMFormTextViewCell.m │ ├── LMForm.h │ ├── LMFormCellProtocol.h │ ├── LMMarco.h │ ├── Manager/ │ │ ├── LMFormTypeManager.h │ │ └── LMFormTypeManager.m │ ├── Model/ │ │ ├── LMFormModel.h │ │ ├── LMFormModel.m │ │ ├── LMFormValidator.h │ │ └── LMFormValidator.m │ └── View/ │ ├── LMDefaultPickerView.h │ ├── LMDefaultPickerView.m │ ├── LMFormTableView.h │ ├── LMFormTableView.m │ ├── LMPopupView.h │ └── LMPopupView.m ├── LMForm.podspec └── README.md
Condensed preview — 63 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (117K chars).
[
{
"path": ".gitignore",
"chars": 785,
"preview": "# OS X\n.DS_Store\n\n# Xcode\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.pers..."
},
{
"path": ".travis.yml",
"chars": 543,
"preview": "# references:\n# * https://www.objc.io/issues/6-build-tools/travis-ci/\n# * https://github.com/supermarin/xcpretty#usage..."
},
{
"path": "Example/.gitignore",
"chars": 785,
"preview": "# OS X\n.DS_Store\n\n# Xcode\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.pers..."
},
{
"path": "Example/LMForm/Base.lproj/LaunchScreen.storyboard",
"chars": 1844,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3..."
},
{
"path": "Example/LMForm/Base.lproj/Main.storyboard",
"chars": 1857,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3..."
},
{
"path": "Example/LMForm/Images.xcassets/AppIcon.appiconset/Contents.json",
"chars": 1591,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"size\" : \"20x20\",\n \"scale\" : \"2x\"\n },\n {\n \"idiom\"..."
},
{
"path": "Example/LMForm/LMAppDelegate.h",
"chars": 276,
"preview": "//\n// LMAppDelegate.h\n// LMForm\n//\n// Created by MaricleZhang on 05/23/2019.\n// Copyright (c) 2019 MaricleZhang. All..."
},
{
"path": "Example/LMForm/LMAppDelegate.m",
"chars": 2351,
"preview": "//\n// LMAppDelegate.m\n// LMForm\n//\n// Created by MaricleZhang on 05/23/2019.\n// Copyright (c) 2019 MaricleZhang. All..."
},
{
"path": "Example/LMForm/LMCustomCell.h",
"chars": 311,
"preview": "//\n// LMCustomCell.h\n// LMForm_Example\n//\n// Created by Zhang on 2019/5/27.\n// Copyright © 2019 MaricleZhang. All ri..."
},
{
"path": "Example/LMForm/LMCustomCell.m",
"chars": 941,
"preview": "//\n// LMCustomCell.m\n// LMForm_Example\n//\n// Created by Zhang on 2019/5/27.\n// Copyright © 2019 MaricleZhang. All ri..."
},
{
"path": "Example/LMForm/LMForm-Info.plist",
"chars": 1566,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P..."
},
{
"path": "Example/LMForm/LMForm-Prefix.pch",
"chars": 321,
"preview": "//\n// Prefix header\n//\n// The contents of this file are implicitly included at the beginning of every source file.\n//..."
},
{
"path": "Example/LMForm/LMViewController.h",
"chars": 214,
"preview": "//\n// LMViewController.h\n// LMForm\n//\n// Created by MaricleZhang on 05/23/2019.\n// Copyright (c) 2019 MaricleZhang...."
},
{
"path": "Example/LMForm/LMViewController.m",
"chars": 5586,
"preview": "//\n// LMViewController.m\n// LMForm\n//\n// Created by MaricleZhang on 05/23/2019.\n// Copyright (c) 2019 MaricleZhang...."
},
{
"path": "Example/LMForm/LMWindowHud.h",
"chars": 273,
"preview": "//\n// LMWindowHud.h\n// LMForm\n//\n// Created by Zhang on 2019/5/20.\n// Copyright © 2019 Maricle. All rights reserved...."
},
{
"path": "Example/LMForm/LMWindowHud.m",
"chars": 1076,
"preview": "//\n// LMWindowHud.m\n// LMForm\n//\n// Created by Zhang on 2019/5/20.\n// Copyright © 2019 Maricle. All rights reserved...."
},
{
"path": "Example/LMForm/en.lproj/InfoPlist.strings",
"chars": 45,
"preview": "/* Localized versions of Info.plist keys */\n\n"
},
{
"path": "Example/LMForm/main.m",
"chars": 335,
"preview": "//\n// main.m\n// LMForm\n//\n// Created by MaricleZhang on 05/23/2019.\n// Copyright (c) 2019 MaricleZhang. All rights r..."
},
{
"path": "Example/LMForm.xcodeproj/project.pbxproj",
"chars": 22760,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *..."
},
{
"path": "Example/LMForm.xcodeproj/xcshareddata/xcschemes/LMForm-Example.xcscheme",
"chars": 3784,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1010\"\n version = \"1.3\">\n <BuildAction..."
},
{
"path": "Example/Podfile",
"chars": 108,
"preview": "# use_frameworks!\n\nplatform :ios, '9.0'\n\ntarget 'LMForm_Example' do\n pod 'LMForm', :path => '../'\n \nend\n"
},
{
"path": "LICENSE",
"chars": 1080,
"preview": "Copyright (c) 2019 MaricleZhang <jian.zhang@qianli.com>\n\nPermission is hereby granted, free of charge, to any person obt..."
},
{
"path": "LMForm/Assets/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "LMForm/Classes/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "LMForm/Classes/Category/UIImage+Bundle.h",
"chars": 242,
"preview": "//\n// UIImage+Bundle.h\n// LMForm\n//\n// Created by Zhang on 2019/5/23.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNULL_..."
},
{
"path": "LMForm/Classes/Category/UIImage+Bundle.m",
"chars": 632,
"preview": "//\n// UIImage+Bundle.m\n// LMForm\n//\n// Created by Zhang on 2019/5/23.\n//\n\n#import \"UIImage+Bundle.h\"\n\n@implementation..."
},
{
"path": "LMForm/Classes/Category/UITextField+LimitLength.h",
"chars": 496,
"preview": "//\n// UITextField+LimitLength.h\n// CategoryDemo\n//\n// Created by MaricleZhang on 2019/3/20.\n// Copyright © 2019 Mari..."
},
{
"path": "LMForm/Classes/Category/UITextField+LimitLength.m",
"chars": 1866,
"preview": "//\n// UITextField+LimitLength.m\n// CategoryDemo\n//\n// Created by MaricleZhang on 2019/3/20.\n// Copyright © 2019 Mari..."
},
{
"path": "LMForm/Classes/Category/UITextView+AutoHeight.h",
"chars": 480,
"preview": "//\n// UITextView+AutoHeight.h\n// LMForm\n//\n// Created by 张建 on 2019/6/5.\n//\n\n#import <UIKit/UIKit.h>\n\nNS_ASSUME_NONNU..."
},
{
"path": "LMForm/Classes/Category/UITextView+AutoHeight.m",
"chars": 5883,
"preview": "//\n// UITextView+AutoHeight.m\n// LMForm\n//\n// Created by 张建 on 2019/6/5.\n//\n\n#import \"UITextView+AutoHeight.h\"\n#impor..."
},
{
"path": "LMForm/Classes/Category/UIView+Extension.h",
"chars": 734,
"preview": "//\n// UIView+Extension.h\n// LMForm\n//\n// Created by Zhang on 2019/5/20.\n// Copyright © 2019 Maricle. All rights rese..."
},
{
"path": "LMForm/Classes/Category/UIView+Extension.m",
"chars": 2320,
"preview": "//\n// UIView+Extension.m\n// LMForm\n//\n// Created by Zhang on 2019/5/20.\n// Copyright © 2019 Maricle. All rights rese..."
},
{
"path": "LMForm/Classes/Cell/LMAddressCell.h",
"chars": 242,
"preview": "//\n// LMAddressCell.h\n// LMForm\n//\n// Created by Zhang on 2019/5/8.\n// Copyright © 2019 Maricle. All rights reserved..."
},
{
"path": "LMForm/Classes/Cell/LMAddressCell.m",
"chars": 2380,
"preview": "//\n// LMAddressCell.m\n// LMForm\n//\n// Created by Zhang on 2019/5/8.\n// Copyright © 2019 Maricle. All rights reserved..."
},
{
"path": "LMForm/Classes/Cell/LMDateCell.h",
"chars": 236,
"preview": "//\n// LMDateCell.h\n// LMForm\n//\n// Created by Zhang on 2019/5/8.\n// Copyright © 2019 Maricle. All rights reserved.\n/..."
},
{
"path": "LMForm/Classes/Cell/LMDateCell.m",
"chars": 2674,
"preview": "//\n// LMDateCell.m\n// LMForm\n//\n// Created by Zhang on 2019/5/8.\n// Copyright © 2019 Maricle. All rights reserved.\n/..."
},
{
"path": "LMForm/Classes/Cell/LMFormAddressInputCell.h",
"chars": 260,
"preview": "//\n// LMFormAddressInputCell.h\n// LMForm\n//\n// Created by Zhang on 2019/5/5.\n// Copyright © 2019 Maricle. All rights..."
},
{
"path": "LMForm/Classes/Cell/LMFormAddressInputCell.m",
"chars": 727,
"preview": "//\n// LMFormAddressInputCell.m\n// LMForm\n//\n// Created by Zhang on 2019/5/5.\n// Copyright © 2019 Maricle. All rights..."
},
{
"path": "LMForm/Classes/Cell/LMFormCell.h",
"chars": 542,
"preview": "//\n// LMFormCell.h\n// LMForm\n//\n// Created by Zhang on 2019/4/30.\n// Copyright © 2019 Maricle. All rights reserved...."
},
{
"path": "LMForm/Classes/Cell/LMFormCell.m",
"chars": 3429,
"preview": "//\n// LMFormCell.m\n// LMForm\n//\n// Created by Zhang on 2019/4/30.\n// Copyright © 2019 Maricle. All rights reserved...."
},
{
"path": "LMForm/Classes/Cell/LMFormInputCell.h",
"chars": 247,
"preview": "//\n// LMFormInputCell.h\n// LMForm\n//\n// Created by Zhang on 2019/4/30.\n// Copyright © 2019 Maricle. All rights reser..."
},
{
"path": "LMForm/Classes/Cell/LMFormInputCell.m",
"chars": 643,
"preview": "//\n// LMFormInputCell.m\n// LMForm\n//\n// Created by Zhang on 2019/4/30.\n// Copyright © 2019 Maricle. All rights reser..."
},
{
"path": "LMForm/Classes/Cell/LMFormSelectorCell.h",
"chars": 253,
"preview": "//\n// LMFormSelectorCell.h\n// LMForm\n//\n// Created by Zhang on 2019/4/30.\n// Copyright © 2019 Maricle. All rights re..."
},
{
"path": "LMForm/Classes/Cell/LMFormSelectorCell.m",
"chars": 2043,
"preview": "//\n// LMFormSelectorCell.m\n// LMForm\n//\n// Created by Zhang on 2019/4/30.\n// Copyright © 2019 Maricle. All rights re..."
},
{
"path": "LMForm/Classes/Cell/LMFormTextViewCell.h",
"chars": 198,
"preview": "//\n// LMFormTextViewCell.h\n// LMForm\n//\n// Created by 张建 on 2019/6/5.\n//\n\n#import \"LMFormCell.h\"\n\nNS_ASSUME_NONNULL_B..."
},
{
"path": "LMForm/Classes/Cell/LMFormTextViewCell.m",
"chars": 2416,
"preview": "//\n// LMFormTextViewCell.m\n// LMForm\n//\n// Created by 张建 on 2019/6/5.\n//\n\n#import \"LMFormTextViewCell.h\"\n#import \"UIT..."
},
{
"path": "LMForm/Classes/LMForm.h",
"chars": 339,
"preview": "//\n// Header.h\n// LMForm\n//\n// Created by Zhang on 2019/5/23.\n// Copyright © 2019 Maricle. All rights reserved.\n//..."
},
{
"path": "LMForm/Classes/LMFormCellProtocol.h",
"chars": 304,
"preview": "//\n// LMFormCellProtocol.h\n// LMForm\n//\n// Created by Zhang on 2019/5/20.\n// Copyright © 2019 Maricle. All rights re..."
},
{
"path": "LMForm/Classes/LMMarco.h",
"chars": 2032,
"preview": "//\n// LMMarco.h\n// LMForm\n//\n// Created by Zhang on 2019/5/20.\n// Copyright © 2019 Maricle. All rights reserved.\n//..."
},
{
"path": "LMForm/Classes/Manager/LMFormTypeManager.h",
"chars": 902,
"preview": "//\n// LMFormTypeManager.h\n// LMForm\n//\n// Created by Zhang on 2019/5/22.\n// Copyright © 2019 Maricle. All rights res..."
},
{
"path": "LMForm/Classes/Manager/LMFormTypeManager.m",
"chars": 2035,
"preview": "//\n// LMFormTypeManager.m\n// LMForm\n//\n// Created by Zhang on 2019/5/22.\n// Copyright © 2019 Maricle. All rights res..."
},
{
"path": "LMForm/Classes/Model/LMFormModel.h",
"chars": 2196,
"preview": "//\n// LMFormModel.h\n// LMForm\n//\n// Created by Zhang on 2019/4/30.\n// Copyright © 2019 Maricle. All rights reserved...."
},
{
"path": "LMForm/Classes/Model/LMFormModel.m",
"chars": 184,
"preview": "//\n// LMFormModel.m\n// LMForm\n//\n// Created by Zhang on 2019/4/30.\n// Copyright © 2019 Maricle. All rights reserved...."
},
{
"path": "LMForm/Classes/Model/LMFormValidator.h",
"chars": 743,
"preview": "//\n// LMFormValidator.h\n// LMForm\n//\n// Created by Zhang on 2019/5/5.\n// Copyright © 2019 Maricle. All rights reserv..."
},
{
"path": "LMForm/Classes/Model/LMFormValidator.m",
"chars": 1111,
"preview": "//\n// LMFormValidator.m\n// LMForm\n//\n// Created by Zhang on 2019/5/5.\n// Copyright © 2019 Maricle. All rights reserv..."
},
{
"path": "LMForm/Classes/View/LMDefaultPickerView.h",
"chars": 455,
"preview": "//\n// LMDefaultPickerView.h\n// LMForm\n//\n// Created by Zhang on 2019/5/21.\n// Copyright © 2019 Maricle. All rights r..."
},
{
"path": "LMForm/Classes/View/LMDefaultPickerView.m",
"chars": 2069,
"preview": "//\n// LMDefaultPickerView.m\n// LMForm\n//\n// Created by Zhang on 2019/5/21.\n// Copyright © 2019 Maricle. All rights r..."
},
{
"path": "LMForm/Classes/View/LMFormTableView.h",
"chars": 450,
"preview": "//\n// LMFormTableView.h\n// LMForm\n//\n// Created by Zhang on 2019/4/30.\n// Copyright © 2019 Maricle. All rights reser..."
},
{
"path": "LMForm/Classes/View/LMFormTableView.m",
"chars": 2863,
"preview": "//\n// LMFormTableView.m\n// LMForm\n//\n// Created by Zhang on 2019/4/30.\n// Copyright © 2019 Maricle. All rights reser..."
},
{
"path": "LMForm/Classes/View/LMPopupView.h",
"chars": 526,
"preview": "//\n// LMPopupView.h\n// LMForm\n//\n// Created by Zhang on 2019/5/21.\n// Copyright © 2019 Maricle. All rights reserved...."
},
{
"path": "LMForm/Classes/View/LMPopupView.m",
"chars": 5705,
"preview": "//\n// LMPopupView.m\n// LMForm\n//\n// Created by Zhang on 2019/5/21.\n// Copyright © 2019 Maricle. All rights reserved...."
},
{
"path": "LMForm.podspec",
"chars": 1357,
"preview": "#\n# Be sure to run `pod lib lint LMForm.podspec' to ensure this is a\n# valid spec before submitting.\n#\n# Any lines start..."
},
{
"path": "README.md",
"chars": 6030,
"preview": "# 更新日志\n\n## 2020-10-22\n\n1.兼容iOS 14.0\n\n2.设置UItextField设置_placeholderLabel.textColor 崩溃处理\n\n3._datePicker.preferredDatePicke..."
}
]
About this extraction
This page contains the full source code of the MaricleZhang/LMForm GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 63 files (104.2 KB), approximately 31.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.