Repository: RockChanel/SWForm
Branch: master
Commit: 526a711149b4
Files: 476
Total size: 1.7 MB
Directory structure:
gitextract_fysrm9id/
├── README.md
├── SWForm/
│ ├── SWForm.bundle/
│ │ ├── Root.plist
│ │ └── en.lproj/
│ │ └── Root.strings
│ ├── SWForm.h
│ ├── SWFormBaseController.h
│ ├── SWFormBaseController.m
│ ├── SWFormCell/
│ │ ├── SWFormBaseCell.h
│ │ ├── SWFormBaseCell.m
│ │ ├── SWFormImageCell.h
│ │ ├── SWFormImageCell.m
│ │ ├── SWFormInputCell.h
│ │ ├── SWFormInputCell.m
│ │ ├── SWFormSelectCell.h
│ │ ├── SWFormSelectCell.m
│ │ ├── SWFormTextViewInputCell.h
│ │ ├── SWFormTextViewInputCell.m
│ │ ├── SWImageCollectionCell.h
│ │ └── SWImageCollectionCell.m
│ ├── SWFormHandler.h
│ ├── SWFormHandler.m
│ ├── SWFormImageCell+ImageHandle.h
│ ├── SWFormImageCell+ImageHandle.m
│ ├── SWFormItem/
│ │ ├── SWFormItem.h
│ │ ├── SWFormItem.m
│ │ ├── SWFormSectionItem.h
│ │ └── SWFormSectionItem.m
│ ├── SWFormManager/
│ │ ├── NSString+SWForm.h
│ │ ├── NSString+SWForm.m
│ │ ├── SWFormCompat.h
│ │ ├── SWFormCompat.m
│ │ ├── SelwynExpandableTextView.h
│ │ ├── SelwynExpandableTextView.m
│ │ ├── UITextView+TextLimit.h
│ │ └── UITextView+TextLimit.m
│ ├── UIImageView+FormImage.h
│ └── UIImageView+FormImage.m
└── SWFormExample/
├── SWForm/
│ ├── SWForm.bundle/
│ │ ├── Root.plist
│ │ └── en.lproj/
│ │ └── Root.strings
│ ├── SWForm.h
│ ├── SWFormBaseController.h
│ ├── SWFormBaseController.m
│ ├── SWFormCell/
│ │ ├── SWFormBaseCell.h
│ │ ├── SWFormBaseCell.m
│ │ ├── SWFormImageCell.h
│ │ ├── SWFormImageCell.m
│ │ ├── SWFormInputCell.h
│ │ ├── SWFormInputCell.m
│ │ ├── SWFormSelectCell.h
│ │ ├── SWFormSelectCell.m
│ │ ├── SWFormTextViewInputCell.h
│ │ ├── SWFormTextViewInputCell.m
│ │ ├── SWImageCollectionCell.h
│ │ └── SWImageCollectionCell.m
│ ├── SWFormHandler.h
│ ├── SWFormHandler.m
│ ├── SWFormImageCell+ImageHandle.h
│ ├── SWFormImageCell+ImageHandle.m
│ ├── SWFormItem/
│ │ ├── SWFormItem.h
│ │ ├── SWFormItem.m
│ │ ├── SWFormSectionItem.h
│ │ └── SWFormSectionItem.m
│ ├── SWFormManager/
│ │ ├── NSString+SWForm.h
│ │ ├── NSString+SWForm.m
│ │ ├── SWFormCompat.h
│ │ ├── SWFormCompat.m
│ │ ├── SelwynExpandableTextView.h
│ │ ├── SelwynExpandableTextView.m
│ │ ├── UITextView+TextLimit.h
│ │ └── UITextView+TextLimit.m
│ ├── UIImageView+FormImage.h
│ └── UIImageView+FormImage.m
├── SWFormExample/
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Assets.xcassets/
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ ├── SWFormCommonController.h
│ ├── SWFormCommonController.m
│ ├── SWFormExample.pch
│ ├── SWFormInfoController.h
│ ├── SWFormInfoController.m
│ ├── Vendors/
│ │ ├── MBProgressHUD/
│ │ │ ├── .svn/
│ │ │ │ ├── all-wcprops
│ │ │ │ ├── entries
│ │ │ │ ├── prop-base/
│ │ │ │ │ ├── MBProgressHUD.h.svn-base
│ │ │ │ │ └── MBProgressHUD.m.svn-base
│ │ │ │ └── text-base/
│ │ │ │ ├── Factory.h.svn-base
│ │ │ │ ├── Factory.m.svn-base
│ │ │ │ ├── MBProgressHUD+Add.h.svn-base
│ │ │ │ ├── MBProgressHUD+Add.m.svn-base
│ │ │ │ ├── MBProgressHUD.h.svn-base
│ │ │ │ ├── MBProgressHUD.m.svn-base
│ │ │ │ └── MacroDef.h.svn-base
│ │ │ ├── Factory.h
│ │ │ ├── Factory.m
│ │ │ ├── MBProgressHUD+Add.h
│ │ │ ├── MBProgressHUD+Add.m
│ │ │ ├── MBProgressHUD.h
│ │ │ ├── MBProgressHUD.m
│ │ │ └── MacroDef.h
│ │ ├── MWPhotoBrowser/
│ │ │ ├── .svn/
│ │ │ │ ├── all-wcprops
│ │ │ │ └── entries
│ │ │ ├── Classes/
│ │ │ │ ├── .svn/
│ │ │ │ │ ├── all-wcprops
│ │ │ │ │ ├── entries
│ │ │ │ │ └── text-base/
│ │ │ │ │ ├── MWCaptionView.h.svn-base
│ │ │ │ │ ├── MWCaptionView.m.svn-base
│ │ │ │ │ ├── MWCommon.h.svn-base
│ │ │ │ │ ├── MWGridCell.h.svn-base
│ │ │ │ │ ├── MWGridCell.m.svn-base
│ │ │ │ │ ├── MWGridViewController.h.svn-base
│ │ │ │ │ ├── MWGridViewController.m.svn-base
│ │ │ │ │ ├── MWPhoto.h.svn-base
│ │ │ │ │ ├── MWPhoto.m.svn-base
│ │ │ │ │ ├── MWPhotoBrowser.h.svn-base
│ │ │ │ │ ├── MWPhotoBrowser.m.svn-base
│ │ │ │ │ ├── MWPhotoBrowserPrivate.h.svn-base
│ │ │ │ │ ├── MWPhotoProtocol.h.svn-base
│ │ │ │ │ ├── MWTapDetectingImageView.h.svn-base
│ │ │ │ │ ├── MWTapDetectingImageView.m.svn-base
│ │ │ │ │ ├── MWTapDetectingView.h.svn-base
│ │ │ │ │ ├── MWTapDetectingView.m.svn-base
│ │ │ │ │ ├── MWZoomingScrollView.h.svn-base
│ │ │ │ │ └── MWZoomingScrollView.m.svn-base
│ │ │ │ ├── MWCaptionView.h
│ │ │ │ ├── MWCaptionView.m
│ │ │ │ ├── MWCommon.h
│ │ │ │ ├── MWGridCell.h
│ │ │ │ ├── MWGridCell.m
│ │ │ │ ├── MWGridViewController.h
│ │ │ │ ├── MWGridViewController.m
│ │ │ │ ├── MWPhoto.h
│ │ │ │ ├── MWPhoto.m
│ │ │ │ ├── MWPhotoBrowser.h
│ │ │ │ ├── MWPhotoBrowser.m
│ │ │ │ ├── MWPhotoBrowserPrivate.h
│ │ │ │ ├── MWPhotoProtocol.h
│ │ │ │ ├── MWTapDetectingImageView.h
│ │ │ │ ├── MWTapDetectingImageView.m
│ │ │ │ ├── MWTapDetectingView.h
│ │ │ │ ├── MWTapDetectingView.m
│ │ │ │ ├── MWZoomingScrollView.h
│ │ │ │ └── MWZoomingScrollView.m
│ │ │ ├── Libraries/
│ │ │ │ ├── .svn/
│ │ │ │ │ ├── all-wcprops
│ │ │ │ │ └── entries
│ │ │ │ ├── DACircularProgress/
│ │ │ │ │ ├── .svn/
│ │ │ │ │ │ ├── all-wcprops
│ │ │ │ │ │ ├── entries
│ │ │ │ │ │ ├── prop-base/
│ │ │ │ │ │ │ ├── DACircularProgressView.h.svn-base
│ │ │ │ │ │ │ └── DACircularProgressView.m.svn-base
│ │ │ │ │ │ └── text-base/
│ │ │ │ │ │ ├── DACircularProgressView.h.svn-base
│ │ │ │ │ │ └── DACircularProgressView.m.svn-base
│ │ │ │ │ ├── DACircularProgressView.h
│ │ │ │ │ └── DACircularProgressView.m
│ │ │ │ ├── EMSDWebImage/
│ │ │ │ │ ├── .svn/
│ │ │ │ │ │ ├── all-wcprops
│ │ │ │ │ │ ├── entries
│ │ │ │ │ │ ├── prop-base/
│ │ │ │ │ │ │ ├── EMSDImageCache.h.svn-base
│ │ │ │ │ │ │ ├── EMSDImageCache.m.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImageCompat.h.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImageCompat.m.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImageDecoder.h.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImageDecoder.m.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImageDownloader.h.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImageDownloader.m.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImageDownloaderOperation.h.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImageDownloaderOperation.m.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImageManager.h.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImageManager.m.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImageOperation.h.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImagePrefetcher.h.svn-base
│ │ │ │ │ │ │ ├── EMSDWebImagePrefetcher.m.svn-base
│ │ │ │ │ │ │ ├── MKAnnotationView+EMWebCache.h.svn-base
│ │ │ │ │ │ │ ├── MKAnnotationView+EMWebCache.m.svn-base
│ │ │ │ │ │ │ ├── NSData+EMImageContentType.h.svn-base
│ │ │ │ │ │ │ ├── NSData+EMImageContentType.m.svn-base
│ │ │ │ │ │ │ ├── UIButton+EMWebCache.h.svn-base
│ │ │ │ │ │ │ ├── UIButton+EMWebCache.m.svn-base
│ │ │ │ │ │ │ ├── UIImage+EMGIF.h.svn-base
│ │ │ │ │ │ │ ├── UIImage+EMGIF.m.svn-base
│ │ │ │ │ │ │ ├── UIImage+EMMultiFormat.h.svn-base
│ │ │ │ │ │ │ ├── UIImage+EMMultiFormat.m.svn-base
│ │ │ │ │ │ │ ├── UIImage+EMWebP.h.svn-base
│ │ │ │ │ │ │ ├── UIImage+EMWebP.m.svn-base
│ │ │ │ │ │ │ ├── UIImageView+EMHighlightedWebCache.h.svn-base
│ │ │ │ │ │ │ ├── UIImageView+EMHighlightedWebCache.m.svn-base
│ │ │ │ │ │ │ ├── UIImageView+EMWebCache.h.svn-base
│ │ │ │ │ │ │ ├── UIImageView+EMWebCache.m.svn-base
│ │ │ │ │ │ │ ├── UIView+EMWebCacheOperation.h.svn-base
│ │ │ │ │ │ │ └── UIView+EMWebCacheOperation.m.svn-base
│ │ │ │ │ │ └── text-base/
│ │ │ │ │ │ ├── EMSDImageCache.h.svn-base
│ │ │ │ │ │ ├── EMSDImageCache.m.svn-base
│ │ │ │ │ │ ├── EMSDWebImageCompat.h.svn-base
│ │ │ │ │ │ ├── EMSDWebImageCompat.m.svn-base
│ │ │ │ │ │ ├── EMSDWebImageDecoder.h.svn-base
│ │ │ │ │ │ ├── EMSDWebImageDecoder.m.svn-base
│ │ │ │ │ │ ├── EMSDWebImageDownloader.h.svn-base
│ │ │ │ │ │ ├── EMSDWebImageDownloader.m.svn-base
│ │ │ │ │ │ ├── EMSDWebImageDownloaderOperation.h.svn-base
│ │ │ │ │ │ ├── EMSDWebImageDownloaderOperation.m.svn-base
│ │ │ │ │ │ ├── EMSDWebImageManager.h.svn-base
│ │ │ │ │ │ ├── EMSDWebImageManager.m.svn-base
│ │ │ │ │ │ ├── EMSDWebImageOperation.h.svn-base
│ │ │ │ │ │ ├── EMSDWebImagePrefetcher.h.svn-base
│ │ │ │ │ │ ├── EMSDWebImagePrefetcher.m.svn-base
│ │ │ │ │ │ ├── MKAnnotationView+EMWebCache.h.svn-base
│ │ │ │ │ │ ├── MKAnnotationView+EMWebCache.m.svn-base
│ │ │ │ │ │ ├── NSData+EMImageContentType.h.svn-base
│ │ │ │ │ │ ├── NSData+EMImageContentType.m.svn-base
│ │ │ │ │ │ ├── UIButton+EMWebCache.h.svn-base
│ │ │ │ │ │ ├── UIButton+EMWebCache.m.svn-base
│ │ │ │ │ │ ├── UIImage+EMGIF.h.svn-base
│ │ │ │ │ │ ├── UIImage+EMGIF.m.svn-base
│ │ │ │ │ │ ├── UIImage+EMMultiFormat.h.svn-base
│ │ │ │ │ │ ├── UIImage+EMMultiFormat.m.svn-base
│ │ │ │ │ │ ├── UIImage+EMWebP.h.svn-base
│ │ │ │ │ │ ├── UIImage+EMWebP.m.svn-base
│ │ │ │ │ │ ├── UIImageView+EMHighlightedWebCache.h.svn-base
│ │ │ │ │ │ ├── UIImageView+EMHighlightedWebCache.m.svn-base
│ │ │ │ │ │ ├── UIImageView+EMWebCache.h.svn-base
│ │ │ │ │ │ ├── UIImageView+EMWebCache.m.svn-base
│ │ │ │ │ │ ├── UIView+EMWebCacheOperation.h.svn-base
│ │ │ │ │ │ └── UIView+EMWebCacheOperation.m.svn-base
│ │ │ │ │ ├── EMSDImageCache.h
│ │ │ │ │ ├── EMSDImageCache.m
│ │ │ │ │ ├── EMSDWebImageCompat.h
│ │ │ │ │ ├── EMSDWebImageCompat.m
│ │ │ │ │ ├── EMSDWebImageDecoder.h
│ │ │ │ │ ├── EMSDWebImageDecoder.m
│ │ │ │ │ ├── EMSDWebImageDownloader.h
│ │ │ │ │ ├── EMSDWebImageDownloader.m
│ │ │ │ │ ├── EMSDWebImageDownloaderOperation.h
│ │ │ │ │ ├── EMSDWebImageDownloaderOperation.m
│ │ │ │ │ ├── EMSDWebImageManager.h
│ │ │ │ │ ├── EMSDWebImageManager.m
│ │ │ │ │ ├── EMSDWebImageOperation.h
│ │ │ │ │ ├── EMSDWebImagePrefetcher.h
│ │ │ │ │ ├── EMSDWebImagePrefetcher.m
│ │ │ │ │ ├── MKAnnotationView+EMWebCache.h
│ │ │ │ │ ├── MKAnnotationView+EMWebCache.m
│ │ │ │ │ ├── NSData+EMImageContentType.h
│ │ │ │ │ ├── NSData+EMImageContentType.m
│ │ │ │ │ ├── UIButton+EMWebCache.h
│ │ │ │ │ ├── UIButton+EMWebCache.m
│ │ │ │ │ ├── UIImage+EMGIF.h
│ │ │ │ │ ├── UIImage+EMGIF.m
│ │ │ │ │ ├── UIImage+EMMultiFormat.h
│ │ │ │ │ ├── UIImage+EMMultiFormat.m
│ │ │ │ │ ├── UIImage+EMWebP.h
│ │ │ │ │ ├── UIImage+EMWebP.m
│ │ │ │ │ ├── UIImageView+EMHighlightedWebCache.h
│ │ │ │ │ ├── UIImageView+EMHighlightedWebCache.m
│ │ │ │ │ ├── UIImageView+EMWebCache.h
│ │ │ │ │ ├── UIImageView+EMWebCache.m
│ │ │ │ │ ├── UIView+EMWebCacheOperation.h
│ │ │ │ │ └── UIView+EMWebCacheOperation.m
│ │ │ │ └── PSTCollectionView/
│ │ │ │ ├── .svn/
│ │ │ │ │ ├── all-wcprops
│ │ │ │ │ ├── entries
│ │ │ │ │ ├── prop-base/
│ │ │ │ │ │ ├── NSIndexPath+PSTCollectionViewAdditions.h.svn-base
│ │ │ │ │ │ ├── NSIndexPath+PSTCollectionViewAdditions.m.svn-base
│ │ │ │ │ │ ├── PSTCollectionView.h.svn-base
│ │ │ │ │ │ ├── PSTCollectionView.m.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewCell.h.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewCell.m.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewCommon.h.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewController.h.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewController.m.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewData.h.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewData.m.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewFlowLayout.h.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewFlowLayout.m.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewItemKey.h.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewItemKey.m.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewLayout+Internals.h.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewLayout.h.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewLayout.m.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewUpdateItem.h.svn-base
│ │ │ │ │ │ ├── PSTCollectionViewUpdateItem.m.svn-base
│ │ │ │ │ │ ├── PSTGridLayoutInfo.h.svn-base
│ │ │ │ │ │ ├── PSTGridLayoutInfo.m.svn-base
│ │ │ │ │ │ ├── PSTGridLayoutItem.h.svn-base
│ │ │ │ │ │ ├── PSTGridLayoutItem.m.svn-base
│ │ │ │ │ │ ├── PSTGridLayoutRow.h.svn-base
│ │ │ │ │ │ ├── PSTGridLayoutRow.m.svn-base
│ │ │ │ │ │ ├── PSTGridLayoutSection.h.svn-base
│ │ │ │ │ │ └── PSTGridLayoutSection.m.svn-base
│ │ │ │ │ └── text-base/
│ │ │ │ │ ├── NSIndexPath+PSTCollectionViewAdditions.h.svn-base
│ │ │ │ │ ├── NSIndexPath+PSTCollectionViewAdditions.m.svn-base
│ │ │ │ │ ├── PSTCollectionView.h.svn-base
│ │ │ │ │ ├── PSTCollectionView.m.svn-base
│ │ │ │ │ ├── PSTCollectionViewCell.h.svn-base
│ │ │ │ │ ├── PSTCollectionViewCell.m.svn-base
│ │ │ │ │ ├── PSTCollectionViewCommon.h.svn-base
│ │ │ │ │ ├── PSTCollectionViewController.h.svn-base
│ │ │ │ │ ├── PSTCollectionViewController.m.svn-base
│ │ │ │ │ ├── PSTCollectionViewData.h.svn-base
│ │ │ │ │ ├── PSTCollectionViewData.m.svn-base
│ │ │ │ │ ├── PSTCollectionViewFlowLayout.h.svn-base
│ │ │ │ │ ├── PSTCollectionViewFlowLayout.m.svn-base
│ │ │ │ │ ├── PSTCollectionViewItemKey.h.svn-base
│ │ │ │ │ ├── PSTCollectionViewItemKey.m.svn-base
│ │ │ │ │ ├── PSTCollectionViewLayout+Internals.h.svn-base
│ │ │ │ │ ├── PSTCollectionViewLayout.h.svn-base
│ │ │ │ │ ├── PSTCollectionViewLayout.m.svn-base
│ │ │ │ │ ├── PSTCollectionViewUpdateItem.h.svn-base
│ │ │ │ │ ├── PSTCollectionViewUpdateItem.m.svn-base
│ │ │ │ │ ├── PSTGridLayoutInfo.h.svn-base
│ │ │ │ │ ├── PSTGridLayoutInfo.m.svn-base
│ │ │ │ │ ├── PSTGridLayoutItem.h.svn-base
│ │ │ │ │ ├── PSTGridLayoutItem.m.svn-base
│ │ │ │ │ ├── PSTGridLayoutRow.h.svn-base
│ │ │ │ │ ├── PSTGridLayoutRow.m.svn-base
│ │ │ │ │ ├── PSTGridLayoutSection.h.svn-base
│ │ │ │ │ └── PSTGridLayoutSection.m.svn-base
│ │ │ │ ├── NSIndexPath+PSTCollectionViewAdditions.h
│ │ │ │ ├── NSIndexPath+PSTCollectionViewAdditions.m
│ │ │ │ ├── PSTCollectionView.h
│ │ │ │ ├── PSTCollectionView.m
│ │ │ │ ├── PSTCollectionViewCell.h
│ │ │ │ ├── PSTCollectionViewCell.m
│ │ │ │ ├── PSTCollectionViewCommon.h
│ │ │ │ ├── PSTCollectionViewController.h
│ │ │ │ ├── PSTCollectionViewController.m
│ │ │ │ ├── PSTCollectionViewData.h
│ │ │ │ ├── PSTCollectionViewData.m
│ │ │ │ ├── PSTCollectionViewFlowLayout.h
│ │ │ │ ├── PSTCollectionViewFlowLayout.m
│ │ │ │ ├── PSTCollectionViewItemKey.h
│ │ │ │ ├── PSTCollectionViewItemKey.m
│ │ │ │ ├── PSTCollectionViewLayout+Internals.h
│ │ │ │ ├── PSTCollectionViewLayout.h
│ │ │ │ ├── PSTCollectionViewLayout.m
│ │ │ │ ├── PSTCollectionViewUpdateItem.h
│ │ │ │ ├── PSTCollectionViewUpdateItem.m
│ │ │ │ ├── PSTGridLayoutInfo.h
│ │ │ │ ├── PSTGridLayoutInfo.m
│ │ │ │ ├── PSTGridLayoutItem.h
│ │ │ │ ├── PSTGridLayoutItem.m
│ │ │ │ ├── PSTGridLayoutRow.h
│ │ │ │ ├── PSTGridLayoutRow.m
│ │ │ │ ├── PSTGridLayoutSection.h
│ │ │ │ └── PSTGridLayoutSection.m
│ │ │ └── MWPhotoBrowser.bundle/
│ │ │ ├── .svn/
│ │ │ │ ├── all-wcprops
│ │ │ │ └── entries
│ │ │ └── images/
│ │ │ └── .svn/
│ │ │ ├── all-wcprops
│ │ │ ├── entries
│ │ │ ├── prop-base/
│ │ │ │ ├── Checkmark.png.svn-base
│ │ │ │ ├── Checkmark@2x.png.svn-base
│ │ │ │ ├── ImageError.png.svn-base
│ │ │ │ ├── ImageError@2x.png.svn-base
│ │ │ │ ├── ImageSelectedOff.png.svn-base
│ │ │ │ ├── ImageSelectedOff@2x.png.svn-base
│ │ │ │ ├── ImageSelectedOn.png.svn-base
│ │ │ │ ├── ImageSelectedOn@2x.png.svn-base
│ │ │ │ ├── ImageSelectedSmallOff.png.svn-base
│ │ │ │ ├── ImageSelectedSmallOff@2x.png.svn-base
│ │ │ │ ├── ImageSelectedSmallOn.png.svn-base
│ │ │ │ ├── ImageSelectedSmallOn@2x.png.svn-base
│ │ │ │ ├── UIBarButtonItemArrowLeft.png.svn-base
│ │ │ │ ├── UIBarButtonItemArrowLeft@2x.png.svn-base
│ │ │ │ ├── UIBarButtonItemArrowOutlineLeft.png.svn-base
│ │ │ │ ├── UIBarButtonItemArrowOutlineLeft@2x.png.svn-base
│ │ │ │ ├── UIBarButtonItemArrowOutlineRight.png.svn-base
│ │ │ │ ├── UIBarButtonItemArrowOutlineRight@2x.png.svn-base
│ │ │ │ ├── UIBarButtonItemArrowRight.png.svn-base
│ │ │ │ ├── UIBarButtonItemArrowRight@2x.png.svn-base
│ │ │ │ ├── UIBarButtonItemGrid.png.svn-base
│ │ │ │ ├── UIBarButtonItemGrid@2x.png.svn-base
│ │ │ │ ├── UIBarButtonItemGridiOS6.png.svn-base
│ │ │ │ └── UIBarButtonItemGridiOS6@2x.png.svn-base
│ │ │ └── text-base/
│ │ │ ├── Checkmark.png.svn-base
│ │ │ ├── Checkmark@2x.png.svn-base
│ │ │ ├── ImageError.png.svn-base
│ │ │ ├── ImageError@2x.png.svn-base
│ │ │ ├── ImageSelectedOff.png.svn-base
│ │ │ ├── ImageSelectedOff@2x.png.svn-base
│ │ │ ├── ImageSelectedOn.png.svn-base
│ │ │ ├── ImageSelectedOn@2x.png.svn-base
│ │ │ ├── ImageSelectedSmallOff.png.svn-base
│ │ │ ├── ImageSelectedSmallOff@2x.png.svn-base
│ │ │ ├── ImageSelectedSmallOn.png.svn-base
│ │ │ ├── ImageSelectedSmallOn@2x.png.svn-base
│ │ │ ├── UIBarButtonItemArrowLeft.png.svn-base
│ │ │ ├── UIBarButtonItemArrowLeft@2x.png.svn-base
│ │ │ ├── UIBarButtonItemArrowOutlineLeft.png.svn-base
│ │ │ ├── UIBarButtonItemArrowOutlineLeft@2x.png.svn-base
│ │ │ ├── UIBarButtonItemArrowOutlineRight.png.svn-base
│ │ │ ├── UIBarButtonItemArrowOutlineRight@2x.png.svn-base
│ │ │ ├── UIBarButtonItemArrowRight.png.svn-base
│ │ │ ├── UIBarButtonItemArrowRight@2x.png.svn-base
│ │ │ ├── UIBarButtonItemGrid.png.svn-base
│ │ │ ├── UIBarButtonItemGrid@2x.png.svn-base
│ │ │ ├── UIBarButtonItemGridiOS6.png.svn-base
│ │ │ └── UIBarButtonItemGridiOS6@2x.png.svn-base
│ │ └── TZImagePickerController/
│ │ ├── .svn/
│ │ │ ├── all-wcprops
│ │ │ ├── entries
│ │ │ ├── prop-base/
│ │ │ │ ├── UIView+Layout.h.svn-base
│ │ │ │ └── UIView+Layout.m.svn-base
│ │ │ └── text-base/
│ │ │ ├── TZAssetCell.h.svn-base
│ │ │ ├── TZAssetCell.m.svn-base
│ │ │ ├── TZAssetModel.h.svn-base
│ │ │ ├── TZAssetModel.m.svn-base
│ │ │ ├── TZImageManager.h.svn-base
│ │ │ ├── TZImageManager.m.svn-base
│ │ │ ├── TZImagePickerController.h.svn-base
│ │ │ ├── TZImagePickerController.m.svn-base
│ │ │ ├── TZPhotoPickerController.h.svn-base
│ │ │ ├── TZPhotoPickerController.m.svn-base
│ │ │ ├── TZPhotoPreviewCell.h.svn-base
│ │ │ ├── TZPhotoPreviewCell.m.svn-base
│ │ │ ├── TZPhotoPreviewController.h.svn-base
│ │ │ ├── TZPhotoPreviewController.m.svn-base
│ │ │ ├── TZVideoPlayerController.h.svn-base
│ │ │ ├── TZVideoPlayerController.m.svn-base
│ │ │ ├── UIView+Layout.h.svn-base
│ │ │ └── UIView+Layout.m.svn-base
│ │ ├── TZAssetCell.h
│ │ ├── TZAssetCell.m
│ │ ├── TZAssetModel.h
│ │ ├── TZAssetModel.m
│ │ ├── TZImageManager.h
│ │ ├── TZImageManager.m
│ │ ├── TZImagePickerController.bundle/
│ │ │ ├── .svn/
│ │ │ │ ├── all-wcprops
│ │ │ │ ├── entries
│ │ │ │ ├── prop-base/
│ │ │ │ │ ├── MMVideoPreviewPlay@2x.png.svn-base
│ │ │ │ │ ├── MMVideoPreviewPlayHL@2x.png.svn-base
│ │ │ │ │ ├── TableViewArrow@2x.png.svn-base
│ │ │ │ │ ├── VideoSendIcon@2x.png.svn-base
│ │ │ │ │ ├── navi_back@2x.png.svn-base
│ │ │ │ │ ├── photo_def_photoPickerVc@2x.png.svn-base
│ │ │ │ │ ├── photo_def_previewVc@2x.png.svn-base
│ │ │ │ │ ├── photo_number_icon@2x.png.svn-base
│ │ │ │ │ ├── photo_original_def@2x.png.svn-base
│ │ │ │ │ ├── photo_original_sel@2x.png.svn-base
│ │ │ │ │ ├── photo_sel_photoPickerVc@2x.png.svn-base
│ │ │ │ │ ├── photo_sel_previewVc@2x.png.svn-base
│ │ │ │ │ ├── preview_number_icon@2x.png.svn-base
│ │ │ │ │ ├── preview_original_def@2x.png.svn-base
│ │ │ │ │ └── takePicture@2x.png.svn-base
│ │ │ │ └── text-base/
│ │ │ │ ├── MMVideoPreviewPlay@2x.png.svn-base
│ │ │ │ ├── MMVideoPreviewPlayHL@2x.png.svn-base
│ │ │ │ ├── Root.plist.svn-base
│ │ │ │ ├── TZAlbumCell.xib.svn-base
│ │ │ │ ├── TZAssetCell.xib.svn-base
│ │ │ │ ├── TableViewArrow@2x.png.svn-base
│ │ │ │ ├── VideoSendIcon@2x.png.svn-base
│ │ │ │ ├── navi_back@2x.png.svn-base
│ │ │ │ ├── photo_def_photoPickerVc@2x.png.svn-base
│ │ │ │ ├── photo_def_previewVc@2x.png.svn-base
│ │ │ │ ├── photo_number_icon@2x.png.svn-base
│ │ │ │ ├── photo_original_def@2x.png.svn-base
│ │ │ │ ├── photo_original_sel@2x.png.svn-base
│ │ │ │ ├── photo_sel_photoPickerVc@2x.png.svn-base
│ │ │ │ ├── photo_sel_previewVc@2x.png.svn-base
│ │ │ │ ├── preview_number_icon@2x.png.svn-base
│ │ │ │ ├── preview_original_def@2x.png.svn-base
│ │ │ │ └── takePicture@2x.png.svn-base
│ │ │ ├── Root.plist
│ │ │ ├── TZAlbumCell.xib
│ │ │ └── TZAssetCell.xib
│ │ ├── TZImagePickerController.h
│ │ ├── TZImagePickerController.m
│ │ ├── TZPhotoPickerController.h
│ │ ├── TZPhotoPickerController.m
│ │ ├── TZPhotoPreviewCell.h
│ │ ├── TZPhotoPreviewCell.m
│ │ ├── TZPhotoPreviewController.h
│ │ ├── TZPhotoPreviewController.m
│ │ ├── TZVideoPlayerController.h
│ │ ├── TZVideoPlayerController.m
│ │ ├── UIView+Layout.h
│ │ └── UIView+Layout.m
│ ├── ViewController.h
│ ├── ViewController.m
│ └── main.m
└── SWFormExample.xcodeproj/
├── project.pbxproj
└── project.xcworkspace/
├── contents.xcworkspacedata
└── xcshareddata/
└── IDEWorkspaceChecks.plist
================================================
FILE CONTENTS
================================================
================================================
FILE: README.md
================================================
# SWForm
>详细说明戳这里:[iOS 高度封装自适应表单(重构版)](https://www.jianshu.com/p/2d2f742622fa)


`SWForm` 旨在快捷构建表单系统框架,支持大部分表单系统所需条目样式,且包含了图片附件、编辑、新增、详情等功能的拓展.
### Warning
若下载项目之后,运行报错 `incompatible project version`,是由于此项目创建 Xcode 版本为 `9.4.1`。若运行 Xcode 版本低于此版本,解决方法如下:
方法一:更新 Xcode 版本
方法二:右键 `SWFormExample.xcodeproj`, 点击显示包内容,打开 `project.pbxproj` 文件,修改降低 `objectVersion` 即可运行
### 使用方式
使用 `SWForm` 框架只需将 `SWForm`文件夹导入项目中,将所有可定制化配置的接口都已经统一入口,方便使用者根据自身需求实现定制化功能.
本项目中提供了所有定制化接口的示例 `SWFormDemo`,以作为参考。
`SWForm` 根据需求提供了三个接口文件,以方便使用者实现高度定制化,分别为 `UIImageView+FormImage`、`SWFormImageCell+ImageHandle`、`SWFormHandler`.
`UIImageView+FormImage` 针对表单图片条目图片加载提供接口:
```objective-c
- (void)sw_setImageItemWithUrl:(NSURL *)url;
```
`SWFormImageCell+ImageHandle` 提供图片添加接口以及图片预览接口,使用者可以在此接口添加自定义图片选择以及图片预览功能:
```objective-c
/**
选择图片数据回调
@param maxImages 最大可选择图片数
@param currentImages 当前选择图片数
@param completion 选择图片数组回调
*/
- (void)sw_selectImageWithMaxImages:(NSInteger)maxImages currentImages:(NSInteger)currentImages completion:(void(^)(NSArray *selectImages))completion;
/**
图片浏览
@param images 图片数组
@param currentIndex 当前浏览的index
*/
- (void)sw_photoBrowserWithImages:(NSArray *)images currentIndex:(NSInteger)currentIndex;
```
`SWFormHandler` 为数据校验文件,提供了相机权限、相册权限以及表单空数据校验功能:
```objective-c
/**
必选(必填)数据空数据校验,可根据需求定制
@param datas 表单数据源
@param success 必选(必填)数据全部校验成功
@param failure 必选(必填)数据某一项校验失败
*/
+ (void)sw_checkFormNullDataWithWithDatas:(NSArray *)datas success:(void(^)(void))success failure:(void(^)(NSString *error))failure;
/**
校验是否有相机权限
*/
+ (void)sw_checkCameraAuthorizationStatusWithGrand:(void(^)(BOOL granted))permissionGranted;
/**
校验是否有相册权限
*/
+ (void)sw_checkAlbumAuthorizationStatusWithGrand:(void(^)(BOOL granted))permissionGranted;
```
================================================
FILE: SWForm/SWForm.bundle/Root.plist
================================================
StringsTable
Root
PreferenceSpecifiers
Type
PSGroupSpecifier
Title
Group
Type
PSTextFieldSpecifier
Title
Name
Key
name_preference
DefaultValue
IsSecure
KeyboardType
Alphabet
AutocapitalizationType
None
AutocorrectionType
No
Type
PSToggleSwitchSpecifier
Title
Enabled
Key
enabled_preference
DefaultValue
Type
PSSliderSpecifier
Key
slider_preference
DefaultValue
0.5
MinimumValue
0
MaximumValue
1
MinimumValueImage
MaximumValueImage
================================================
FILE: SWForm/SWForm.h
================================================
//
// SWForm.h
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#ifndef SWForm_h
#define SWForm_h
#import "SWFormItem.h"
#import "SWFormSectionItem.h"
#import "SWFormHandler.h"
#import "SWFormCompat.h"
#endif /* SWForm_h */
================================================
FILE: SWForm/SWFormBaseController.h
================================================
//
// SWFormBaseController.h
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
/**
SWFormBaseController 表单基类,所有表单必须继承于BaseController,实现了表单动态配置
*/
@interface SWFormBaseController : UIViewController
/**
表单tableView
*/
@property (nonatomic, strong) UITableView *formTableView;
/**
表单数据源,数据源格式应为 @[SWFormSection..],否则断言会直接崩溃
*/
@property (nonatomic, strong) NSMutableArray *mutableItems;
/**
表单页面初始化方法
@param style 表单tableView样式
*/
- (instancetype)initWithStyle:(UITableViewStyle)style;
@end
================================================
FILE: SWForm/SWFormBaseController.m
================================================
//
// SWFormBaseController.m
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseController.h"
#import "SWForm.h"
#import "SWFormInputCell.h"
#import "SWFormTextViewInputCell.h"
#import "SWFormSelectCell.h"
#import "SWFormImageCell.h"
@interface SWFormBaseController ()
@property (nonatomic, readonly) UITableViewStyle style;
@end
@implementation SWFormBaseController
- (NSMutableArray *)mutableItems {
if (!_mutableItems) {
_mutableItems = [[NSMutableArray alloc]init];
}
return _mutableItems;
}
- (instancetype)initWithStyle:(UITableViewStyle)style {
self = [super init];
if (self) {
_style = style;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
UITableViewController *tableViewController = [[UITableViewController alloc] initWithStyle:_style];
[self addChildViewController:tableViewController];
[tableViewController.view setFrame:self.view.bounds];
// 获取tableViewController的tableView实现表单自动上移
_formTableView = tableViewController.tableView;
_formTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
_formTableView.dataSource = self;
_formTableView.delegate = self;
//_formTableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
_formTableView.showsVerticalScrollIndicator = NO;
_formTableView.showsHorizontalScrollIndicator = NO;
_formTableView.backgroundColor = [UIColor whiteColor];
_formTableView.tableHeaderView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, CGFLOAT_MIN)];
_formTableView.tableFooterView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, CGFLOAT_MIN)];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(fingerTapped:)];
// 加上这句不会影响你 tableview 上的 action (button,cell selected...)
singleTap.cancelsTouchesInView = NO;
[_formTableView addGestureRecognizer:singleTap];
[self.view addSubview:_formTableView];
}
- (void)fingerTapped:(UITapGestureRecognizer *)gestureRecognizer {
[self.view endEditing:YES];
}
#pragma mark -- TableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.mutableItems.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSParameterAssert([self.mutableItems[section] isKindOfClass:[SWFormSectionItem class]]);
SWFormSectionItem *sectionItem = self.mutableItems[section];
return sectionItem.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
NSParameterAssert([sectionItem.items[indexPath.row] isKindOfClass:[SWFormItem class]]);
SWFormItem *item = sectionItem.items[indexPath.row];
SWWeakSelf
// 表单条目类别判断
if (item.itemType == SWFormItemTypeTextViewInput) {
static NSString *textViewInput_cell_id = @"textViewInput_cell_id";
SWFormTextViewInputCell *cell = [tableView textViewInputCellWithId:textViewInput_cell_id];
cell.item = item;
cell.textViewInputCompletion = ^(NSString *text) {
[weakSelf updateTextViewInputWithText:text indexPath:indexPath];
};
return cell;
}
else if (item.itemType == SWFormItemTypeSelect) {
static NSString *select_cell_id = @"select_cell_id";
SWFormSelectCell *cell = [tableView selectCellWithId:select_cell_id];
cell.item = item;
return cell;
}
else if (item.itemType == SWFormItemTypeImage) {
static NSString *image_cell_id = @"image_cell_id";
SWFormImageCell *cell = [tableView imageCellWithId:image_cell_id];
cell.item = item;
cell.imageCompletion = ^(NSArray *images) {
[weakSelf updateImageWithImages:images indexPath:indexPath];
};
return cell;
}
else {
static NSString *input_cell_id = @"input_cell_id";
SWFormInputCell *cell = [tableView inputCellWithId:input_cell_id];
cell.item = item;
cell.inputCompletion = ^(NSString *text) {
[weakSelf updateInputWithText:text indexPath:indexPath];
};
return cell;
}
}
#pragma mark -- TableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
SWFormItem *item = sectionItem.items[indexPath.row];
if (item.itemType == SWFormItemTypeTextViewInput) {
return [SWFormTextViewInputCell heightWithItem:item];
}
else if (item.itemType == SWFormItemTypeSelect) {
return [SWFormSelectCell heightWithItem:item];
}
else if (item.itemType == SWFormItemTypeImage) {
return [SWFormImageCell heightWithItem:item];
}
else {
return [SWFormInputCell heightWithItem:item];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
SWFormItem *item = sectionItem.items[indexPath.row];
if (item.itemType == SWFormItemTypeSelect && item.itemSelectCompletion) {
item.itemSelectCompletion(item);
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
SWFormSectionItem *sectionItem = self.mutableItems[section];
return sectionItem.headerHeight > 0 ? sectionItem.headerHeight:0.01;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
SWFormSectionItem *sectionItem = self.mutableItems[section];
return sectionItem.footerHeight > 0 ? sectionItem.footerHeight:0.01;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
SWFormSectionItem *sectionItem = self.mutableItems[section];
UIView *header = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, sectionItem.headerHeight)];
return sectionItem.headerView ? sectionItem.headerView:header;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
SWFormSectionItem *sectionItem = self.mutableItems[section];
UIView *footer = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, sectionItem.footerHeight)];
return sectionItem.footerView ? sectionItem.footerView:footer;
}
#pragma mark -- 表单条目响应block处理
- (void)updateInputWithText:(NSString *)text indexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
SWFormItem *item = sectionItem.items[indexPath.row];
item.info = text;
}
- (void)updateTextViewInputWithText:(NSString *)text indexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
SWFormItem *item = sectionItem.items[indexPath.row];
item.info = text;
}
- (void)updateImageWithImages:(NSArray *)images indexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
SWFormItem *item = sectionItem.items[indexPath.row];
item.images = images;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
================================================
FILE: SWForm/SWFormCell/SWFormBaseCell.h
================================================
//
// SWFormBaseCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
@class SelwynExpandableTextView;
/**
SWFormBaseCell 表单条目基类,所有表单条目都继承于BaseCell
*/
@interface SWFormBaseCell : UITableViewCell
@property (nonatomic, strong) SelwynExpandableTextView *expandableTextView;
/**
表单标题
*/
@property (nonatomic, strong) UILabel *titleLabel;
/**
表单条目所在的tableView
*/
@property (nonatomic, weak) UITableView *expandableTableView;
@end
================================================
FILE: SWForm/SWFormCell/SWFormBaseCell.m
================================================
//
// SWFormBaseCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseCell.h"
#import "SelwynExpandableTextView.h"
#import "SWFormCompat.h"
@interface SWFormBaseCell()
@end
@implementation SWFormBaseCell
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc]init];
_titleLabel.font = [UIFont systemFontOfSize:SW_TitleFont];
_titleLabel.adjustsFontSizeToFitWidth = YES;
[self.contentView addSubview:_titleLabel];
}
return _titleLabel;
}
- (SelwynExpandableTextView *)expandableTextView {
if (!_expandableTextView) {
_expandableTextView = [[SelwynExpandableTextView alloc]init];
_expandableTextView.delegate = self;
_expandableTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
_expandableTextView.textContainer.lineFragmentPadding = 0;
_expandableTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
_expandableTextView.backgroundColor = [UIColor clearColor];
_expandableTextView.font = [UIFont systemFontOfSize:SW_InfoFont];
_expandableTextView.scrollEnabled = NO;
_expandableTextView.autocorrectionType = UITextAutocorrectionTypeNo;
_expandableTextView.layoutManager.allowsNonContiguousLayout = NO;
_expandableTextView.showsVerticalScrollIndicator = NO;
_expandableTextView.showsHorizontalScrollIndicator = NO;
[self.contentView addSubview:_expandableTextView];
}
return _expandableTextView;
}
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
@end
================================================
FILE: SWForm/SWFormCell/SWFormImageCell.h
================================================
//
// SWFormImageCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseCell.h"
@class SWFormItem;
/**
图片选择或删除block
@param images 当前已存在图片数组
*/
typedef void(^SWImageCompletion)(NSArray *images);
/**
SWFormImageCell 表单图片选择条目
*/
@interface SWFormImageCell : SWFormBaseCell
@property (nonatomic, strong) SWFormItem *item;
@property (nonatomic, copy) SWImageCompletion imageCompletion;
+ (CGFloat)heightWithItem:(SWFormItem *)item;
@end
@interface UITableView (SWFormImageCell)
- (SWFormImageCell *)imageCellWithId:(NSString *)cellId;
@end
================================================
FILE: SWForm/SWFormCell/SWFormImageCell.m
================================================
//
// SWFormImageCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormImageCell.h"
#import "SWFormItem.h"
#import "SWImageCollectionCell.h"
#import "SWFormCompat.h"
#import "SWFormImageCell+ImageHandle.h"
static NSString *image_cell_id = @"image_cell_id";
static CGFloat const SW_ImageWidth = 80.0f;
static NSInteger const SW_RowImageCount = 4;
@interface SWFormImageCell()
@property (nonatomic, strong) UIImageView *icon;
@property (nonatomic, strong) UICollectionView *imageCollection;
@property (nonatomic, strong) UIButton *selectBtn;
@property (nonatomic, strong) NSMutableArray *mutableImages;
@end
@implementation SWFormImageCell
- (void)setItem:(SWFormItem *)item {
_item = item;
self.titleLabel.attributedText = item.attributedTitle;
self.selectBtn.enabled = item.editable;
self.mutableImages = [NSMutableArray arrayWithArray:item.images];
}
- (void)layoutSubviews {
[super layoutSubviews];
self.titleLabel.frame = CGRectMake(SW_EdgeMargin, SW_EdgeMargin, SW_TitleWidth, SW_TitleHeight);
self.icon.frame = CGRectMake(self.frame.size.width - 38, SW_EdgeMargin + 2, 23, SW_TitleHeight - 4);
self.selectBtn.frame = CGRectMake(0, 0, self.frame.size.width, self.item.defaultHeight);
if (self.mutableImages.count > 0) {
self.imageCollection.frame = CGRectMake(0, self.item.defaultHeight, self.frame.size.width, self.frame.size.height - self.item.defaultHeight);
self.imageCollection.hidden = NO;
[self.imageCollection reloadData];
}
else {
self.imageCollection.frame = CGRectZero;
self.imageCollection.hidden = YES;
}
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.mutableImages.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
SWImageCollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:image_cell_id forIndexPath:indexPath];
cell.image = self.mutableImages[indexPath.item];
cell.editable = self.item.editable;
cell.deleteImageCompletion = ^{
[self.mutableImages removeObjectAtIndex:indexPath.item];
[self sw_reloadData];
};
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self sw_photoBrowserWithImages:self.mutableImages currentIndex:indexPath.item];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(SW_ImageWidth, SW_ImageWidth);
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(0, 10, 10, 10);
}
- (void)selectImageAction {
if (self.mutableImages.count >= self.item.maxImageCount) {
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:[NSString stringWithFormat:@"最多选择%ld张附件",(long)self.item.maxImageCount] message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alertView show];
return;
}
__weak typeof(self) weakSelf = self;
// 选择图片事件函数
[self sw_selectImageWithMaxImages:self.item.maxImageCount currentImages:self.mutableImages.count completion:^(NSArray *selectImages) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf.mutableImages addObjectsFromArray:selectImages];
[strongSelf sw_reloadData];
}];
}
#pragma mark -- 刷新当前图片数据
- (void)sw_reloadData {
if (self.imageCompletion) {
self.imageCompletion(self.mutableImages);
}
[UIView performWithoutAnimation:^{
[self.expandableTableView beginUpdates];
[self.expandableTableView endUpdates];
}];
}
+ (CGFloat)heightWithItem:(SWFormItem *)item {
NSInteger rows = item.images.count%SW_RowImageCount > 0 ? item.images.count/SW_RowImageCount+1:item.images.count/SW_RowImageCount;
return item.images.count > 0 ? item.defaultHeight + 10*(rows+1) + rows*SW_ImageWidth:item.defaultHeight;
}
#pragma mark -- 懒加载实现
- (NSMutableArray *)mutableImages {
if (!_mutableImages) {
_mutableImages = [[NSMutableArray alloc]init];
}
return _mutableImages;
}
- (UICollectionView *)imageCollection {
if (!_imageCollection) {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init];
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
layout.minimumInteritemSpacing = 10;
layout.minimumLineSpacing = 10;
_imageCollection = [[UICollectionView alloc]initWithFrame:CGRectZero collectionViewLayout:layout];
_imageCollection.backgroundColor = [UIColor whiteColor];
_imageCollection.delegate = self;
_imageCollection.dataSource = self;
_imageCollection.hidden = YES;
_imageCollection.scrollEnabled = NO;
_imageCollection.showsVerticalScrollIndicator = NO;
_imageCollection.showsHorizontalScrollIndicator = NO;
[_imageCollection registerClass:[SWImageCollectionCell class] forCellWithReuseIdentifier:image_cell_id];
[self.contentView addSubview:_imageCollection];
}
return _imageCollection;
}
- (UIButton *)selectBtn {
if (!_selectBtn) {
_selectBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[_selectBtn addTarget:self action:@selector(selectImageAction) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:_selectBtn];
}
return _selectBtn;
}
- (UIImageView *)icon {
if (!_icon) {
_icon = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"SWForm.bundle/SWImageIcon"]];
[self.contentView addSubview:_icon];
}
return _icon;
}
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
@end
@implementation UITableView (SWFormImageCell)
- (SWFormImageCell *)imageCellWithId:(NSString *)cellId
{
SWFormImageCell *cell = [self dequeueReusableCellWithIdentifier:cellId];
if (cell == nil) {
cell = [[SWFormImageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.expandableTableView = self;
}
return cell;
}
@end
================================================
FILE: SWForm/SWFormCell/SWFormInputCell.h
================================================
//
// SWFormInputCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseCell.h"
@class SWFormItem;
/**
输入内容block
@param text 当前输入内容
*/
typedef void(^SWInputCompletion)(NSString *text);
/**
SWFormInputCell 表单输入条目,标题居左,详情居右,支持单行与多行输入
*/
@interface SWFormInputCell : SWFormBaseCell
/**
条目配置参数
*/
@property (nonatomic, strong) SWFormItem *item;
@property (nonatomic, copy) SWInputCompletion inputCompletion;
/**
获取条目高度
*/
+ (CGFloat)heightWithItem:(SWFormItem *)item;
@end
/**
SWFormInputCell 对于UITableView的分类,实现SWFormInputCell初始化
*/
@interface UITableView (SWFormInputCell)
- (SWFormInputCell *)inputCellWithId:(NSString *)cellId;
@end
================================================
FILE: SWForm/SWFormCell/SWFormInputCell.m
================================================
//
// SWFormInputCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormInputCell.h"
#import "SWFormItem.h"
#import "SWFormCompat.h"
#import "SelwynExpandableTextView.h"
#import "UITextView+TextLimit.h"
#import "NSString+SWForm.h"
@interface SWFormInputCell()
@end
@implementation SWFormInputCell
- (void)setItem:(SWFormItem *)item {
_item = item;
self.titleLabel.attributedText = item.attributedTitle;
self.expandableTextView.text = [item.info addUnit:item.unit];
self.expandableTextView.attributedPlaceholder = item.attributedPlaceholder;
self.expandableTextView.editable = item.editable;
self.expandableTextView.keyboardType = item.keyboardType;
self.accessoryType = UITableViewCellAccessoryNone;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.titleLabel.frame = CGRectMake(SW_EdgeMargin, SW_EdgeMargin, SW_TitleWidth, SW_TitleHeight);
CGFloat newHeight = [SWFormInputCell heightWithItem:self.item];
self.expandableTextView.frame = CGRectMake(SW_TitleWidth + 2*SW_EdgeMargin, SW_EdgeMargin, SW_SCRREN_WIDTH - (SW_TitleWidth + 3*SW_EdgeMargin), newHeight - 2*SW_EdgeMargin);
}
- (void)textViewDidBeginEditing:(UITextView *)textView {
self.expandableTextView.text = self.item.info;
}
- (void)textViewDidChange:(UITextView *)textView {
if (self.item.maxInputLength > 0) {
// 限制输入字数
[self.expandableTextView textLimitWithMaxLength:self.item.maxInputLength];
}
if (self.inputCompletion) {
self.inputCompletion(self.expandableTextView.text);
}
// 防止输入时表单因刷新动画抖动
[UIView performWithoutAnimation:^{
[self.expandableTableView beginUpdates];
[self.expandableTableView endUpdates];
}];
}
- (void)textViewDidEndEditing:(UITextView *)textView {
self.expandableTextView.text = [self.item.info addUnit:self.item.unit];
}
+ (CGFloat)heightWithItem:(SWFormItem *)item {
CGFloat infoHeight = [item.info sizeWithFontSize:SW_InfoFont maxSize:CGSizeMake(SW_SCRREN_WIDTH - (SW_TitleWidth + 3*SW_EdgeMargin), MAXFLOAT)].height;
return MAX(item.defaultHeight, infoHeight + 2*SW_EdgeMargin);
}
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
@end
@implementation UITableView (SWFormInputCell)
- (SWFormInputCell *)inputCellWithId:(NSString *)cellId
{
SWFormInputCell *cell = [self dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[SWFormInputCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.expandableTableView = self;
}
return cell;
}
@end
================================================
FILE: SWForm/SWFormCell/SWFormSelectCell.h
================================================
//
// SWFormSelectCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseCell.h"
@class SWFormItem;
/**
SWFormSelectCell 表单选择条目
*/
@interface SWFormSelectCell : SWFormBaseCell
@property (nonatomic, strong) SWFormItem *item;
+ (CGFloat)heightWithItem:(SWFormItem *)item;
@end
@interface UITableView (SWFormSelectCell)
- (SWFormSelectCell *)selectCellWithId:(NSString *)cellId;
@end
================================================
FILE: SWForm/SWFormCell/SWFormSelectCell.m
================================================
//
// SWFormSelectCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormSelectCell.h"
#import "SWFormItem.h"
#import "SelwynExpandableTextView.h"
#import "SWFormCompat.h"
#import "NSString+SWForm.h"
@implementation SWFormSelectCell
- (void)setItem:(SWFormItem *)item {
_item = item;
self.titleLabel.attributedText = item.attributedTitle;
self.expandableTextView.text = [item.info addUnit:item.unit];
self.expandableTextView.attributedPlaceholder = item.attributedPlaceholder;
self.expandableTextView.editable = item.editable;
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.titleLabel.frame = CGRectMake(SW_EdgeMargin, (self.item.defaultHeight - SW_TitleHeight)/2, SW_TitleWidth, SW_TitleHeight);
self.expandableTextView.userInteractionEnabled = NO;
CGFloat newHeight = [SWFormSelectCell heightWithItem:self.item];
self.expandableTextView.frame = CGRectMake(SW_TitleWidth + 2*SW_EdgeMargin, SW_EdgeMargin, SW_SCRREN_WIDTH - (SW_TitleWidth + 2*SW_EdgeMargin + 30), newHeight - 2*SW_EdgeMargin);
}
+ (CGFloat)heightWithItem:(SWFormItem *)item {
CGFloat infoHeight = [item.info sizeWithFontSize:SW_InfoFont maxSize:CGSizeMake(SW_SCRREN_WIDTH - SW_TitleWidth - 2*SW_EdgeMargin - 30, MAXFLOAT)].height;
return MAX(item.defaultHeight, infoHeight + 2*SW_EdgeMargin);
}
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
@end
@implementation UITableView (SWFormSelectCell)
- (SWFormSelectCell *)selectCellWithId:(NSString *)cellId
{
SWFormSelectCell *cell = [self dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[SWFormSelectCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.expandableTableView = self;
}
return cell;
}
@end
================================================
FILE: SWForm/SWFormCell/SWFormTextViewInputCell.h
================================================
//
// SWFormTextViewInputCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseCell.h"
@class SWFormItem;
typedef void(^SWTextViewInputCompletion)(NSString *text);
/**
SWFormTextViewInputCell 表单输入条目,标题居上,详情居下,支持单行与多行输入
*/
@interface SWFormTextViewInputCell : SWFormBaseCell
@property (nonatomic, strong) SWFormItem *item;
@property (nonatomic, copy) SWTextViewInputCompletion textViewInputCompletion;
+ (CGFloat)heightWithItem:(SWFormItem *)item;
@end
@interface UITableView (SWFormTextViewInputCell)
- (SWFormTextViewInputCell *)textViewInputCellWithId:(NSString *)cellId;
@end
================================================
FILE: SWForm/SWFormCell/SWFormTextViewInputCell.m
================================================
//
// SWFormTextViewInputCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormTextViewInputCell.h"
#import "SWFormItem.h"
#import "SelwynExpandableTextView.h"
#import "SWFormCompat.h"
#import "UITextView+TextLimit.h"
#import "NSString+SWForm.h"
@interface SWFormTextViewInputCell()
@end
@implementation SWFormTextViewInputCell
- (void)setItem:(SWFormItem *)item {
_item = item;
self.titleLabel.attributedText = item.attributedTitle;
self.expandableTextView.text = [item.info addUnit:item.unit];
self.expandableTextView.attributedPlaceholder = item.attributedPlaceholder;
self.expandableTextView.editable = item.editable;
self.expandableTextView.keyboardType = item.keyboardType;
self.expandableTextView.currentLength = item.info.length;
self.expandableTextView.showLength = item.showLength;
self.expandableTextView.maxLength = item.maxInputLength;
self.accessoryType = UITableViewCellAccessoryNone;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.titleLabel.frame = CGRectMake(SW_EdgeMargin, SW_EdgeMargin, SW_SCRREN_WIDTH - 2*SW_EdgeMargin, SW_TitleHeight);
//重置 expandableTextView 内边距
self.expandableTextView.textContainerInset = UIEdgeInsetsMake(SW_EdgeMargin, SW_EdgeMargin, SW_EdgeMargin, SW_EdgeMargin);
self.expandableTextView.backgroundColor = SW_TEXTVIEW_BACKGROUNDCOLOR;
CGFloat newHeight = [SWFormTextViewInputCell heightWithItem:self.item];
self.expandableTextView.frame = CGRectMake(SW_EdgeMargin, CGRectGetMaxY(self.titleLabel.frame) + SW_EdgeMargin, SW_SCRREN_WIDTH - 2*SW_EdgeMargin, newHeight - 3*SW_EdgeMargin - SW_TitleHeight);
}
- (void)textViewDidBeginEditing:(UITextView *)textView {
self.expandableTextView.text = self.item.info;
self.expandableTextView.currentLength = self.expandableTextView.text.length;
}
- (void)textViewDidChange:(UITextView *)textView {
if (self.item.maxInputLength > 0) {
[self.expandableTextView textLimitWithMaxLength:self.item.maxInputLength];
}
self.expandableTextView.currentLength = self.expandableTextView.text.length;
if (self.textViewInputCompletion) {
self.textViewInputCompletion(self.expandableTextView.text);
}
[UIView performWithoutAnimation:^{
[self.expandableTableView beginUpdates];
[self.expandableTableView endUpdates];
}];
}
- (void)textViewDidEndEditing:(UITextView *)textView {
self.expandableTextView.currentLength = self.expandableTextView.text.length;
self.expandableTextView.text = [self.item.info addUnit:self.item.unit];
}
+ (CGFloat)heightWithItem:(SWFormItem *)item {
CGFloat infoHeight = [item.info sizeWithFontSize:SW_InfoFont maxSize:CGSizeMake(SW_SCRREN_WIDTH - 4*SW_EdgeMargin, MAXFLOAT)].height;
return MAX(item.defaultHeight, infoHeight + SW_TitleHeight + 5*SW_EdgeMargin);
}
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
@end
@implementation UITableView (SWFormTextViewInputCell)
- (SWFormTextViewInputCell *)textViewInputCellWithId:(NSString *)cellId
{
SWFormTextViewInputCell *cell = [self dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[SWFormTextViewInputCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.expandableTableView = self;
}
return cell;
}
@end
================================================
FILE: SWForm/SWFormCell/SWImageCollectionCell.h
================================================
//
// SWImageCollectionCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
typedef void(^SWDeleteImageCompletion)(void);
/**
表单图片条目图片展示
*/
@interface SWImageCollectionCell : UICollectionViewCell
/**
当前图片删除操作block
*/
@property (nonatomic, copy) SWDeleteImageCompletion deleteImageCompletion;
/**
当前图片,支持UIImage、NSURL、NSString(图片URLString)类型
*/
@property (nonatomic, strong) id image;
/**
当前图片是否可编辑
*/
@property (nonatomic, assign) BOOL editable;
@end
================================================
FILE: SWForm/SWFormCell/SWImageCollectionCell.m
================================================
//
// SWImageCollectionCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWImageCollectionCell.h"
#import "SWFormCompat.h"
#import "UIImageView+FormImage.h"
/**
删除图标宽高
*/
static CGFloat const SWDeleteIconWidth = 22.0f;
/**
删除按钮宽高,大于图标宽高,增强交互性
*/
static CGFloat const SWDeleteBtnWidth = 25.0f;
@interface SWImageCollectionCell()
@property (nonatomic, strong) UIImageView *currentImageView;
@property (nonatomic, strong) UIImageView *deleteIcon;
@property (nonatomic, strong) UIButton *deleteBtn;
@end
@implementation SWImageCollectionCell
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor whiteColor];
self.currentImageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, SWDeleteIconWidth/2, self.frame.size.width - SWDeleteIconWidth/2, self.frame.size.height - SWDeleteIconWidth/2)];
self.currentImageView.clipsToBounds = YES;
self.currentImageView.contentMode = UIViewContentModeScaleAspectFill;
[self.contentView addSubview:self.currentImageView];
self.deleteIcon = [[UIImageView alloc]initWithFrame:CGRectMake(self.frame.size.width - SWDeleteIconWidth, 0, SWDeleteIconWidth, SWDeleteIconWidth)];
self.deleteIcon.image = [UIImage imageNamed:SW_DeleteIcon];
[self.contentView addSubview:self.deleteIcon];
self.deleteBtn = [UIButton buttonWithType:UIButtonTypeSystem];
self.deleteBtn.frame = CGRectMake(self.frame.size.width - SWDeleteBtnWidth, 0, SWDeleteBtnWidth, SWDeleteBtnWidth);
[self.deleteBtn addTarget:self action:@selector(deleteAction) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:self.deleteBtn];
}
return self;
}
- (void)deleteAction {
if (self.deleteImageCompletion) {
self.deleteImageCompletion();
}
}
- (void)setEditable:(BOOL)editable {
_editable = editable;
self.deleteBtn.hidden = !editable;
self.deleteIcon.hidden = !editable;
}
#pragma mark -- 设置当前图片
- (void)setImage:(id)image {
_image = image;
if ([image isKindOfClass:[UIImage class]]) {
self.currentImageView.image = image;
}
else if ([image isKindOfClass:[NSURL class]]) {
[self.currentImageView sw_setImageItemWithUrl:image];
}
else if ([image isKindOfClass:[NSString class]]) {
[self.currentImageView sw_setImageItemWithUrl:[NSURL URLWithString:image]];
}
else {
self.currentImageView.image = [UIImage imageNamed:SW_PlaceholderImage];
}
}
@end
================================================
FILE: SWForm/SWFormHandler.h
================================================
//
// SWFormHandler.h
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
/**
SWFormHandler 数据校验文件,包含相机权限、相册权限以及表单空数据校验
*/
@interface SWFormHandler : NSObject
/**
必选(必填)数据空数据校验,可根据需求定制
@param datas 表单数据源
@param success 必选(必填)数据全部校验成功
@param failure 必选(必填)数据某一项校验失败
*/
+ (void)sw_checkFormNullDataWithWithDatas:(NSArray *)datas success:(void(^)(void))success failure:(void(^)(NSString *error))failure;
/**
校验是否有相机权限
*/
+ (void)sw_checkCameraAuthorizationStatusWithGrand:(void(^)(BOOL granted))permissionGranted;
/**
校验是否有相册权限
*/
+ (void)sw_checkAlbumAuthorizationStatusWithGrand:(void(^)(BOOL granted))permissionGranted;
@end
================================================
FILE: SWForm/SWFormHandler.m
================================================
//
// SWFormHandler.m
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormHandler.h"
#import
#import
#import
#import "SWFormItem.h"
#import "SWFormSectionItem.h"
@implementation SWFormHandler
+ (void)sw_checkFormNullDataWithWithDatas:(NSArray *)datas success:(void(^)(void))success failure:(void(^)(NSString *error))failure {
for (int sec = 0; sec < datas.count; sec++) {
SWFormSectionItem *sectionItem = datas[sec];
for (int row = 0; row < sectionItem.items.count; row++) {
SWFormItem *rowItem = sectionItem.items[row];
if (rowItem.required) {
if (rowItem.itemType == SWFormItemTypeInput || rowItem.itemType == SWFormItemTypeTextViewInput) {
if (!rowItem.info || [rowItem.info isEqualToString:@""]) {
failure([NSString stringWithFormat:@"请输入%@", rowItem.title]);
return;
}
}
else if (rowItem.itemType == SWFormItemTypeSelect) {
if (!rowItem.info || [rowItem.info isEqualToString:@""]) {
failure([NSString stringWithFormat:@"请选择%@", rowItem.title]);
return;
}
}
else if (rowItem.itemType == SWFormItemTypeImage) {
if (!rowItem.images || rowItem.images.count == 0) {
failure([NSString stringWithFormat:@"请选择%@", rowItem.title]);
return;
}
}
}
}
}
success();
}
#pragma mark -- 校验相机相册权限
+ (void)sw_checkCameraAuthorizationStatusWithGrand:(void(^)(BOOL granted))permissionGranted
{
AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
switch (videoAuthStatus) {
// 已授权
case AVAuthorizationStatusAuthorized:
{
permissionGranted(YES);
}
break;
// 未询问用户是否授权
case AVAuthorizationStatusNotDetermined:
{
// 提示用户授权
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
permissionGranted(granted);
}];
}
break;
// 用户拒绝授权或权限受限
case AVAuthorizationStatusRestricted:
case AVAuthorizationStatusDenied:
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"请在”设置-隐私-相机”选项中,允许访问你的相机" message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alert show];
permissionGranted(NO);
}
break;
default:
break;
}
}
+ (void)sw_checkAlbumAuthorizationStatusWithGrand:(void(^)(BOOL granted))permissionGranted {
PHAuthorizationStatus photoAuthStatus = [PHPhotoLibrary authorizationStatus];
switch (photoAuthStatus) {
// 已授权
case PHAuthorizationStatusAuthorized:
{
permissionGranted(YES);
}
break;
// 未询问用户是否授权
case PHAuthorizationStatusNotDetermined:
{
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
permissionGranted(status == PHAuthorizationStatusAuthorized);
}];
}
break;
// 用户拒绝授权或权限受限
case PHAuthorizationStatusRestricted:
case PHAuthorizationStatusDenied:
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"请在”设置-隐私-相片”选项中,允许访问你的相册" message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alert show];
permissionGranted(NO);
}
break;
default:
break;
}
}
@end
================================================
FILE: SWForm/SWFormImageCell+ImageHandle.h
================================================
//
// SWFormImageCell+ImageHandle.h
// SWFormDemo
//
// Created by zijin on 2018/6/1.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormImageCell.h"
@interface SWFormImageCell (ImageHandle)
/**
选择图片数据回调
@param maxImages 最大可选择图片数
@param currentImages 当前选择图片数
@param completion 选择图片数组回调
*/
- (void)sw_selectImageWithMaxImages:(NSInteger)maxImages currentImages:(NSInteger)currentImages completion:(void(^)(NSArray *selectImages))completion;
/**
图片浏览
@param images 图片数组
@param currentIndex 当前浏览的index
*/
- (void)sw_photoBrowserWithImages:(NSArray *)images currentIndex:(NSInteger)currentIndex;
@end
================================================
FILE: SWForm/SWFormImageCell+ImageHandle.m
================================================
//
// SWFormImageCell+ImageHandle.m
// SWFormDemo
//
// Created by zijin on 2018/6/1.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormImageCell+ImageHandle.h"
#import "objc/runtime.h"
static char SW_MAXIMAGECOUNT;
static char SW_CURRENTIMAGECOUNT;
static char SW_SELECTCOMPLETION;
static char SW_BROWSERPHOTOS;
typedef void(^SWSelectImageCompletion)(NSArray *images);
@interface UITableViewCell()
@end
@implementation SWFormImageCell (ImageHandle)
- (void)sw_selectImageWithMaxImages:(NSInteger)maxImages currentImages:(NSInteger)currentImages completion:(void(^)(NSArray *selectImages))completion {
[self sw_setMaxImagesCount:maxImages];
[self sw_setCurrentImagesCount:currentImages];
[self sw_setSelectImagesCompletion:completion];
}
- (void)sw_photoBrowserWithImages:(NSArray *)images currentIndex:(NSInteger)currentIndex {
}
#pragma mark -- 设置图片选择block
- (void)sw_setSelectImagesCompletion:(SWSelectImageCompletion)completion {
objc_setAssociatedObject(self, &SW_SELECTCOMPLETION, completion, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (SWSelectImageCompletion)sw_selectImagesCompletion {
return objc_getAssociatedObject(self, &SW_SELECTCOMPLETION);
}
#pragma mark -- 设置当前获取的图片
- (void)sw_setBrowserPhotos:(NSArray *)photos {
objc_setAssociatedObject(self, &SW_BROWSERPHOTOS, photos, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSArray *)sw_browserPhotos {
return objc_getAssociatedObject(self, &SW_BROWSERPHOTOS);
}
#pragma mark -- 设置最大图片数
- (void)sw_setMaxImagesCount:(NSInteger)count {
objc_setAssociatedObject(self, &SW_MAXIMAGECOUNT, @(count), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSInteger)sw_maxImagesCount {
return [objc_getAssociatedObject(self, &SW_MAXIMAGECOUNT) integerValue];
}
#pragma mark -- 设置当前图片数
- (void)sw_setCurrentImagesCount:(NSInteger)count {
objc_setAssociatedObject(self, &SW_CURRENTIMAGECOUNT, @(count), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSInteger)sw_currentImagesCount {
return [objc_getAssociatedObject(self, &SW_CURRENTIMAGECOUNT) integerValue];
}
#pragma mark -- 获取当前View所在的ViewController
- (UIViewController *)superViewController:(UIView *)view{
UIResponder *responder = view;
while ((responder = [responder nextResponder]))
if ([responder isKindOfClass: [UIViewController class]])
return (UIViewController *)responder;
return nil;
}
@end
================================================
FILE: SWForm/SWFormItem/SWFormItem.h
================================================
//
// SWFormItem.h
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
#import
@class SWFormItem;
typedef NS_ENUM(NSInteger, SWFormItemType) {
/**
表单条目可单行或多行输入(标题居左)
*/
SWFormItemTypeInput = 0,
/**
表单条目可选择(标题居左)
*/
SWFormItemTypeSelect = 1,
/**
表单条目可多行输入(标题居上)
*/
SWFormItemTypeTextViewInput = 2,
/**
表单条目包含图片选择
*/
SWFormItemTypeImage = 3,
};
typedef NS_ENUM(NSInteger, SWFormItemUnitType) {
SWFormItemUnitTypeNone = 0, // 无单位
SWFormItemUnitTypeYuan, // 元
SWFormItemUnitTypeYear, // 年
SWFormItemUnitTypeMillion, // 万元
SWFormItemUnitTypeCustom, // 自定义单位
};
typedef void(^SWItemSelectCompletion)(SWFormItem *item);
/**
SWFormItem 主要对表单条目提供动态配置属性
*/
@interface SWFormItem : NSObject
/**
表单条目缺省高度,缺省值为44.0f, 可根据需求设置
*/
@property (nonatomic, assign) CGFloat defaultHeight;
/**
表单条目类型
*/
@property (nonatomic, assign) SWFormItemType itemType;
/**
表单条目标题,表单标题为单行显示,尽可能简短,若标题太长,会牺牲字体大小以达到显示完全的效果
*/
@property (nonatomic, copy, nonnull) NSString *title;
@property (nonatomic, strong, nonnull) NSAttributedString *attributedTitle;
/**
表单条目详情
*/
@property (nonatomic, copy, nullable) NSString *info;
/**
表单条目占位字符
*/
@property (nonatomic, copy, nullable) NSString *placeholder;
@property (nonatomic, strong, nullable) NSAttributedString *attributedPlaceholder;
/**
是否显示表单条目占位字符 YES:显示 NO:不显示 --- 新增 default is YES;详情 default is NO
*/
@property (nonatomic, assign) BOOL showPlaceholder;
/**
图片附件条目图片数组,支持UIImage、NSURL、NSString(图片URLString)类型元素
*/
@property (nonatomic, strong, nullable) NSArray *images;
/**
images 图片数组中类型筛选出为UIImage的数组子集,以实现图片上传筛选
*/
@property (nonatomic, strong, readonly) NSArray *selectImages;
/**
表单条目键盘类型
*/
@property (nonatomic, assign) UIKeyboardType keyboardType;
/**
表单条目是否可编辑 YES:可编辑 NO:不可编辑
*/
@property (nonatomic, assign) BOOL editable;
/**
表单条目是否必填(必选) YES:必填(必选) NO:可填(可选)
*/
@property (nonatomic, assign) BOOL required;
/**
SWFormItemTypeInput 以及 SWFormItemTypeTextViewInput 类别中表示最大输入字数
0 表示无限制
*/
@property (nonatomic, assign) NSUInteger maxInputLength;
/**
SWFormItemTypeImage 类别中表示最大选择图片数
*/
@property (nonatomic, assign) NSUInteger maxImageCount;
/**
表单条目点击选择事件block
*/
@property (nonatomic, copy, nullable) SWItemSelectCompletion itemSelectCompletion;
/**
条目附带单位
*/
@property (nonatomic, copy, nullable) NSString *unit;
/**
表单条目单位类别
*/
@property (nonatomic, assign) SWFormItemUnitType itemUnitType;
/**
是否显示当前字数
只在 SWFormItemTypeTextViewInput 类型下有效,若无最大字数限制,则只显示字数; 若有字数限制,则显示 "当前字数/最大字数"
*/
@property (nonatomic, assign) BOOL showLength;
@end
/**
SWFormItem_Add 快捷构建新增表单条目
@param title 标题
@param info 详情
@param itemType 条目类别
@param editable 是否可编辑
@param required 是否必填
@param keyboardType 键盘类别
*/
FOUNDATION_EXPORT SWFormItem *SWFormItem_Add(NSString * _Nonnull title, NSString * _Nullable info, SWFormItemType itemType, BOOL editable, BOOL required, UIKeyboardType keyboardType);
/**
SWFormItem_Info 快捷构建详情表单条目
*/
FOUNDATION_EXPORT SWFormItem *SWFormItem_Info(NSString * _Nonnull title, NSString * _Nullable info, SWFormItemType itemType);
================================================
FILE: SWForm/SWFormItem/SWFormItem.m
================================================
//
// SWFormItem.m
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormItem.h"
#import "SWFormCompat.h"
static NSString *const SWUnitYuan = @"元";
static NSString *const SWUnitYear = @"年";
static NSString *const SWUnitMillion = @"万元";
@interface SWFormItem()
+ (instancetype)sw_itemWithTitle:(NSString *)title info:(NSString *)info itemType:(SWFormItemType)itemType editable:(BOOL)editable required:(BOOL)required keyboardType:(UIKeyboardType)keyboardType;
+ (instancetype)sw_itemWithTitle:(NSString *)title info:(NSString *)info itemType:(SWFormItemType)itemType;
@end
inline SWFormItem *SWFormItem_Add(NSString * _Nonnull title, NSString * _Nullable info, SWFormItemType itemType, BOOL editable, BOOL required, UIKeyboardType keyboardType) {
return [SWFormItem sw_itemWithTitle:title info:info itemType:itemType editable:editable required:required keyboardType:keyboardType];
}
inline SWFormItem *SWFormItem_Info(NSString * _Nonnull title, NSString * _Nullable info, SWFormItemType itemType) {
return [SWFormItem sw_itemWithTitle:title info:info itemType:itemType];
}
@implementation SWFormItem
+ (instancetype)sw_itemWithTitle:(NSString *)title info:(NSString *)info itemType:(SWFormItemType)itemType editable:(BOOL)editable required:(BOOL)required keyboardType:(UIKeyboardType)keyboardType {
return [[self alloc]initWithTitle:title info:info itemType:itemType editable:editable required:required keyboardType:keyboardType images:nil showPlaceholder:YES];
}
+ (instancetype)sw_itemWithTitle:(NSString *)title info:(NSString *)info itemType:(SWFormItemType)itemType {
return [[self alloc]initWithTitle:title info:info itemType:itemType editable:NO required:NO keyboardType:UIKeyboardTypeDefault images:nil showPlaceholder:NO];
}
- (instancetype)initWithTitle:(NSString *)title info:(NSString *)info itemType:(SWFormItemType)itemType editable:(BOOL)editable required:(BOOL)required keyboardType:(UIKeyboardType)keyboardType images:(NSArray *)images showPlaceholder:(BOOL)showPlaceholder{
self = [super init];
if (self) {
self.itemUnitType = SWFormItemUnitTypeNone;
self.maxInputLength = SW_GlobalMaxInputLength;
self.maxImageCount = SW_GlobalMaxImages;
self.title = title;
self.info = info;
self.itemType = itemType;
self.editable = editable;
self.required = required;
self.keyboardType = keyboardType;
self.images = images;
self.showPlaceholder = showPlaceholder;
[self sw_setDefaultHeight:itemType];
[self sw_setPlaceholderWithShow:showPlaceholder itemType:itemType];
[self sw_setAttributedTitleWithRequired:required title:title itemType:itemType];
}
return self;
}
#pragma mark -- 根据表单条目类型设置条目缺省高度
- (void)sw_setDefaultHeight:(SWFormItemType)itemType {
self.defaultHeight = itemType == SWFormItemTypeTextViewInput ? SW_DefaultTextViewItemHeight:SW_DefaultItemHeight;
}
#pragma mark -- 设置是否显示输入框占位字符
- (void)sw_setPlaceholderWithShow:(BOOL)show itemType:(SWFormItemType)itemType {
if (!show) {
self.placeholder = @"";
return;
}
switch (itemType) {
case SWFormItemTypeInput:
case SWFormItemTypeTextViewInput:
{
self.placeholder = @"请输入";
}
break;
case SWFormItemTypeSelect:
{
self.placeholder = @"请选择";
}
break;
default:
self.placeholder = @"";
break;
}
}
#pragma mark -- 设置标题显示
- (void)sw_setAttributedTitleWithRequired:(BOOL)required title:(NSString *)title itemType:(SWFormItemType)itemType{
if (required) {
if (SW_TitleShowType == SWTitleShowTypeDefault) {
switch (self.itemType) {
case SWFormItemTypeInput:
case SWFormItemTypeTextViewInput:
{
title = [NSString stringWithFormat:@"%@(必填)", title];
}
break;
case SWFormItemTypeSelect:
case SWFormItemTypeImage:
{
title = [NSString stringWithFormat:@"%@(必选)", title];
}
break;
default:
break;
}
}
else if (SW_TitleShowType == SWTitleShowTypeRedStarFront) {
title = [NSString stringWithFormat:@"*%@", title];
}
else if (SW_TitleShowType == SWTitleShowTypeRedStarBack) {
title = [NSString stringWithFormat:@"%@*", title];
}
}
NSMutableAttributedString *attributedTitle = [[NSMutableAttributedString alloc]initWithString:title attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:SW_TitleFont], NSForegroundColorAttributeName:SW_TITLECOLOR}];
if (required) {
if (SW_TitleShowType == SWTitleShowTypeRedStarFront) {
[attributedTitle addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 1)];
}
else if (SW_TitleShowType == SWTitleShowTypeRedStarBack) {
[attributedTitle addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(title.length - 1, 1)];
}
}
_attributedTitle = attributedTitle;
}
#pragma mark -- 重写get方法
- (NSArray *)selectImages {
NSMutableArray *tempImages = [NSMutableArray array];
for (id temp in self.images) {
if ([temp isKindOfClass:[UIImage class]]) {
[tempImages addObject:temp];
}
}
return tempImages;
}
#pragma mark -- 重写属性set方法,防止单独改变属性无响应效果
/**
设置表单条目附带单位
*/
- (void)setItemUnitType:(SWFormItemUnitType)itemUnitType {
NSString *tempUnit = self.unit ?: @"";
switch (itemUnitType) {
case SWFormItemUnitTypeNone:
{
tempUnit = @"";
}
break;
case SWFormItemUnitTypeYuan:
{
tempUnit = SWUnitYuan;
}
break;
case SWFormItemUnitTypeYear:
{
tempUnit = SWUnitYear;
}
break;
case SWFormItemUnitTypeMillion:
{
tempUnit = SWUnitMillion;
}
default:
break;
}
_unit = tempUnit;
}
/**
根据单位设置单元格单位类别,防止单位与单元格式不一致
*/
- (void)setUnit:(NSString *)unit {
_unit = unit;
if ([unit isEqualToString:@""]) {
_itemUnitType = SWFormItemUnitTypeNone;
}
else if (unit == SWUnitYuan) {
_itemUnitType = SWFormItemUnitTypeYuan;
}
else if (unit == SWUnitYear) {
_itemUnitType = SWFormItemUnitTypeYear;
}
else if (unit == SWUnitMillion) {
_itemUnitType = SWFormItemUnitTypeMillion;
}
else {
_itemUnitType = SWFormItemUnitTypeCustom;
}
}
- (void)setImages:(NSArray *)images {
_images = images;
[self selectImages];
}
- (void)setTitle:(NSString *)title {
_title = title;
[self sw_setAttributedTitleWithRequired:self.required title:title itemType:self.itemType];
}
- (void)setRequired:(BOOL)required {
_required = required;
[self sw_setAttributedTitleWithRequired:required title:self.title itemType:self.itemType];
}
- (void)setItemType:(SWFormItemType)itemType {
_itemType = itemType;
[self sw_setDefaultHeight:itemType];
[self sw_setAttributedTitleWithRequired:self.required title:self.title itemType:itemType];
[self sw_setPlaceholderWithShow:self.showPlaceholder itemType:itemType];
}
- (void)setShowPlaceholder:(BOOL)showPlaceholder {
_showPlaceholder = showPlaceholder;
[self sw_setPlaceholderWithShow:showPlaceholder itemType:self.itemType];
}
- (void)setPlaceholder:(NSString *)placeholder {
_placeholder = placeholder;
NSAttributedString *attributedPlaceholder = [[NSAttributedString alloc]initWithString:placeholder ?: @"" attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:SW_InfoFont],NSForegroundColorAttributeName:SW_PLACEHOLDERCOLOR}];
_attributedPlaceholder = attributedPlaceholder;
}
- (void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholder {
_attributedPlaceholder = attributedPlaceholder ?: [[NSAttributedString alloc]initWithString:@""];
}
@end
================================================
FILE: SWForm/SWFormItem/SWFormSectionItem.h
================================================
//
// SWFormSectionItem.h
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
#import
/**
SWFormSectionItem 主要对表单section条目提供动态配置属性
*/
@interface SWFormSectionItem : NSObject
/**
表单section包含的条目集合
*/
@property (nonatomic, strong, nonnull) NSArray *items;
/**
表单section头部高度
*/
@property (nonatomic, assign) CGFloat headerHeight;
/**
表单section尾部高度
*/
@property (nonatomic, assign) CGFloat footerHeight;
/**
表单section头部视图
*/
@property (nonatomic, strong, nullable) UIView *headerView;
/**
表单section尾部视图
*/
@property (nonatomic, strong, nullable) UIView *footerView;
@end
/**
SWSectionItem 快捷构建表单section条目
@param items 表单section包含的条目集合
*/
FOUNDATION_EXPORT SWFormSectionItem *SWSectionItem(NSArray * _Nonnull items);
================================================
FILE: SWForm/SWFormItem/SWFormSectionItem.m
================================================
//
// SWFormSectionItem.m
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormSectionItem.h"
@interface SWFormSectionItem()
+ (instancetype)sw_sectionItem:(NSArray *)items;
@end
inline SWFormSectionItem *SWSectionItem(NSArray * _Nonnull items) {
return [SWFormSectionItem sw_sectionItem:items];
}
@implementation SWFormSectionItem
+ (instancetype)sw_sectionItem:(NSArray *)items {
return [[self alloc]initWithItems:items];
}
- (instancetype)initWithItems:(NSArray *)items {
self = [super init];
if (self) {
self.items = items;
}
return self;
}
@end
================================================
FILE: SWForm/SWFormManager/NSString+SWForm.h
================================================
//
// NSString+SWForm.h
// SWFormDemo
//
// Created by zijin on 2018/6/26.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
@interface NSString (SWForm)
/**
字符串添加单位
@param unit 单位
*/
- (NSString *)addUnit:(NSString *)unit;
/**
获取字符串的Size大小
@param fontSize 字体大小
@param maxSize 最大显示Size
*/
- (CGSize)sizeWithFontSize:(CGFloat)fontSize maxSize:(CGSize)maxSize;
@end
================================================
FILE: SWForm/SWFormManager/NSString+SWForm.m
================================================
//
// NSString+SWForm.m
// SWFormDemo
//
// Created by zijin on 2018/6/26.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "NSString+SWForm.h"
@implementation NSString (SWForm)
- (NSString *)addUnit:(NSString *)unit {
if ([self isEqualToString:@""] || [unit isEqualToString:@""]) {
return self;
}
return [NSString stringWithFormat:@"%@ %@", self, unit];
}
- (CGSize)sizeWithFontSize:(CGFloat)fontSize maxSize:(CGSize)maxSize {
return [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]} context:nil].size;
}
@end
================================================
FILE: SWForm/SWFormManager/SWFormCompat.h
================================================
//
// SWFormCompat.h
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
/**
SWFormCompat 主要配置表单涉及的相关常量参数,可根据需求修改配置
*/
#import
/**
必选条目标题呈现形式类别
*/
typedef NS_ENUM(NSInteger, SWTitleShowType) {
/**
默认呈现形式,如: 标题(必填)
*/
SWTitleShowTypeDefault,
/**
标题前部加红色*,如: *标题
*/
SWTitleShowTypeRedStarFront,
/**
标题后部加红色*,如: 标题*
*/
SWTitleShowTypeRedStarBack,
/**
仅显示标题
*/
SWTitleShowTypeOnlyTitle,
};
/**
表单标题字体大小,缺省为16
*/
extern CGFloat const SW_TitleFont;
/**
表单详情字体大小,缺省为16
*/
extern CGFloat const SW_InfoFont;
/**
表单条目边缘距离,缺省为10.0f
*/
extern CGFloat const SW_EdgeMargin;
/**
表单标题宽度,缺省为100.0f
*/
extern CGFloat const SW_TitleWidth;
/**
表单标题高度,缺省为24.0f
*/
extern CGFloat const SW_TitleHeight;
/**
表单条目初始高度,缺省为44.0f,SWFormItemTypeTextViewInput 类型缺省高度为200,为确保显示正常,设置值>= 44
*/
extern CGFloat const SW_DefaultItemHeight;
extern CGFloat const SW_DefaultTextViewItemHeight;
/**
表单标题显示类别,缺省为 SWTitleShowTypeRedStarFront
*/
extern NSInteger const SW_TitleShowType;
/**
表单输入字数限制,缺省为200
0 表示无限制
*/
extern NSUInteger const SW_GlobalMaxInputLength;
/**
表单选择图片附件数,缺省为6
*/
extern NSUInteger const SW_GlobalMaxImages;
/**
表单图片条目图片加载失败占位图
*/
extern NSString *const SW_PlaceholderImage;
/**
表单附件删除图标
*/
extern NSString *const SW_DeleteIcon;
/**
表单TextView字数提示文字大小
*/
extern CGFloat const SW_LengHintFont;
/**
表单条目输入框占位符字体颜色
*/
#define SW_PLACEHOLDERCOLOR [UIColor colorWithRed:187/255.0 green:187/255.0 blue:187/255.0 alpha:1/1.0]
/**
SWFormItemTypeTextViewInput 类别 TextView 背景颜色
*/
#define SW_TEXTVIEW_BACKGROUNDCOLOR [UIColor colorWithRed:250/255.0 green:250/255.0 blue:250/255.0 alpha:1/1.0]
/**
表单条目标题颜色
*/
#define SW_TITLECOLOR [UIColor colorWithRed:51/255.0 green:51/255.0 blue:51/255.0 alpha:1/1.0]
/**
获取屏幕宽度
*/
#define SW_SCRREN_WIDTH [UIScreen mainScreen].bounds.size.width
/// 弱引用
#define SWWeakSelf __weak typeof(self) weakSelf = self;
================================================
FILE: SWForm/SWFormManager/SWFormCompat.m
================================================
//
// SWFormCompat.m
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormCompat.h"
// 表单标题字体大小
CGFloat const SW_TitleFont = 16.0f;
// 表单详情字体大小
CGFloat const SW_InfoFont = 16.0f;
// 表单条目边缘距离
CGFloat const SW_EdgeMargin = 10.0f;
// 表单标题宽度
CGFloat const SW_TitleWidth = 80.0f;
// 表单标题高度
CGFloat const SW_TitleHeight = 24.0f;
// 表单条目初始高度
CGFloat const SW_DefaultItemHeight = 44.0f;
CGFloat const SW_DefaultTextViewItemHeight = 200.0f;
// 表单标题显示类别
NSInteger const SW_TitleShowType = SWTitleShowTypeRedStarFront;
// 表单输入字数限制
NSUInteger const SW_GlobalMaxInputLength = 200;
// 表单选择图片附件数
NSUInteger const SW_GlobalMaxImages = 6;
// 表单TextView字数提示文字大小
CGFloat const SW_LengHintFont = 12;
// 表单图片条目图片加载失败占位图
NSString *const SW_PlaceholderImage = @"SWForm.bundle/SWPlaceholderIcon";
// 表单附件删除图标
NSString *const SW_DeleteIcon = @"SWForm.bundle/SWDeleteIcon";
================================================
FILE: SWForm/SWFormManager/SelwynExpandableTextView.h
================================================
//
// SelwynExpandableTextView.h
// SelwynFormDemo
//
// Created by BSW on 2017/6/24.
// Copyright © 2017年 selwyn. All rights reserved.
//
#import
/**
SelwynExpandableTextView 可高度自适应的UITextView
*/
@interface SelwynExpandableTextView : UITextView
@property (nonatomic, assign) NSUInteger currentLength;
@property (nonatomic, assign) NSUInteger maxLength;
@property (nonatomic, assign) BOOL showLength;
@property (copy, nonatomic) IBInspectable NSString *placeholder;
@property (copy, nonatomic) NSAttributedString *attributedPlaceholder;
@property (nonatomic) IBInspectable double fadeTime;
@property (retain, nonatomic) UIColor *placeholderTextColor UI_APPEARANCE_SELECTOR;
@end
================================================
FILE: SWForm/SWFormManager/SelwynExpandableTextView.m
================================================
//
// SelwynExpandableTextView.m
// SelwynFormDemo
//
// Created by BSW on 2017/6/24.
// Copyright © 2017年 selwyn. All rights reserved.
//
#import "SelwynExpandableTextView.h"
#import "SWFormCompat.h"
#define HAS_TEXT_CONTAINER [self respondsToSelector:@selector(textContainer)]
#define HAS_TEXT_CONTAINER_INSETS(x) [(x) respondsToSelector:@selector(textContainerInset)]
@interface SelwynExpandableTextView()
@property (strong, nonatomic) UITextView *_placeholderTextView;
@property (nonatomic, strong) UILabel *lenthLab;
@end
static NSString * const kAttributedPlaceholderKey = @"attributedPlaceholder";
static NSString * const kPlaceholderKey = @"placeholder";
static NSString * const kFontKey = @"font";
static NSString * const kAttributedTextKey = @"attributedText";
static NSString * const kTextKey = @"text";
static NSString * const kExclusionPathsKey = @"exclusionPaths";
static NSString * const kLineFragmentPaddingKey = @"lineFragmentPadding";
static NSString * const kTextContainerInsetKey = @"textContainerInset";
static NSString * const kTextAlignmentKey = @"textAlignment";
@implementation SelwynExpandableTextView
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self preparePlaceholder];
}
return self;
}
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer
{
self = [super initWithFrame:frame textContainer:textContainer];
if (self) {
[self preparePlaceholder];
}
return self;
}
#else
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self preparePlaceholder];
}
return self;
}
#endif
- (void)preparePlaceholder
{
NSAssert(!self._placeholderTextView, @"placeholder has been prepared already: %@", self._placeholderTextView);
// the label which displays the placeholder
// needs to inherit some properties from its parent text view
// account for standard UITextViewPadding
CGRect frame = self.bounds;
self._placeholderTextView = [[UITextView alloc] initWithFrame:frame];
self._placeholderTextView.opaque = NO;
self._placeholderTextView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
self._placeholderTextView.backgroundColor = [UIColor clearColor];
self._placeholderTextView.textColor = [UIColor colorWithWhite:0.7f alpha:0.7f];
self._placeholderTextView.editable = NO;
self._placeholderTextView.scrollEnabled = YES;
self._placeholderTextView.userInteractionEnabled = NO;
self._placeholderTextView.font = self.font;
self._placeholderTextView.isAccessibilityElement = NO;
self._placeholderTextView.contentOffset = self.contentOffset;
self._placeholderTextView.contentInset = self.contentInset;
if ([self._placeholderTextView respondsToSelector:@selector(setSelectable:)]) {
self._placeholderTextView.selectable = NO;
}
if (HAS_TEXT_CONTAINER) {
self._placeholderTextView.textContainer.exclusionPaths = self.textContainer.exclusionPaths;
self._placeholderTextView.textContainer.lineFragmentPadding = self.textContainer.lineFragmentPadding;
}
if (HAS_TEXT_CONTAINER_INSETS(self)) {
self._placeholderTextView.textContainerInset = self.textContainerInset;
}
if (_attributedPlaceholder) {
self._placeholderTextView.attributedText = _attributedPlaceholder;
} else if (_placeholder) {
self._placeholderTextView.text = _placeholder;
}
[self setPlaceholderVisibleForText:self.text];
self.clipsToBounds = YES;
// some observations
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self selector:@selector(textDidChange:)
name:UITextViewTextDidChangeNotification object:self];
[defaultCenter addObserver:self selector:@selector(textDidBeginEditing:) name:UITextViewTextDidBeginEditingNotification object:self];
[self addObserver:self forKeyPath:kAttributedPlaceholderKey
options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:kPlaceholderKey
options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:kFontKey
options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:kAttributedTextKey
options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:kTextKey
options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:kTextAlignmentKey
options:NSKeyValueObservingOptionNew context:nil];
if (HAS_TEXT_CONTAINER) {
[self.textContainer addObserver:self forKeyPath:kExclusionPathsKey
options:NSKeyValueObservingOptionNew context:nil];
[self.textContainer addObserver:self forKeyPath:kLineFragmentPaddingKey
options:NSKeyValueObservingOptionNew context:nil];
}
if (HAS_TEXT_CONTAINER_INSETS(self)) {
[self addObserver:self forKeyPath:kTextContainerInsetKey
options:NSKeyValueObservingOptionNew context:nil];
}
}
- (void)setPlaceholder:(NSString *)placeholderText
{
_placeholder = [placeholderText copy];
_attributedPlaceholder = [[NSAttributedString alloc] initWithString:placeholderText];
[self resizePlaceholderFrame];
}
- (void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholderText
{
_placeholder = attributedPlaceholderText.string;
_attributedPlaceholder = [attributedPlaceholderText copy];
[self resizePlaceholderFrame];
}
- (void)layoutSubviews
{
[super layoutSubviews];
self._placeholderTextView.textAlignment = self.textAlignment;
[self resizePlaceholderFrame];
[self resizeLengthFrame];
}
- (void)setCurrentLength:(NSUInteger)currentLength {
_currentLength = currentLength;
[self resizeLengthFrame];
}
- (void)resizePlaceholderFrame
{
CGRect frame = self._placeholderTextView.frame;
frame.size = self.bounds.size;
self._placeholderTextView.frame = frame;
}
- (void)resizeLengthFrame {
if (self.showLength) {
NSString *length = [NSString stringWithFormat:@"%lu", self.currentLength];
if (self.maxLength > 0) {
length = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)self.currentLength, (unsigned long)self.maxLength];
}
self.lenthLab.text = length;
self.lenthLab.frame = CGRectMake(self.frame.size.width - 210, self.frame.size.height - 10 - 14, 200, 14);
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:kAttributedPlaceholderKey]) {
self._placeholderTextView.attributedText = [change valueForKey:NSKeyValueChangeNewKey];
}
else if ([keyPath isEqualToString:kPlaceholderKey]) {
self._placeholderTextView.text = [change valueForKey:NSKeyValueChangeNewKey];
}
else if ([keyPath isEqualToString:kFontKey]) {
self._placeholderTextView.font = [change valueForKey:NSKeyValueChangeNewKey];
}
else if ([keyPath isEqualToString:kAttributedTextKey]) {
NSAttributedString *newAttributedText = [change valueForKey:NSKeyValueChangeNewKey];
[self setPlaceholderVisibleForText:newAttributedText.string];
}
else if ([keyPath isEqualToString:kTextKey]) {
NSString *newText = [change valueForKey:NSKeyValueChangeNewKey];
[self setPlaceholderVisibleForText:newText];
}
else if ([keyPath isEqualToString:kExclusionPathsKey]) {
self._placeholderTextView.textContainer.exclusionPaths = [change objectForKey:NSKeyValueChangeNewKey];
[self resizePlaceholderFrame];
}
else if ([keyPath isEqualToString:kLineFragmentPaddingKey]) {
self._placeholderTextView.textContainer.lineFragmentPadding = [[change objectForKey:NSKeyValueChangeNewKey] floatValue];
[self resizePlaceholderFrame];
}
else if ([keyPath isEqualToString:kTextContainerInsetKey]) {
NSValue *value = [change objectForKey:NSKeyValueChangeNewKey];
self._placeholderTextView.textContainerInset = value.UIEdgeInsetsValue;
}
else if ([keyPath isEqualToString:kTextAlignmentKey]) {
NSNumber *alignment = [change objectForKey:NSKeyValueChangeNewKey];
self._placeholderTextView.textAlignment = alignment.intValue;
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor
{
self._placeholderTextView.textColor = placeholderTextColor;
}
- (UIColor *)placeholderTextColor
{
return self._placeholderTextView.textColor;
}
- (void)textDidChange:(NSNotification *)aNotification
{
[self setPlaceholderVisibleForText:self.text];
}
- (void)textDidBeginEditing:(NSNotification *)aNotification {
[self setPlaceholderVisibleForText:self.text];
}
/**
- (BOOL)becomeFirstResponder
{
[self setPlaceholderVisibleForText:self.text];
return [super becomeFirstResponder];
}
*/
- (void)setPlaceholderVisibleForText:(NSString *)text
{
if (text.length < 1) {
if (self.fadeTime > 0.0) {
if (![self._placeholderTextView isDescendantOfView:self]) {
self._placeholderTextView.alpha = 0;
[self addSubview:self._placeholderTextView];
[self sendSubviewToBack:self._placeholderTextView];
}
[UIView animateWithDuration:_fadeTime animations:^{
self._placeholderTextView.alpha = 1;
}];
}
else {
[self addSubview:self._placeholderTextView];
[self sendSubviewToBack:self._placeholderTextView];
self._placeholderTextView.alpha = 1;
}
}
else {
if (self.fadeTime > 0.0) {
[UIView animateWithDuration:_fadeTime animations:^{
self._placeholderTextView.alpha = 0;
}];
}
else {
[self._placeholderTextView removeFromSuperview];
}
}
}
- (UILabel *)lenthLab {
if (!_lenthLab) {
_lenthLab = [[UILabel alloc]init];
_lenthLab.font = [UIFont systemFontOfSize:SW_LengHintFont];
_lenthLab.backgroundColor = [UIColor clearColor];
_lenthLab.textAlignment = NSTextAlignmentRight;
_lenthLab.textColor = SW_PLACEHOLDERCOLOR;
[self addSubview:_lenthLab];
}
return _lenthLab;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self removeObserver:self forKeyPath:kAttributedPlaceholderKey];
[self removeObserver:self forKeyPath:kPlaceholderKey];
[self removeObserver:self forKeyPath:kFontKey];
[self removeObserver:self forKeyPath:kAttributedTextKey];
[self removeObserver:self forKeyPath:kTextKey];
[self removeObserver:self forKeyPath:kTextAlignmentKey];
if (HAS_TEXT_CONTAINER) {
[self.textContainer removeObserver:self forKeyPath:kExclusionPathsKey];
[self.textContainer removeObserver:self forKeyPath:kLineFragmentPaddingKey];
}
if (HAS_TEXT_CONTAINER_INSETS(self)) {
[self removeObserver:self forKeyPath:kTextContainerInsetKey];
}
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
@end
================================================
FILE: SWForm/SWFormManager/UITextView+TextLimit.h
================================================
//
// UITextView+TextLimit.h
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
@interface UITextView (TextLimit)
/**
TextView 添加字数限制
@param maxLength 限制字数
*/
- (void)textLimitWithMaxLength:(NSInteger)maxLength;
@end
================================================
FILE: SWForm/SWFormManager/UITextView+TextLimit.m
================================================
//
// UITextView+TextLimit.m
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "UITextView+TextLimit.h"
@implementation UITextView (TextLimit)
- (void)textLimitWithMaxLength:(NSInteger)maxLength {
NSString *toBeString = self.text;
NSArray *inputModes = [UITextInputMode activeInputModes];
UITextInputMode *currentMode = [inputModes firstObject];
// 输入内容中文校验
if ([currentMode.primaryLanguage isEqualToString:@"zh-Hans"]) {
UITextRange *selectedRange = [self markedTextRange];
UITextPosition *position = [self positionFromPosition:selectedRange.start offset:0];
if (!position) {
if (toBeString.length > maxLength) {
self.text = [toBeString substringToIndex:maxLength];
}
}
}
else{
if (toBeString.length > maxLength) {
self.text = [toBeString substringToIndex:maxLength];
}
}
}
@end
================================================
FILE: SWForm/UIImageView+FormImage.h
================================================
//
// UIImageView+FormImage.h
// SWFormDemo
//
// Created by zijin on 2018/5/31.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
/**
图片附件加载
*/
@interface UIImageView (FormImage)
/**
图片附件条目图片加载
可以在此使用 SDWebImage
@param url 图片加载的url
*/
- (void)sw_setImageItemWithUrl:(NSURL *)url;
@end
================================================
FILE: SWForm/UIImageView+FormImage.m
================================================
//
// UIImageView+FormImage.m
// SWFormDemo
//
// Created by zijin on 2018/5/31.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "UIImageView+FormImage.h"
#import "SWFormCompat.h"
@implementation UIImageView (FormImage)
- (void)sw_setImageItemWithUrl:(NSURL *)url {
// [self sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:SW_PlaceholderImage]];
}
@end
================================================
FILE: SWFormExample/SWForm/SWForm.bundle/Root.plist
================================================
StringsTable
Root
PreferenceSpecifiers
Type
PSGroupSpecifier
Title
Group
Type
PSTextFieldSpecifier
Title
Name
Key
name_preference
DefaultValue
IsSecure
KeyboardType
Alphabet
AutocapitalizationType
None
AutocorrectionType
No
Type
PSToggleSwitchSpecifier
Title
Enabled
Key
enabled_preference
DefaultValue
Type
PSSliderSpecifier
Key
slider_preference
DefaultValue
0.5
MinimumValue
0
MaximumValue
1
MinimumValueImage
MaximumValueImage
================================================
FILE: SWFormExample/SWForm/SWForm.h
================================================
//
// SWForm.h
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#ifndef SWForm_h
#define SWForm_h
#import "SWFormItem.h"
#import "SWFormSectionItem.h"
#import "SWFormHandler.h"
#import "SWFormCompat.h"
#endif /* SWForm_h */
================================================
FILE: SWFormExample/SWForm/SWFormBaseController.h
================================================
//
// SWFormBaseController.h
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
/**
SWFormBaseController 表单基类,所有表单必须继承于BaseController,实现了表单动态配置
*/
@interface SWFormBaseController : UIViewController
/**
表单tableView
*/
@property (nonatomic, strong) UITableView *formTableView;
/**
表单数据源,数据源格式应为 @[SWFormSection..],否则断言会直接崩溃
*/
@property (nonatomic, strong) NSMutableArray *mutableItems;
/**
表单页面初始化方法
@param style 表单tableView样式
*/
- (instancetype)initWithStyle:(UITableViewStyle)style;
@end
================================================
FILE: SWFormExample/SWForm/SWFormBaseController.m
================================================
//
// SWFormBaseController.m
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseController.h"
#import "SWForm.h"
#import "SWFormInputCell.h"
#import "SWFormTextViewInputCell.h"
#import "SWFormSelectCell.h"
#import "SWFormImageCell.h"
@interface SWFormBaseController ()
@property (nonatomic, readonly) UITableViewStyle style;
@end
@implementation SWFormBaseController
- (NSMutableArray *)mutableItems {
if (!_mutableItems) {
_mutableItems = [[NSMutableArray alloc]init];
}
return _mutableItems;
}
- (instancetype)initWithStyle:(UITableViewStyle)style {
self = [super init];
if (self) {
_style = style;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
UITableViewController *tableViewController = [[UITableViewController alloc] initWithStyle:_style];
[self addChildViewController:tableViewController];
[tableViewController.view setFrame:self.view.bounds];
// 获取tableViewController的tableView实现表单自动上移
_formTableView = tableViewController.tableView;
_formTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
_formTableView.dataSource = self;
_formTableView.delegate = self;
//_formTableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
_formTableView.showsVerticalScrollIndicator = NO;
_formTableView.showsHorizontalScrollIndicator = NO;
_formTableView.backgroundColor = [UIColor whiteColor];
_formTableView.tableHeaderView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, CGFLOAT_MIN)];
_formTableView.tableFooterView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, CGFLOAT_MIN)];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(fingerTapped:)];
// 加上这句不会影响你 tableview 上的 action (button,cell selected...)
singleTap.cancelsTouchesInView = NO;
[_formTableView addGestureRecognizer:singleTap];
[self.view addSubview:_formTableView];
}
- (void)fingerTapped:(UITapGestureRecognizer *)gestureRecognizer {
[self.view endEditing:YES];
}
#pragma mark -- TableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.mutableItems.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSParameterAssert([self.mutableItems[section] isKindOfClass:[SWFormSectionItem class]]);
SWFormSectionItem *sectionItem = self.mutableItems[section];
return sectionItem.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
NSParameterAssert([sectionItem.items[indexPath.row] isKindOfClass:[SWFormItem class]]);
SWFormItem *item = sectionItem.items[indexPath.row];
SWWeakSelf
// 表单条目类别判断
if (item.itemType == SWFormItemTypeTextViewInput) {
static NSString *textViewInput_cell_id = @"textViewInput_cell_id";
SWFormTextViewInputCell *cell = [tableView textViewInputCellWithId:textViewInput_cell_id];
cell.item = item;
cell.textViewInputCompletion = ^(NSString *text) {
[weakSelf updateTextViewInputWithText:text indexPath:indexPath];
};
return cell;
}
else if (item.itemType == SWFormItemTypeSelect) {
static NSString *select_cell_id = @"select_cell_id";
SWFormSelectCell *cell = [tableView selectCellWithId:select_cell_id];
cell.item = item;
return cell;
}
else if (item.itemType == SWFormItemTypeImage) {
static NSString *image_cell_id = @"image_cell_id";
SWFormImageCell *cell = [tableView imageCellWithId:image_cell_id];
cell.item = item;
cell.imageCompletion = ^(NSArray *images) {
[weakSelf updateImageWithImages:images indexPath:indexPath];
};
return cell;
}
else {
static NSString *input_cell_id = @"input_cell_id";
SWFormInputCell *cell = [tableView inputCellWithId:input_cell_id];
cell.item = item;
cell.inputCompletion = ^(NSString *text) {
[weakSelf updateInputWithText:text indexPath:indexPath];
};
return cell;
}
}
#pragma mark -- TableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
SWFormItem *item = sectionItem.items[indexPath.row];
if (item.itemType == SWFormItemTypeTextViewInput) {
return [SWFormTextViewInputCell heightWithItem:item];
}
else if (item.itemType == SWFormItemTypeSelect) {
return [SWFormSelectCell heightWithItem:item];
}
else if (item.itemType == SWFormItemTypeImage) {
return [SWFormImageCell heightWithItem:item];
}
else {
return [SWFormInputCell heightWithItem:item];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
SWFormItem *item = sectionItem.items[indexPath.row];
if (item.itemType == SWFormItemTypeSelect && item.itemSelectCompletion) {
item.itemSelectCompletion(item);
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
SWFormSectionItem *sectionItem = self.mutableItems[section];
return sectionItem.headerHeight > 0 ? sectionItem.headerHeight:0.01;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
SWFormSectionItem *sectionItem = self.mutableItems[section];
return sectionItem.footerHeight > 0 ? sectionItem.footerHeight:0.01;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
SWFormSectionItem *sectionItem = self.mutableItems[section];
UIView *header = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, sectionItem.headerHeight)];
return sectionItem.headerView ? sectionItem.headerView:header;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
SWFormSectionItem *sectionItem = self.mutableItems[section];
UIView *footer = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, sectionItem.footerHeight)];
return sectionItem.footerView ? sectionItem.footerView:footer;
}
#pragma mark -- 表单条目响应block处理
- (void)updateInputWithText:(NSString *)text indexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
SWFormItem *item = sectionItem.items[indexPath.row];
item.info = text;
}
- (void)updateTextViewInputWithText:(NSString *)text indexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
SWFormItem *item = sectionItem.items[indexPath.row];
item.info = text;
}
- (void)updateImageWithImages:(NSArray *)images indexPath:(NSIndexPath *)indexPath {
SWFormSectionItem *sectionItem = self.mutableItems[indexPath.section];
SWFormItem *item = sectionItem.items[indexPath.row];
item.images = images;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWFormBaseCell.h
================================================
//
// SWFormBaseCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
@class SelwynExpandableTextView;
/**
SWFormBaseCell 表单条目基类,所有表单条目都继承于BaseCell
*/
@interface SWFormBaseCell : UITableViewCell
@property (nonatomic, strong) SelwynExpandableTextView *expandableTextView;
/**
表单标题
*/
@property (nonatomic, strong) UILabel *titleLabel;
/**
表单条目所在的tableView
*/
@property (nonatomic, weak) UITableView *expandableTableView;
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWFormBaseCell.m
================================================
//
// SWFormBaseCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseCell.h"
#import "SelwynExpandableTextView.h"
#import "SWFormCompat.h"
@interface SWFormBaseCell()
@end
@implementation SWFormBaseCell
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc]init];
_titleLabel.font = [UIFont systemFontOfSize:SW_TitleFont];
_titleLabel.adjustsFontSizeToFitWidth = YES;
[self.contentView addSubview:_titleLabel];
}
return _titleLabel;
}
- (SelwynExpandableTextView *)expandableTextView {
if (!_expandableTextView) {
_expandableTextView = [[SelwynExpandableTextView alloc]init];
_expandableTextView.delegate = self;
_expandableTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
_expandableTextView.textContainer.lineFragmentPadding = 0;
_expandableTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
_expandableTextView.backgroundColor = [UIColor clearColor];
_expandableTextView.font = [UIFont systemFontOfSize:SW_InfoFont];
_expandableTextView.scrollEnabled = NO;
_expandableTextView.autocorrectionType = UITextAutocorrectionTypeNo;
_expandableTextView.layoutManager.allowsNonContiguousLayout = NO;
_expandableTextView.showsVerticalScrollIndicator = NO;
_expandableTextView.showsHorizontalScrollIndicator = NO;
[self.contentView addSubview:_expandableTextView];
}
return _expandableTextView;
}
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWFormImageCell.h
================================================
//
// SWFormImageCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseCell.h"
@class SWFormItem;
/**
图片选择或删除block
@param images 当前已存在图片数组
*/
typedef void(^SWImageCompletion)(NSArray *images);
/**
SWFormImageCell 表单图片选择条目
*/
@interface SWFormImageCell : SWFormBaseCell
@property (nonatomic, strong) SWFormItem *item;
@property (nonatomic, copy) SWImageCompletion imageCompletion;
+ (CGFloat)heightWithItem:(SWFormItem *)item;
@end
@interface UITableView (SWFormImageCell)
- (SWFormImageCell *)imageCellWithId:(NSString *)cellId;
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWFormImageCell.m
================================================
//
// SWFormImageCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormImageCell.h"
#import "SWFormItem.h"
#import "SWImageCollectionCell.h"
#import "SWFormCompat.h"
#import "SWFormImageCell+ImageHandle.h"
static NSString *image_cell_id = @"image_cell_id";
static CGFloat const SW_ImageWidth = 80.0f;
static NSInteger const SW_RowImageCount = 4;
@interface SWFormImageCell()
@property (nonatomic, strong) UIImageView *icon;
@property (nonatomic, strong) UICollectionView *imageCollection;
@property (nonatomic, strong) UIButton *selectBtn;
@property (nonatomic, strong) NSMutableArray *mutableImages;
@end
@implementation SWFormImageCell
- (void)setItem:(SWFormItem *)item {
_item = item;
self.titleLabel.attributedText = item.attributedTitle;
self.selectBtn.enabled = item.editable;
self.mutableImages = [NSMutableArray arrayWithArray:item.images];
}
- (void)layoutSubviews {
[super layoutSubviews];
self.titleLabel.frame = CGRectMake(SW_EdgeMargin, SW_EdgeMargin, SW_TitleWidth, SW_TitleHeight);
self.icon.frame = CGRectMake(self.frame.size.width - 38, SW_EdgeMargin + 2, 23, SW_TitleHeight - 4);
self.selectBtn.frame = CGRectMake(0, 0, self.frame.size.width, self.item.defaultHeight);
if (self.mutableImages.count > 0) {
self.imageCollection.frame = CGRectMake(0, self.item.defaultHeight, self.frame.size.width, self.frame.size.height - self.item.defaultHeight);
self.imageCollection.hidden = NO;
[self.imageCollection reloadData];
}
else {
self.imageCollection.frame = CGRectZero;
self.imageCollection.hidden = YES;
}
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.mutableImages.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
SWImageCollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:image_cell_id forIndexPath:indexPath];
cell.image = self.mutableImages[indexPath.item];
cell.editable = self.item.editable;
cell.deleteImageCompletion = ^{
[self.mutableImages removeObjectAtIndex:indexPath.item];
[self sw_reloadData];
};
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self sw_photoBrowserWithImages:self.mutableImages currentIndex:indexPath.item];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(SW_ImageWidth, SW_ImageWidth);
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(0, 10, 10, 10);
}
- (void)selectImageAction {
if (self.mutableImages.count >= self.item.maxImageCount) {
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:[NSString stringWithFormat:@"最多选择%ld张附件",(long)self.item.maxImageCount] message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alertView show];
return;
}
__weak typeof(self) weakSelf = self;
// 选择图片事件函数
[self sw_selectImageWithMaxImages:self.item.maxImageCount currentImages:self.mutableImages.count completion:^(NSArray *selectImages) {
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf.mutableImages addObjectsFromArray:selectImages];
[strongSelf sw_reloadData];
}];
}
#pragma mark -- 刷新当前图片数据
- (void)sw_reloadData {
if (self.imageCompletion) {
self.imageCompletion(self.mutableImages);
}
[UIView performWithoutAnimation:^{
[self.expandableTableView beginUpdates];
[self.expandableTableView endUpdates];
}];
}
+ (CGFloat)heightWithItem:(SWFormItem *)item {
NSInteger rows = item.images.count%SW_RowImageCount > 0 ? item.images.count/SW_RowImageCount+1:item.images.count/SW_RowImageCount;
return item.images.count > 0 ? item.defaultHeight + 10*(rows+1) + rows*SW_ImageWidth:item.defaultHeight;
}
#pragma mark -- 懒加载实现
- (NSMutableArray *)mutableImages {
if (!_mutableImages) {
_mutableImages = [[NSMutableArray alloc]init];
}
return _mutableImages;
}
- (UICollectionView *)imageCollection {
if (!_imageCollection) {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init];
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
layout.minimumInteritemSpacing = 10;
layout.minimumLineSpacing = 10;
_imageCollection = [[UICollectionView alloc]initWithFrame:CGRectZero collectionViewLayout:layout];
_imageCollection.backgroundColor = [UIColor whiteColor];
_imageCollection.delegate = self;
_imageCollection.dataSource = self;
_imageCollection.hidden = YES;
_imageCollection.scrollEnabled = NO;
_imageCollection.showsVerticalScrollIndicator = NO;
_imageCollection.showsHorizontalScrollIndicator = NO;
[_imageCollection registerClass:[SWImageCollectionCell class] forCellWithReuseIdentifier:image_cell_id];
[self.contentView addSubview:_imageCollection];
}
return _imageCollection;
}
- (UIButton *)selectBtn {
if (!_selectBtn) {
_selectBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[_selectBtn addTarget:self action:@selector(selectImageAction) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:_selectBtn];
}
return _selectBtn;
}
- (UIImageView *)icon {
if (!_icon) {
_icon = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"SWForm.bundle/SWImageIcon"]];
[self.contentView addSubview:_icon];
}
return _icon;
}
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
@end
@implementation UITableView (SWFormImageCell)
- (SWFormImageCell *)imageCellWithId:(NSString *)cellId
{
SWFormImageCell *cell = [self dequeueReusableCellWithIdentifier:cellId];
if (cell == nil) {
cell = [[SWFormImageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.expandableTableView = self;
}
return cell;
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWFormInputCell.h
================================================
//
// SWFormInputCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseCell.h"
@class SWFormItem;
/**
输入内容block
@param text 当前输入内容
*/
typedef void(^SWInputCompletion)(NSString *text);
/**
SWFormInputCell 表单输入条目,标题居左,详情居右,支持单行与多行输入
*/
@interface SWFormInputCell : SWFormBaseCell
/**
条目配置参数
*/
@property (nonatomic, strong) SWFormItem *item;
@property (nonatomic, copy) SWInputCompletion inputCompletion;
/**
获取条目高度
*/
+ (CGFloat)heightWithItem:(SWFormItem *)item;
@end
/**
SWFormInputCell 对于UITableView的分类,实现SWFormInputCell初始化
*/
@interface UITableView (SWFormInputCell)
- (SWFormInputCell *)inputCellWithId:(NSString *)cellId;
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWFormInputCell.m
================================================
//
// SWFormInputCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormInputCell.h"
#import "SWFormItem.h"
#import "SWFormCompat.h"
#import "SelwynExpandableTextView.h"
#import "UITextView+TextLimit.h"
#import "NSString+SWForm.h"
@interface SWFormInputCell()
@end
@implementation SWFormInputCell
- (void)setItem:(SWFormItem *)item {
_item = item;
self.titleLabel.attributedText = item.attributedTitle;
self.expandableTextView.text = [item.info addUnit:item.unit];
self.expandableTextView.attributedPlaceholder = item.attributedPlaceholder;
self.expandableTextView.editable = item.editable;
self.expandableTextView.keyboardType = item.keyboardType;
self.accessoryType = UITableViewCellAccessoryNone;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.titleLabel.frame = CGRectMake(SW_EdgeMargin, SW_EdgeMargin, SW_TitleWidth, SW_TitleHeight);
CGFloat newHeight = [SWFormInputCell heightWithItem:self.item];
self.expandableTextView.frame = CGRectMake(SW_TitleWidth + 2*SW_EdgeMargin, SW_EdgeMargin, SW_SCRREN_WIDTH - (SW_TitleWidth + 3*SW_EdgeMargin), newHeight - 2*SW_EdgeMargin);
}
- (void)textViewDidBeginEditing:(UITextView *)textView {
self.expandableTextView.text = self.item.info;
}
- (void)textViewDidChange:(UITextView *)textView {
if (self.item.maxInputLength > 0) {
// 限制输入字数
[self.expandableTextView textLimitWithMaxLength:self.item.maxInputLength];
}
if (self.inputCompletion) {
self.inputCompletion(self.expandableTextView.text);
}
// 防止输入时表单因刷新动画抖动
[UIView performWithoutAnimation:^{
[self.expandableTableView beginUpdates];
[self.expandableTableView endUpdates];
}];
}
- (void)textViewDidEndEditing:(UITextView *)textView {
self.expandableTextView.text = [self.item.info addUnit:self.item.unit];
}
+ (CGFloat)heightWithItem:(SWFormItem *)item {
CGFloat infoHeight = [item.info sizeWithFontSize:SW_InfoFont maxSize:CGSizeMake(SW_SCRREN_WIDTH - (SW_TitleWidth + 3*SW_EdgeMargin), MAXFLOAT)].height;
return MAX(item.defaultHeight, infoHeight + 2*SW_EdgeMargin);
}
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
@end
@implementation UITableView (SWFormInputCell)
- (SWFormInputCell *)inputCellWithId:(NSString *)cellId
{
SWFormInputCell *cell = [self dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[SWFormInputCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.expandableTableView = self;
}
return cell;
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWFormSelectCell.h
================================================
//
// SWFormSelectCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseCell.h"
@class SWFormItem;
/**
SWFormSelectCell 表单选择条目
*/
@interface SWFormSelectCell : SWFormBaseCell
@property (nonatomic, strong) SWFormItem *item;
+ (CGFloat)heightWithItem:(SWFormItem *)item;
@end
@interface UITableView (SWFormSelectCell)
- (SWFormSelectCell *)selectCellWithId:(NSString *)cellId;
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWFormSelectCell.m
================================================
//
// SWFormSelectCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormSelectCell.h"
#import "SWFormItem.h"
#import "SelwynExpandableTextView.h"
#import "SWFormCompat.h"
#import "NSString+SWForm.h"
@implementation SWFormSelectCell
- (void)setItem:(SWFormItem *)item {
_item = item;
self.titleLabel.attributedText = item.attributedTitle;
self.expandableTextView.text = [item.info addUnit:item.unit];
self.expandableTextView.attributedPlaceholder = item.attributedPlaceholder;
self.expandableTextView.editable = item.editable;
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.titleLabel.frame = CGRectMake(SW_EdgeMargin, (self.item.defaultHeight - SW_TitleHeight)/2, SW_TitleWidth, SW_TitleHeight);
self.expandableTextView.userInteractionEnabled = NO;
CGFloat newHeight = [SWFormSelectCell heightWithItem:self.item];
self.expandableTextView.frame = CGRectMake(SW_TitleWidth + 2*SW_EdgeMargin, SW_EdgeMargin, SW_SCRREN_WIDTH - (SW_TitleWidth + 2*SW_EdgeMargin + 30), newHeight - 2*SW_EdgeMargin);
}
+ (CGFloat)heightWithItem:(SWFormItem *)item {
CGFloat infoHeight = [item.info sizeWithFontSize:SW_InfoFont maxSize:CGSizeMake(SW_SCRREN_WIDTH - SW_TitleWidth - 2*SW_EdgeMargin - 30, MAXFLOAT)].height;
return MAX(item.defaultHeight, infoHeight + 2*SW_EdgeMargin);
}
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
@end
@implementation UITableView (SWFormSelectCell)
- (SWFormSelectCell *)selectCellWithId:(NSString *)cellId
{
SWFormSelectCell *cell = [self dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[SWFormSelectCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.expandableTableView = self;
}
return cell;
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWFormTextViewInputCell.h
================================================
//
// SWFormTextViewInputCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseCell.h"
@class SWFormItem;
typedef void(^SWTextViewInputCompletion)(NSString *text);
/**
SWFormTextViewInputCell 表单输入条目,标题居上,详情居下,支持单行与多行输入
*/
@interface SWFormTextViewInputCell : SWFormBaseCell
@property (nonatomic, strong) SWFormItem *item;
@property (nonatomic, copy) SWTextViewInputCompletion textViewInputCompletion;
+ (CGFloat)heightWithItem:(SWFormItem *)item;
@end
@interface UITableView (SWFormTextViewInputCell)
- (SWFormTextViewInputCell *)textViewInputCellWithId:(NSString *)cellId;
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWFormTextViewInputCell.m
================================================
//
// SWFormTextViewInputCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormTextViewInputCell.h"
#import "SWFormItem.h"
#import "SelwynExpandableTextView.h"
#import "SWFormCompat.h"
#import "UITextView+TextLimit.h"
#import "NSString+SWForm.h"
@interface SWFormTextViewInputCell()
@end
@implementation SWFormTextViewInputCell
- (void)setItem:(SWFormItem *)item {
_item = item;
self.titleLabel.attributedText = item.attributedTitle;
self.expandableTextView.text = [item.info addUnit:item.unit];
self.expandableTextView.attributedPlaceholder = item.attributedPlaceholder;
self.expandableTextView.editable = item.editable;
self.expandableTextView.keyboardType = item.keyboardType;
self.expandableTextView.currentLength = item.info.length;
self.expandableTextView.showLength = item.showLength;
self.expandableTextView.maxLength = item.maxInputLength;
self.accessoryType = UITableViewCellAccessoryNone;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.titleLabel.frame = CGRectMake(SW_EdgeMargin, SW_EdgeMargin, SW_SCRREN_WIDTH - 2*SW_EdgeMargin, SW_TitleHeight);
//重置 expandableTextView 内边距
self.expandableTextView.textContainerInset = UIEdgeInsetsMake(SW_EdgeMargin, SW_EdgeMargin, SW_EdgeMargin, SW_EdgeMargin);
self.expandableTextView.backgroundColor = SW_TEXTVIEW_BACKGROUNDCOLOR;
CGFloat newHeight = [SWFormTextViewInputCell heightWithItem:self.item];
self.expandableTextView.frame = CGRectMake(SW_EdgeMargin, CGRectGetMaxY(self.titleLabel.frame) + SW_EdgeMargin, SW_SCRREN_WIDTH - 2*SW_EdgeMargin, newHeight - 3*SW_EdgeMargin - SW_TitleHeight);
}
- (void)textViewDidBeginEditing:(UITextView *)textView {
self.expandableTextView.text = self.item.info;
self.expandableTextView.currentLength = self.expandableTextView.text.length;
}
- (void)textViewDidChange:(UITextView *)textView {
if (self.item.maxInputLength > 0) {
[self.expandableTextView textLimitWithMaxLength:self.item.maxInputLength];
}
self.expandableTextView.currentLength = self.expandableTextView.text.length;
if (self.textViewInputCompletion) {
self.textViewInputCompletion(self.expandableTextView.text);
}
[UIView performWithoutAnimation:^{
[self.expandableTableView beginUpdates];
[self.expandableTableView endUpdates];
}];
}
- (void)textViewDidEndEditing:(UITextView *)textView {
self.expandableTextView.currentLength = self.expandableTextView.text.length;
self.expandableTextView.text = [self.item.info addUnit:self.item.unit];
}
+ (CGFloat)heightWithItem:(SWFormItem *)item {
CGFloat infoHeight = [item.info sizeWithFontSize:SW_InfoFont maxSize:CGSizeMake(SW_SCRREN_WIDTH - 4*SW_EdgeMargin, MAXFLOAT)].height;
return MAX(item.defaultHeight, infoHeight + SW_TitleHeight + 5*SW_EdgeMargin);
}
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
@end
@implementation UITableView (SWFormTextViewInputCell)
- (SWFormTextViewInputCell *)textViewInputCellWithId:(NSString *)cellId
{
SWFormTextViewInputCell *cell = [self dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[SWFormTextViewInputCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.expandableTableView = self;
}
return cell;
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWImageCollectionCell.h
================================================
//
// SWImageCollectionCell.h
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
typedef void(^SWDeleteImageCompletion)(void);
/**
表单图片条目图片展示
*/
@interface SWImageCollectionCell : UICollectionViewCell
/**
当前图片删除操作block
*/
@property (nonatomic, copy) SWDeleteImageCompletion deleteImageCompletion;
/**
当前图片,支持UIImage、NSURL、NSString(图片URLString)类型
*/
@property (nonatomic, strong) id image;
/**
当前图片是否可编辑
*/
@property (nonatomic, assign) BOOL editable;
@end
================================================
FILE: SWFormExample/SWForm/SWFormCell/SWImageCollectionCell.m
================================================
//
// SWImageCollectionCell.m
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWImageCollectionCell.h"
#import "SWFormCompat.h"
#import "UIImageView+FormImage.h"
/**
删除图标宽高
*/
static CGFloat const SWDeleteIconWidth = 22.0f;
/**
删除按钮宽高,大于图标宽高,增强交互性
*/
static CGFloat const SWDeleteBtnWidth = 25.0f;
@interface SWImageCollectionCell()
@property (nonatomic, strong) UIImageView *currentImageView;
@property (nonatomic, strong) UIImageView *deleteIcon;
@property (nonatomic, strong) UIButton *deleteBtn;
@end
@implementation SWImageCollectionCell
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor whiteColor];
self.currentImageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, SWDeleteIconWidth/2, self.frame.size.width - SWDeleteIconWidth/2, self.frame.size.height - SWDeleteIconWidth/2)];
self.currentImageView.clipsToBounds = YES;
self.currentImageView.contentMode = UIViewContentModeScaleAspectFill;
[self.contentView addSubview:self.currentImageView];
self.deleteIcon = [[UIImageView alloc]initWithFrame:CGRectMake(self.frame.size.width - SWDeleteIconWidth, 0, SWDeleteIconWidth, SWDeleteIconWidth)];
self.deleteIcon.image = [UIImage imageNamed:SW_DeleteIcon];
[self.contentView addSubview:self.deleteIcon];
self.deleteBtn = [UIButton buttonWithType:UIButtonTypeSystem];
self.deleteBtn.frame = CGRectMake(self.frame.size.width - SWDeleteBtnWidth, 0, SWDeleteBtnWidth, SWDeleteBtnWidth);
[self.deleteBtn addTarget:self action:@selector(deleteAction) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:self.deleteBtn];
}
return self;
}
- (void)deleteAction {
if (self.deleteImageCompletion) {
self.deleteImageCompletion();
}
}
- (void)setEditable:(BOOL)editable {
_editable = editable;
self.deleteBtn.hidden = !editable;
self.deleteIcon.hidden = !editable;
}
#pragma mark -- 设置当前图片
- (void)setImage:(id)image {
_image = image;
if ([image isKindOfClass:[UIImage class]]) {
self.currentImageView.image = image;
}
else if ([image isKindOfClass:[NSURL class]]) {
[self.currentImageView sw_setImageItemWithUrl:image];
}
else if ([image isKindOfClass:[NSString class]]) {
[self.currentImageView sw_setImageItemWithUrl:[NSURL URLWithString:image]];
}
else {
self.currentImageView.image = [UIImage imageNamed:SW_PlaceholderImage];
}
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormHandler.h
================================================
//
// SWFormHandler.h
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
/**
SWFormHandler 数据校验文件,包含相机权限、相册权限以及表单空数据校验
*/
@interface SWFormHandler : NSObject
/**
必选(必填)数据空数据校验,可根据需求定制
@param datas 表单数据源
@param success 必选(必填)数据全部校验成功
@param failure 必选(必填)数据某一项校验失败
*/
+ (void)sw_checkFormNullDataWithWithDatas:(NSArray *)datas success:(void(^)(void))success failure:(void(^)(NSString *error))failure;
/**
校验是否有相机权限
*/
+ (void)sw_checkCameraAuthorizationStatusWithGrand:(void(^)(BOOL granted))permissionGranted;
/**
校验是否有相册权限
*/
+ (void)sw_checkAlbumAuthorizationStatusWithGrand:(void(^)(BOOL granted))permissionGranted;
@end
================================================
FILE: SWFormExample/SWForm/SWFormHandler.m
================================================
//
// SWFormHandler.m
// SWFormDemo
//
// Created by zijin on 2018/5/30.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormHandler.h"
#import
#import
#import
#import "SWFormItem.h"
#import "SWFormSectionItem.h"
@implementation SWFormHandler
+ (void)sw_checkFormNullDataWithWithDatas:(NSArray *)datas success:(void(^)(void))success failure:(void(^)(NSString *error))failure {
for (int sec = 0; sec < datas.count; sec++) {
SWFormSectionItem *sectionItem = datas[sec];
for (int row = 0; row < sectionItem.items.count; row++) {
SWFormItem *rowItem = sectionItem.items[row];
if (rowItem.required) {
if (rowItem.itemType == SWFormItemTypeInput || rowItem.itemType == SWFormItemTypeTextViewInput) {
if (!rowItem.info || [rowItem.info isEqualToString:@""]) {
failure([NSString stringWithFormat:@"请输入%@", rowItem.title]);
return;
}
}
else if (rowItem.itemType == SWFormItemTypeSelect) {
if (!rowItem.info || [rowItem.info isEqualToString:@""]) {
failure([NSString stringWithFormat:@"请选择%@", rowItem.title]);
return;
}
}
else if (rowItem.itemType == SWFormItemTypeImage) {
if (!rowItem.images || rowItem.images.count == 0) {
failure([NSString stringWithFormat:@"请选择%@", rowItem.title]);
return;
}
}
}
}
}
success();
}
#pragma mark -- 校验相机相册权限
+ (void)sw_checkCameraAuthorizationStatusWithGrand:(void(^)(BOOL granted))permissionGranted
{
AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
switch (videoAuthStatus) {
// 已授权
case AVAuthorizationStatusAuthorized:
{
permissionGranted(YES);
}
break;
// 未询问用户是否授权
case AVAuthorizationStatusNotDetermined:
{
// 提示用户授权
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
permissionGranted(granted);
}];
}
break;
// 用户拒绝授权或权限受限
case AVAuthorizationStatusRestricted:
case AVAuthorizationStatusDenied:
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"请在”设置-隐私-相机”选项中,允许访问你的相机" message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alert show];
permissionGranted(NO);
}
break;
default:
break;
}
}
+ (void)sw_checkAlbumAuthorizationStatusWithGrand:(void(^)(BOOL granted))permissionGranted {
PHAuthorizationStatus photoAuthStatus = [PHPhotoLibrary authorizationStatus];
switch (photoAuthStatus) {
// 已授权
case PHAuthorizationStatusAuthorized:
{
permissionGranted(YES);
}
break;
// 未询问用户是否授权
case PHAuthorizationStatusNotDetermined:
{
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
permissionGranted(status == PHAuthorizationStatusAuthorized);
}];
}
break;
// 用户拒绝授权或权限受限
case PHAuthorizationStatusRestricted:
case PHAuthorizationStatusDenied:
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"请在”设置-隐私-相片”选项中,允许访问你的相册" message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alert show];
permissionGranted(NO);
}
break;
default:
break;
}
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormImageCell+ImageHandle.h
================================================
//
// SWFormImageCell+ImageHandle.h
// SWFormDemo
//
// Created by zijin on 2018/6/1.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormImageCell.h"
@interface SWFormImageCell (ImageHandle)
/**
选择图片数据回调
@param maxImages 最大可选择图片数
@param currentImages 当前选择图片数
@param completion 选择图片数组回调
*/
- (void)sw_selectImageWithMaxImages:(NSInteger)maxImages currentImages:(NSInteger)currentImages completion:(void(^)(NSArray *selectImages))completion;
/**
图片浏览
@param images 图片数组
@param currentIndex 当前浏览的index
*/
- (void)sw_photoBrowserWithImages:(NSArray *)images currentIndex:(NSInteger)currentIndex;
@end
================================================
FILE: SWFormExample/SWForm/SWFormImageCell+ImageHandle.m
================================================
//
// SWFormImageCell+ImageHandle.m
// SWFormDemo
//
// Created by zijin on 2018/6/1.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormImageCell+ImageHandle.h"
#import "SWFormHandler.h"
#import "TZImagePickerController.h"
#import "objc/runtime.h"
#import "MWPhotoBrowser.h"
#import
static char SW_MAXIMAGECOUNT;
static char SW_CURRENTIMAGECOUNT;
static char SW_SELECTCOMPLETION;
static char SW_BROWSERPHOTOS;
typedef void(^SWSelectImageCompletion)(NSArray *images);
@interface UITableViewCell()
@end
@implementation SWFormImageCell (ImageHandle)
- (void)sw_selectImageWithMaxImages:(NSInteger)maxImages currentImages:(NSInteger)currentImages completion:(void(^)(NSArray *selectImages))completion {
[self sw_setMaxImagesCount:maxImages];
[self sw_setCurrentImagesCount:currentImages];
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"拍照",@"从相册选取", nil];
actionSheet.tag = 10;
[actionSheet showInView:self.superview];
[self sw_setSelectImagesCompletion:completion];
}
#pragma mark -- MWPhotoBrowserDelegate
- (void)sw_photoBrowserWithImages:(NSArray *)images currentIndex:(NSInteger)currentIndex {
NSMutableArray *photos = [NSMutableArray array];
for (int i = 0; i < images.count; i++) {
id object = images[i];
MWPhoto *photo;
if ([object isKindOfClass:[UIImage class]]) {
photo = [MWPhoto photoWithImage:object];
}
else if ([object isKindOfClass:[NSURL class]]) {
photo = [MWPhoto photoWithURL:object];
}
else if ([object isKindOfClass:[NSString class]]) {
photo = [MWPhoto photoWithURL:[NSURL URLWithString:object]];
}
if (photo) {
[photos addObject:photo];
}
}
[self sw_setBrowserPhotos:photos];
MWPhotoBrowser *photoBrowser = [[MWPhotoBrowser alloc] initWithDelegate:self];
photoBrowser.displayActionButton = YES;
photoBrowser.displayNavArrows = YES;
photoBrowser.displaySelectionButtons = NO;
photoBrowser.alwaysShowControls = NO;
photoBrowser.zoomPhotosToFill = YES;
photoBrowser.enableGrid = NO;
photoBrowser.startOnGrid = NO;
[photoBrowser setCurrentPhotoIndex:currentIndex];
UINavigationController *photoNavigationController = [[UINavigationController alloc] initWithRootViewController:photoBrowser];
photoNavigationController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[[self superViewController:self] presentViewController:photoNavigationController animated:YES completion:nil];
}
- (NSUInteger)numberOfPhotosInPhotoBrowser:(MWPhotoBrowser *)photoBrowser {
return [self sw_browserPhotos].count;
}
- (id )photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index
{
if (index < [self sw_browserPhotos].count) {
return [[self sw_browserPhotos] objectAtIndex:index];
}
return nil;
}
#pragma mark -- 从相册选择图片或者拍照
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (actionSheet.tag == 10) {
switch (buttonIndex) {
case 0:
{
[self takePhoto];
}
break;
case 1:
{
[self localPhoto];
}
break;
default:
break;
}
}
}
/**
拍照
*/
- (void)takePhoto {
[SWFormHandler sw_checkCameraAuthorizationStatusWithGrand:^(BOOL granted) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[self selectFromTakePhoto];
});
}
}];
}
/**
本地相册选择
*/
- (void)localPhoto {
[SWFormHandler sw_checkAlbumAuthorizationStatusWithGrand:^(BOOL granted) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[self selectFromLocalPhoto];
});
}
}];
}
- (void)selectFromTakePhoto {
if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera])
{
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[[self superViewController:self] presentViewController:picker animated:YES completion:nil];
}else
{
NSLog(@"无法打开照相机,请检查后重试");
}
}
/** 选取本地图片 */
- (void)selectFromLocalPhoto{
TZImagePickerController *imagePickerVC = [[TZImagePickerController alloc] initWithMaxImagesCount:([self sw_maxImagesCount] - [self sw_currentImagesCount]) delegate:self];
imagePickerVC.allowPickingVideo = NO;
if ([self superViewController:self]) {
[[self superViewController:self] presentViewController:imagePickerVC animated:YES completion:nil];
}
}
#pragma mark -- UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *selectImage = [info valueForKey:UIImagePickerControllerOriginalImage];
SWSelectImageCompletion completion = [self sw_selectImagesCompletion];
if (completion) {
completion(@[selectImage]);
}
[picker dismissViewControllerAnimated:YES completion:nil];
}
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark -- TZImagePickerControllerDelegate
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto {
SWSelectImageCompletion completion = [self sw_selectImagesCompletion];
if (completion) {
completion(photos);
}
}
#pragma mark -- 设置图片选择block
- (void)sw_setSelectImagesCompletion:(SWSelectImageCompletion)completion {
objc_setAssociatedObject(self, &SW_SELECTCOMPLETION, completion, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (SWSelectImageCompletion)sw_selectImagesCompletion {
return objc_getAssociatedObject(self, &SW_SELECTCOMPLETION);
}
#pragma mark -- 设置当前获取的图片
- (void)sw_setBrowserPhotos:(NSArray *)photos {
objc_setAssociatedObject(self, &SW_BROWSERPHOTOS, photos, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSArray *)sw_browserPhotos {
return objc_getAssociatedObject(self, &SW_BROWSERPHOTOS);
}
#pragma mark -- 设置最大图片数
- (void)sw_setMaxImagesCount:(NSInteger)count {
objc_setAssociatedObject(self, &SW_MAXIMAGECOUNT, @(count), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSInteger)sw_maxImagesCount {
return [objc_getAssociatedObject(self, &SW_MAXIMAGECOUNT) integerValue];
}
#pragma mark -- 设置当前图片数
- (void)sw_setCurrentImagesCount:(NSInteger)count {
objc_setAssociatedObject(self, &SW_CURRENTIMAGECOUNT, @(count), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSInteger)sw_currentImagesCount {
return [objc_getAssociatedObject(self, &SW_CURRENTIMAGECOUNT) integerValue];
}
#pragma mark -- 获取当前View所在的ViewController
- (UIViewController *)superViewController:(UIView *)view{
UIResponder *responder = view;
while ((responder = [responder nextResponder]))
if ([responder isKindOfClass: [UIViewController class]])
return (UIViewController *)responder;
return nil;
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormItem/SWFormItem.h
================================================
//
// SWFormItem.h
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
#import
@class SWFormItem;
typedef NS_ENUM(NSInteger, SWFormItemType) {
/**
表单条目可单行或多行输入(标题居左)
*/
SWFormItemTypeInput = 0,
/**
表单条目可选择(标题居左)
*/
SWFormItemTypeSelect = 1,
/**
表单条目可多行输入(标题居上)
*/
SWFormItemTypeTextViewInput = 2,
/**
表单条目包含图片选择
*/
SWFormItemTypeImage = 3,
};
typedef NS_ENUM(NSInteger, SWFormItemUnitType) {
SWFormItemUnitTypeNone = 0, // 无单位
SWFormItemUnitTypeYuan, // 元
SWFormItemUnitTypeYear, // 年
SWFormItemUnitTypeMillion, // 万元
SWFormItemUnitTypeCustom, // 自定义单位
};
typedef void(^SWItemSelectCompletion)(SWFormItem *item);
/**
SWFormItem 主要对表单条目提供动态配置属性
*/
@interface SWFormItem : NSObject
/**
表单条目缺省高度,缺省值为44.0f, 可根据需求设置
*/
@property (nonatomic, assign) CGFloat defaultHeight;
/**
表单条目类型
*/
@property (nonatomic, assign) SWFormItemType itemType;
/**
表单条目标题,表单标题为单行显示,尽可能简短,若标题太长,会牺牲字体大小以达到显示完全的效果
*/
@property (nonatomic, copy, nonnull) NSString *title;
@property (nonatomic, strong, nonnull) NSAttributedString *attributedTitle;
/**
表单条目详情
*/
@property (nonatomic, copy, nullable) NSString *info;
/**
表单条目占位字符
*/
@property (nonatomic, copy, nullable) NSString *placeholder;
@property (nonatomic, strong, nullable) NSAttributedString *attributedPlaceholder;
/**
是否显示表单条目占位字符 YES:显示 NO:不显示 --- 新增 default is YES;详情 default is NO
*/
@property (nonatomic, assign) BOOL showPlaceholder;
/**
图片附件条目图片数组,支持UIImage、NSURL、NSString(图片URLString)类型元素
*/
@property (nonatomic, strong, nullable) NSArray *images;
/**
images 图片数组中类型筛选出为UIImage的数组子集,以实现图片上传筛选
*/
@property (nonatomic, strong, readonly) NSArray *selectImages;
/**
表单条目键盘类型
*/
@property (nonatomic, assign) UIKeyboardType keyboardType;
/**
表单条目是否可编辑 YES:可编辑 NO:不可编辑
*/
@property (nonatomic, assign) BOOL editable;
/**
表单条目是否必填(必选) YES:必填(必选) NO:可填(可选)
*/
@property (nonatomic, assign) BOOL required;
/**
SWFormItemTypeInput 以及 SWFormItemTypeTextViewInput 类别中表示最大输入字数
0 表示无限制
*/
@property (nonatomic, assign) NSUInteger maxInputLength;
/**
SWFormItemTypeImage 类别中表示最大选择图片数
*/
@property (nonatomic, assign) NSUInteger maxImageCount;
/**
表单条目点击选择事件block
*/
@property (nonatomic, copy, nullable) SWItemSelectCompletion itemSelectCompletion;
/**
条目附带单位
*/
@property (nonatomic, copy, nullable) NSString *unit;
/**
表单条目单位类别
*/
@property (nonatomic, assign) SWFormItemUnitType itemUnitType;
/**
是否显示当前字数
只在 SWFormItemTypeTextViewInput 类型下有效,若无最大字数限制,则只显示字数; 若有字数限制,则显示 "当前字数/最大字数"
*/
@property (nonatomic, assign) BOOL showLength;
@end
/**
SWFormItem_Add 快捷构建新增表单条目
@param title 标题
@param info 详情
@param itemType 条目类别
@param editable 是否可编辑
@param required 是否必填
@param keyboardType 键盘类别
*/
FOUNDATION_EXPORT SWFormItem *SWFormItem_Add(NSString * _Nonnull title, NSString * _Nullable info, SWFormItemType itemType, BOOL editable, BOOL required, UIKeyboardType keyboardType);
/**
SWFormItem_Info 快捷构建详情表单条目
*/
FOUNDATION_EXPORT SWFormItem *SWFormItem_Info(NSString * _Nonnull title, NSString * _Nullable info, SWFormItemType itemType);
================================================
FILE: SWFormExample/SWForm/SWFormItem/SWFormItem.m
================================================
//
// SWFormItem.m
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormItem.h"
#import "SWFormCompat.h"
static NSString *const SWUnitYuan = @"元";
static NSString *const SWUnitYear = @"年";
static NSString *const SWUnitMillion = @"万元";
@interface SWFormItem()
+ (instancetype)sw_itemWithTitle:(NSString *)title info:(NSString *)info itemType:(SWFormItemType)itemType editable:(BOOL)editable required:(BOOL)required keyboardType:(UIKeyboardType)keyboardType;
+ (instancetype)sw_itemWithTitle:(NSString *)title info:(NSString *)info itemType:(SWFormItemType)itemType;
@end
inline SWFormItem *SWFormItem_Add(NSString * _Nonnull title, NSString * _Nullable info, SWFormItemType itemType, BOOL editable, BOOL required, UIKeyboardType keyboardType) {
return [SWFormItem sw_itemWithTitle:title info:info itemType:itemType editable:editable required:required keyboardType:keyboardType];
}
inline SWFormItem *SWFormItem_Info(NSString * _Nonnull title, NSString * _Nullable info, SWFormItemType itemType) {
return [SWFormItem sw_itemWithTitle:title info:info itemType:itemType];
}
@implementation SWFormItem
+ (instancetype)sw_itemWithTitle:(NSString *)title info:(NSString *)info itemType:(SWFormItemType)itemType editable:(BOOL)editable required:(BOOL)required keyboardType:(UIKeyboardType)keyboardType {
return [[self alloc]initWithTitle:title info:info itemType:itemType editable:editable required:required keyboardType:keyboardType images:nil showPlaceholder:YES];
}
+ (instancetype)sw_itemWithTitle:(NSString *)title info:(NSString *)info itemType:(SWFormItemType)itemType {
return [[self alloc]initWithTitle:title info:info itemType:itemType editable:NO required:NO keyboardType:UIKeyboardTypeDefault images:nil showPlaceholder:NO];
}
- (instancetype)initWithTitle:(NSString *)title info:(NSString *)info itemType:(SWFormItemType)itemType editable:(BOOL)editable required:(BOOL)required keyboardType:(UIKeyboardType)keyboardType images:(NSArray *)images showPlaceholder:(BOOL)showPlaceholder{
self = [super init];
if (self) {
self.itemUnitType = SWFormItemUnitTypeNone;
self.maxInputLength = SW_GlobalMaxInputLength;
self.maxImageCount = SW_GlobalMaxImages;
self.title = title;
self.info = info;
self.itemType = itemType;
self.editable = editable;
self.required = required;
self.keyboardType = keyboardType;
self.images = images;
self.showPlaceholder = showPlaceholder;
[self sw_setDefaultHeight:itemType];
[self sw_setPlaceholderWithShow:showPlaceholder itemType:itemType];
[self sw_setAttributedTitleWithRequired:required title:title itemType:itemType];
}
return self;
}
#pragma mark -- 根据表单条目类型设置条目缺省高度
- (void)sw_setDefaultHeight:(SWFormItemType)itemType {
self.defaultHeight = itemType == SWFormItemTypeTextViewInput ? SW_DefaultTextViewItemHeight:SW_DefaultItemHeight;
}
#pragma mark -- 设置是否显示输入框占位字符
- (void)sw_setPlaceholderWithShow:(BOOL)show itemType:(SWFormItemType)itemType {
if (!show) {
self.placeholder = @"";
return;
}
switch (itemType) {
case SWFormItemTypeInput:
case SWFormItemTypeTextViewInput:
{
self.placeholder = @"请输入";
}
break;
case SWFormItemTypeSelect:
{
self.placeholder = @"请选择";
}
break;
default:
self.placeholder = @"";
break;
}
}
#pragma mark -- 设置标题显示
- (void)sw_setAttributedTitleWithRequired:(BOOL)required title:(NSString *)title itemType:(SWFormItemType)itemType{
if (required) {
if (SW_TitleShowType == SWTitleShowTypeDefault) {
switch (self.itemType) {
case SWFormItemTypeInput:
case SWFormItemTypeTextViewInput:
{
title = [NSString stringWithFormat:@"%@(必填)", title];
}
break;
case SWFormItemTypeSelect:
case SWFormItemTypeImage:
{
title = [NSString stringWithFormat:@"%@(必选)", title];
}
break;
default:
break;
}
}
else if (SW_TitleShowType == SWTitleShowTypeRedStarFront) {
title = [NSString stringWithFormat:@"*%@", title];
}
else if (SW_TitleShowType == SWTitleShowTypeRedStarBack) {
title = [NSString stringWithFormat:@"%@*", title];
}
}
NSMutableAttributedString *attributedTitle = [[NSMutableAttributedString alloc]initWithString:title attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:SW_TitleFont], NSForegroundColorAttributeName:SW_TITLECOLOR}];
if (required) {
if (SW_TitleShowType == SWTitleShowTypeRedStarFront) {
[attributedTitle addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 1)];
}
else if (SW_TitleShowType == SWTitleShowTypeRedStarBack) {
[attributedTitle addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(title.length - 1, 1)];
}
}
_attributedTitle = attributedTitle;
}
#pragma mark -- 重写get方法
- (NSArray *)selectImages {
NSMutableArray *tempImages = [NSMutableArray array];
for (id temp in self.images) {
if ([temp isKindOfClass:[UIImage class]]) {
[tempImages addObject:temp];
}
}
return tempImages;
}
#pragma mark -- 重写属性set方法,防止单独改变属性无响应效果
/**
设置表单条目附带单位
*/
- (void)setItemUnitType:(SWFormItemUnitType)itemUnitType {
NSString *tempUnit = self.unit ?: @"";
switch (itemUnitType) {
case SWFormItemUnitTypeNone:
{
tempUnit = @"";
}
break;
case SWFormItemUnitTypeYuan:
{
tempUnit = SWUnitYuan;
}
break;
case SWFormItemUnitTypeYear:
{
tempUnit = SWUnitYear;
}
break;
case SWFormItemUnitTypeMillion:
{
tempUnit = SWUnitMillion;
}
default:
break;
}
_unit = tempUnit;
}
/**
根据单位设置单元格单位类别,防止单位与单元格式不一致
*/
- (void)setUnit:(NSString *)unit {
_unit = unit;
if ([unit isEqualToString:@""]) {
_itemUnitType = SWFormItemUnitTypeNone;
}
else if (unit == SWUnitYuan) {
_itemUnitType = SWFormItemUnitTypeYuan;
}
else if (unit == SWUnitYear) {
_itemUnitType = SWFormItemUnitTypeYear;
}
else if (unit == SWUnitMillion) {
_itemUnitType = SWFormItemUnitTypeMillion;
}
else {
_itemUnitType = SWFormItemUnitTypeCustom;
}
}
- (void)setImages:(NSArray *)images {
_images = images;
[self selectImages];
}
- (void)setTitle:(NSString *)title {
_title = title;
[self sw_setAttributedTitleWithRequired:self.required title:title itemType:self.itemType];
}
- (void)setRequired:(BOOL)required {
_required = required;
[self sw_setAttributedTitleWithRequired:required title:self.title itemType:self.itemType];
}
- (void)setItemType:(SWFormItemType)itemType {
_itemType = itemType;
[self sw_setDefaultHeight:itemType];
[self sw_setAttributedTitleWithRequired:self.required title:self.title itemType:itemType];
[self sw_setPlaceholderWithShow:self.showPlaceholder itemType:itemType];
}
- (void)setShowPlaceholder:(BOOL)showPlaceholder {
_showPlaceholder = showPlaceholder;
[self sw_setPlaceholderWithShow:showPlaceholder itemType:self.itemType];
}
- (void)setPlaceholder:(NSString *)placeholder {
_placeholder = placeholder;
NSAttributedString *attributedPlaceholder = [[NSAttributedString alloc]initWithString:placeholder ?: @"" attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:SW_InfoFont],NSForegroundColorAttributeName:SW_PLACEHOLDERCOLOR}];
_attributedPlaceholder = attributedPlaceholder;
}
- (void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholder {
_attributedPlaceholder = attributedPlaceholder ?: [[NSAttributedString alloc]initWithString:@""];
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormItem/SWFormSectionItem.h
================================================
//
// SWFormSectionItem.h
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
#import
/**
SWFormSectionItem 主要对表单section条目提供动态配置属性
*/
@interface SWFormSectionItem : NSObject
/**
表单section包含的条目集合
*/
@property (nonatomic, strong, nonnull) NSArray *items;
/**
表单section头部高度
*/
@property (nonatomic, assign) CGFloat headerHeight;
/**
表单section尾部高度
*/
@property (nonatomic, assign) CGFloat footerHeight;
/**
表单section头部视图
*/
@property (nonatomic, strong, nullable) UIView *headerView;
/**
表单section尾部视图
*/
@property (nonatomic, strong, nullable) UIView *footerView;
@end
/**
SWSectionItem 快捷构建表单section条目
@param items 表单section包含的条目集合
*/
FOUNDATION_EXPORT SWFormSectionItem *SWSectionItem(NSArray * _Nonnull items);
================================================
FILE: SWFormExample/SWForm/SWFormItem/SWFormSectionItem.m
================================================
//
// SWFormSectionItem.m
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormSectionItem.h"
@interface SWFormSectionItem()
+ (instancetype)sw_sectionItem:(NSArray *)items;
@end
inline SWFormSectionItem *SWSectionItem(NSArray * _Nonnull items) {
return [SWFormSectionItem sw_sectionItem:items];
}
@implementation SWFormSectionItem
+ (instancetype)sw_sectionItem:(NSArray *)items {
return [[self alloc]initWithItems:items];
}
- (instancetype)initWithItems:(NSArray *)items {
self = [super init];
if (self) {
self.items = items;
}
return self;
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormManager/NSString+SWForm.h
================================================
//
// NSString+SWForm.h
// SWFormDemo
//
// Created by zijin on 2018/6/26.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
@interface NSString (SWForm)
/**
字符串添加单位
@param unit 单位
*/
- (NSString *)addUnit:(NSString *)unit;
/**
获取字符串的Size大小
@param fontSize 字体大小
@param maxSize 最大显示Size
*/
- (CGSize)sizeWithFontSize:(CGFloat)fontSize maxSize:(CGSize)maxSize;
@end
================================================
FILE: SWFormExample/SWForm/SWFormManager/NSString+SWForm.m
================================================
//
// NSString+SWForm.m
// SWFormDemo
//
// Created by zijin on 2018/6/26.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "NSString+SWForm.h"
@implementation NSString (SWForm)
- (NSString *)addUnit:(NSString *)unit {
if ([self isEqualToString:@""] || [unit isEqualToString:@""]) {
return self;
}
return [NSString stringWithFormat:@"%@ %@", self, unit];
}
- (CGSize)sizeWithFontSize:(CGFloat)fontSize maxSize:(CGSize)maxSize {
return [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]} context:nil].size;
}
@end
================================================
FILE: SWFormExample/SWForm/SWFormManager/SWFormCompat.h
================================================
//
// SWFormCompat.h
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
/**
SWFormCompat 主要配置表单涉及的相关常量参数,可根据需求修改配置
*/
#import
/**
必选条目标题呈现形式类别
*/
typedef NS_ENUM(NSInteger, SWTitleShowType) {
/**
默认呈现形式,如: 标题(必填)
*/
SWTitleShowTypeDefault,
/**
标题前部加红色*,如: *标题
*/
SWTitleShowTypeRedStarFront,
/**
标题后部加红色*,如: 标题*
*/
SWTitleShowTypeRedStarBack,
/**
仅显示标题
*/
SWTitleShowTypeOnlyTitle,
};
/**
表单标题字体大小,缺省为16
*/
extern CGFloat const SW_TitleFont;
/**
表单详情字体大小,缺省为16
*/
extern CGFloat const SW_InfoFont;
/**
表单条目边缘距离,缺省为10.0f
*/
extern CGFloat const SW_EdgeMargin;
/**
表单标题宽度,缺省为100.0f
*/
extern CGFloat const SW_TitleWidth;
/**
表单标题高度,缺省为24.0f
*/
extern CGFloat const SW_TitleHeight;
/**
表单条目初始高度,缺省为44.0f,SWFormItemTypeTextViewInput 类型缺省高度为200,为确保显示正常,设置值>= 44
*/
extern CGFloat const SW_DefaultItemHeight;
extern CGFloat const SW_DefaultTextViewItemHeight;
/**
表单标题显示类别,缺省为 SWTitleShowTypeRedStarFront
*/
extern NSInteger const SW_TitleShowType;
/**
表单输入字数限制,缺省为200
0 表示无限制
*/
extern NSUInteger const SW_GlobalMaxInputLength;
/**
表单选择图片附件数,缺省为6
*/
extern NSUInteger const SW_GlobalMaxImages;
/**
表单图片条目图片加载失败占位图
*/
extern NSString *const SW_PlaceholderImage;
/**
表单附件删除图标
*/
extern NSString *const SW_DeleteIcon;
/**
表单TextView字数提示文字大小
*/
extern CGFloat const SW_LengHintFont;
/**
表单条目输入框占位符字体颜色
*/
#define SW_PLACEHOLDERCOLOR [UIColor colorWithRed:187/255.0 green:187/255.0 blue:187/255.0 alpha:1/1.0]
/**
SWFormItemTypeTextViewInput 类别 TextView 背景颜色
*/
#define SW_TEXTVIEW_BACKGROUNDCOLOR [UIColor colorWithRed:250/255.0 green:250/255.0 blue:250/255.0 alpha:1/1.0]
/**
表单条目标题颜色
*/
#define SW_TITLECOLOR [UIColor colorWithRed:51/255.0 green:51/255.0 blue:51/255.0 alpha:1/1.0]
/**
获取屏幕宽度
*/
#define SW_SCRREN_WIDTH [UIScreen mainScreen].bounds.size.width
/// 弱引用
#define SWWeakSelf __weak typeof(self) weakSelf = self;
================================================
FILE: SWFormExample/SWForm/SWFormManager/SWFormCompat.m
================================================
//
// SWFormCompat.m
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormCompat.h"
// 表单标题字体大小
CGFloat const SW_TitleFont = 16.0f;
// 表单详情字体大小
CGFloat const SW_InfoFont = 16.0f;
// 表单条目边缘距离
CGFloat const SW_EdgeMargin = 10.0f;
// 表单标题宽度
CGFloat const SW_TitleWidth = 80.0f;
// 表单标题高度
CGFloat const SW_TitleHeight = 24.0f;
// 表单条目初始高度
CGFloat const SW_DefaultItemHeight = 44.0f;
CGFloat const SW_DefaultTextViewItemHeight = 200.0f;
// 表单标题显示类别
NSInteger const SW_TitleShowType = SWTitleShowTypeRedStarFront;
// 表单输入字数限制
NSUInteger const SW_GlobalMaxInputLength = 200;
// 表单选择图片附件数
NSUInteger const SW_GlobalMaxImages = 6;
// 表单TextView字数提示文字大小
CGFloat const SW_LengHintFont = 12;
// 表单图片条目图片加载失败占位图
NSString *const SW_PlaceholderImage = @"SWForm.bundle/SWPlaceholderIcon";
// 表单附件删除图标
NSString *const SW_DeleteIcon = @"SWForm.bundle/SWDeleteIcon";
================================================
FILE: SWFormExample/SWForm/SWFormManager/SelwynExpandableTextView.h
================================================
//
// SelwynExpandableTextView.h
// SelwynFormDemo
//
// Created by BSW on 2017/6/24.
// Copyright © 2017年 selwyn. All rights reserved.
//
#import
/**
SelwynExpandableTextView 可高度自适应的UITextView
*/
@interface SelwynExpandableTextView : UITextView
@property (nonatomic, assign) NSUInteger currentLength;
@property (nonatomic, assign) NSUInteger maxLength;
@property (nonatomic, assign) BOOL showLength;
@property (copy, nonatomic) IBInspectable NSString *placeholder;
@property (copy, nonatomic) NSAttributedString *attributedPlaceholder;
@property (nonatomic) IBInspectable double fadeTime;
@property (retain, nonatomic) UIColor *placeholderTextColor UI_APPEARANCE_SELECTOR;
@end
================================================
FILE: SWFormExample/SWForm/SWFormManager/SelwynExpandableTextView.m
================================================
//
// SelwynExpandableTextView.m
// SelwynFormDemo
//
// Created by BSW on 2017/6/24.
// Copyright © 2017年 selwyn. All rights reserved.
//
#import "SelwynExpandableTextView.h"
#import "SWFormCompat.h"
#define HAS_TEXT_CONTAINER [self respondsToSelector:@selector(textContainer)]
#define HAS_TEXT_CONTAINER_INSETS(x) [(x) respondsToSelector:@selector(textContainerInset)]
@interface SelwynExpandableTextView()
@property (strong, nonatomic) UITextView *_placeholderTextView;
@property (nonatomic, strong) UILabel *lenthLab;
@end
static NSString * const kAttributedPlaceholderKey = @"attributedPlaceholder";
static NSString * const kPlaceholderKey = @"placeholder";
static NSString * const kFontKey = @"font";
static NSString * const kAttributedTextKey = @"attributedText";
static NSString * const kTextKey = @"text";
static NSString * const kExclusionPathsKey = @"exclusionPaths";
static NSString * const kLineFragmentPaddingKey = @"lineFragmentPadding";
static NSString * const kTextContainerInsetKey = @"textContainerInset";
static NSString * const kTextAlignmentKey = @"textAlignment";
@implementation SelwynExpandableTextView
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self preparePlaceholder];
}
return self;
}
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer
{
self = [super initWithFrame:frame textContainer:textContainer];
if (self) {
[self preparePlaceholder];
}
return self;
}
#else
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self preparePlaceholder];
}
return self;
}
#endif
- (void)preparePlaceholder
{
NSAssert(!self._placeholderTextView, @"placeholder has been prepared already: %@", self._placeholderTextView);
// the label which displays the placeholder
// needs to inherit some properties from its parent text view
// account for standard UITextViewPadding
CGRect frame = self.bounds;
self._placeholderTextView = [[UITextView alloc] initWithFrame:frame];
self._placeholderTextView.opaque = NO;
self._placeholderTextView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
self._placeholderTextView.backgroundColor = [UIColor clearColor];
self._placeholderTextView.textColor = [UIColor colorWithWhite:0.7f alpha:0.7f];
self._placeholderTextView.editable = NO;
self._placeholderTextView.scrollEnabled = YES;
self._placeholderTextView.userInteractionEnabled = NO;
self._placeholderTextView.font = self.font;
self._placeholderTextView.isAccessibilityElement = NO;
self._placeholderTextView.contentOffset = self.contentOffset;
self._placeholderTextView.contentInset = self.contentInset;
if ([self._placeholderTextView respondsToSelector:@selector(setSelectable:)]) {
self._placeholderTextView.selectable = NO;
}
if (HAS_TEXT_CONTAINER) {
self._placeholderTextView.textContainer.exclusionPaths = self.textContainer.exclusionPaths;
self._placeholderTextView.textContainer.lineFragmentPadding = self.textContainer.lineFragmentPadding;
}
if (HAS_TEXT_CONTAINER_INSETS(self)) {
self._placeholderTextView.textContainerInset = self.textContainerInset;
}
if (_attributedPlaceholder) {
self._placeholderTextView.attributedText = _attributedPlaceholder;
} else if (_placeholder) {
self._placeholderTextView.text = _placeholder;
}
[self setPlaceholderVisibleForText:self.text];
self.clipsToBounds = YES;
// some observations
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self selector:@selector(textDidChange:)
name:UITextViewTextDidChangeNotification object:self];
[defaultCenter addObserver:self selector:@selector(textDidBeginEditing:) name:UITextViewTextDidBeginEditingNotification object:self];
[self addObserver:self forKeyPath:kAttributedPlaceholderKey
options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:kPlaceholderKey
options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:kFontKey
options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:kAttributedTextKey
options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:kTextKey
options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:kTextAlignmentKey
options:NSKeyValueObservingOptionNew context:nil];
if (HAS_TEXT_CONTAINER) {
[self.textContainer addObserver:self forKeyPath:kExclusionPathsKey
options:NSKeyValueObservingOptionNew context:nil];
[self.textContainer addObserver:self forKeyPath:kLineFragmentPaddingKey
options:NSKeyValueObservingOptionNew context:nil];
}
if (HAS_TEXT_CONTAINER_INSETS(self)) {
[self addObserver:self forKeyPath:kTextContainerInsetKey
options:NSKeyValueObservingOptionNew context:nil];
}
}
- (void)setPlaceholder:(NSString *)placeholderText
{
_placeholder = [placeholderText copy];
_attributedPlaceholder = [[NSAttributedString alloc] initWithString:placeholderText];
[self resizePlaceholderFrame];
}
- (void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholderText
{
_placeholder = attributedPlaceholderText.string;
_attributedPlaceholder = [attributedPlaceholderText copy];
[self resizePlaceholderFrame];
}
- (void)layoutSubviews
{
[super layoutSubviews];
self._placeholderTextView.textAlignment = self.textAlignment;
[self resizePlaceholderFrame];
[self resizeLengthFrame];
}
- (void)setCurrentLength:(NSUInteger)currentLength {
_currentLength = currentLength;
[self resizeLengthFrame];
}
- (void)resizePlaceholderFrame
{
CGRect frame = self._placeholderTextView.frame;
frame.size = self.bounds.size;
self._placeholderTextView.frame = frame;
}
- (void)resizeLengthFrame {
if (self.showLength) {
NSString *length = [NSString stringWithFormat:@"%lu", self.currentLength];
if (self.maxLength > 0) {
length = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)self.currentLength, (unsigned long)self.maxLength];
}
self.lenthLab.text = length;
self.lenthLab.frame = CGRectMake(self.frame.size.width - 210, self.frame.size.height - 10 - 14, 200, 14);
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:kAttributedPlaceholderKey]) {
self._placeholderTextView.attributedText = [change valueForKey:NSKeyValueChangeNewKey];
}
else if ([keyPath isEqualToString:kPlaceholderKey]) {
self._placeholderTextView.text = [change valueForKey:NSKeyValueChangeNewKey];
}
else if ([keyPath isEqualToString:kFontKey]) {
self._placeholderTextView.font = [change valueForKey:NSKeyValueChangeNewKey];
}
else if ([keyPath isEqualToString:kAttributedTextKey]) {
NSAttributedString *newAttributedText = [change valueForKey:NSKeyValueChangeNewKey];
[self setPlaceholderVisibleForText:newAttributedText.string];
}
else if ([keyPath isEqualToString:kTextKey]) {
NSString *newText = [change valueForKey:NSKeyValueChangeNewKey];
[self setPlaceholderVisibleForText:newText];
}
else if ([keyPath isEqualToString:kExclusionPathsKey]) {
self._placeholderTextView.textContainer.exclusionPaths = [change objectForKey:NSKeyValueChangeNewKey];
[self resizePlaceholderFrame];
}
else if ([keyPath isEqualToString:kLineFragmentPaddingKey]) {
self._placeholderTextView.textContainer.lineFragmentPadding = [[change objectForKey:NSKeyValueChangeNewKey] floatValue];
[self resizePlaceholderFrame];
}
else if ([keyPath isEqualToString:kTextContainerInsetKey]) {
NSValue *value = [change objectForKey:NSKeyValueChangeNewKey];
self._placeholderTextView.textContainerInset = value.UIEdgeInsetsValue;
}
else if ([keyPath isEqualToString:kTextAlignmentKey]) {
NSNumber *alignment = [change objectForKey:NSKeyValueChangeNewKey];
self._placeholderTextView.textAlignment = alignment.intValue;
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor
{
self._placeholderTextView.textColor = placeholderTextColor;
}
- (UIColor *)placeholderTextColor
{
return self._placeholderTextView.textColor;
}
- (void)textDidChange:(NSNotification *)aNotification
{
[self setPlaceholderVisibleForText:self.text];
}
- (void)textDidBeginEditing:(NSNotification *)aNotification {
[self setPlaceholderVisibleForText:self.text];
}
/**
- (BOOL)becomeFirstResponder
{
[self setPlaceholderVisibleForText:self.text];
return [super becomeFirstResponder];
}
*/
- (void)setPlaceholderVisibleForText:(NSString *)text
{
if (text.length < 1) {
if (self.fadeTime > 0.0) {
if (![self._placeholderTextView isDescendantOfView:self]) {
self._placeholderTextView.alpha = 0;
[self addSubview:self._placeholderTextView];
[self sendSubviewToBack:self._placeholderTextView];
}
[UIView animateWithDuration:_fadeTime animations:^{
self._placeholderTextView.alpha = 1;
}];
}
else {
[self addSubview:self._placeholderTextView];
[self sendSubviewToBack:self._placeholderTextView];
self._placeholderTextView.alpha = 1;
}
}
else {
if (self.fadeTime > 0.0) {
[UIView animateWithDuration:_fadeTime animations:^{
self._placeholderTextView.alpha = 0;
}];
}
else {
[self._placeholderTextView removeFromSuperview];
}
}
}
- (UILabel *)lenthLab {
if (!_lenthLab) {
_lenthLab = [[UILabel alloc]init];
_lenthLab.font = [UIFont systemFontOfSize:SW_LengHintFont];
_lenthLab.backgroundColor = [UIColor clearColor];
_lenthLab.textAlignment = NSTextAlignmentRight;
_lenthLab.textColor = SW_PLACEHOLDERCOLOR;
[self addSubview:_lenthLab];
}
return _lenthLab;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self removeObserver:self forKeyPath:kAttributedPlaceholderKey];
[self removeObserver:self forKeyPath:kPlaceholderKey];
[self removeObserver:self forKeyPath:kFontKey];
[self removeObserver:self forKeyPath:kAttributedTextKey];
[self removeObserver:self forKeyPath:kTextKey];
[self removeObserver:self forKeyPath:kTextAlignmentKey];
if (HAS_TEXT_CONTAINER) {
[self.textContainer removeObserver:self forKeyPath:kExclusionPathsKey];
[self.textContainer removeObserver:self forKeyPath:kLineFragmentPaddingKey];
}
if (HAS_TEXT_CONTAINER_INSETS(self)) {
[self removeObserver:self forKeyPath:kTextContainerInsetKey];
}
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
@end
================================================
FILE: SWFormExample/SWForm/SWFormManager/UITextView+TextLimit.h
================================================
//
// UITextView+TextLimit.h
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
@interface UITextView (TextLimit)
/**
TextView 添加字数限制
@param maxLength 限制字数
*/
- (void)textLimitWithMaxLength:(NSInteger)maxLength;
@end
================================================
FILE: SWFormExample/SWForm/SWFormManager/UITextView+TextLimit.m
================================================
//
// UITextView+TextLimit.m
// SWFormDemo
//
// Created by zijin on 2018/5/29.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "UITextView+TextLimit.h"
@implementation UITextView (TextLimit)
- (void)textLimitWithMaxLength:(NSInteger)maxLength {
NSString *toBeString = self.text;
NSArray *inputModes = [UITextInputMode activeInputModes];
UITextInputMode *currentMode = [inputModes firstObject];
// 输入内容中文校验
if ([currentMode.primaryLanguage isEqualToString:@"zh-Hans"]) {
UITextRange *selectedRange = [self markedTextRange];
UITextPosition *position = [self positionFromPosition:selectedRange.start offset:0];
if (!position) {
if (toBeString.length > maxLength) {
self.text = [toBeString substringToIndex:maxLength];
}
}
}
else{
if (toBeString.length > maxLength) {
self.text = [toBeString substringToIndex:maxLength];
}
}
}
@end
================================================
FILE: SWFormExample/SWForm/UIImageView+FormImage.h
================================================
//
// UIImageView+FormImage.h
// SWFormDemo
//
// Created by zijin on 2018/5/31.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
/**
图片附件加载
*/
@interface UIImageView (FormImage)
/**
图片附件条目图片加载
可以在此使用 SDWebImage
@param url 图片加载的url
*/
- (void)sw_setImageItemWithUrl:(NSURL *)url;
@end
================================================
FILE: SWFormExample/SWForm/UIImageView+FormImage.m
================================================
//
// UIImageView+FormImage.m
// SWFormDemo
//
// Created by zijin on 2018/5/31.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "UIImageView+FormImage.h"
#import "SWFormCompat.h"
#import "UIImageView+EMWebCache.h"
@implementation UIImageView (FormImage)
/**
图片附件条目图片加载
建议在此使用 SDWebImage
*/
- (void)sw_setImageItemWithUrl:(NSURL *)url {
[self sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:SW_PlaceholderImage]];
}
@end
================================================
FILE: SWFormExample/SWFormExample/AppDelegate.h
================================================
//
// AppDelegate.h
// SWFormExample
//
// Created by zijin on 2018/6/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import
@interface AppDelegate : UIResponder
@property (strong, nonatomic) UIWindow *window;
@end
================================================
FILE: SWFormExample/SWFormExample/AppDelegate.m
================================================
//
// AppDelegate.m
// SWFormExample
//
// Created by zijin on 2018/6/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
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 invalidate graphics rendering callbacks. 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 active 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: SWFormExample/SWFormExample/Assets.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: SWFormExample/SWFormExample/Assets.xcassets/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: SWFormExample/SWFormExample/Base.lproj/LaunchScreen.storyboard
================================================
================================================
FILE: SWFormExample/SWFormExample/Base.lproj/Main.storyboard
================================================
================================================
FILE: SWFormExample/SWFormExample/Info.plist
================================================
NSPhotoLibraryUsageDescription
需要访问相册
NSCameraUsageDescription
需要使用相机
NSAppTransportSecurity
NSAllowsArbitraryLoads
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
APPL
CFBundleShortVersionString
1.0
CFBundleVersion
1
LSRequiresIPhoneOS
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
Main
UIRequiredDeviceCapabilities
armv7
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UISupportedInterfaceOrientations~ipad
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
================================================
FILE: SWFormExample/SWFormExample/SWFormCommonController.h
================================================
//
// SWFormCommonController.h
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseController.h"
@interface SWFormCommonController : SWFormBaseController
@end
================================================
FILE: SWFormExample/SWFormExample/SWFormCommonController.m
================================================
//
// SWFormCommonController.m
// SWFormDemo
//
// Created by zijin on 2018/5/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormCommonController.h"
#import "SWForm.h"
#import "SWFormHandler.h"
@interface SWFormCommonController ()
@property (nonatomic, strong) NSArray *genders;
@property (nonatomic, strong) SWFormItem *name;
@property (nonatomic, strong) SWFormItem *age;
@property (nonatomic, strong) SWFormItem *price;
@property (nonatomic, strong) SWFormItem *gender;
@property (nonatomic, strong) SWFormItem *intro;
@property (nonatomic, strong) SWFormItem *image;
@end
@implementation SWFormCommonController
- (void)dealloc
{
NSLog(@"dealloc");
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.genders = @[@"男",@"女"];
[self datas];
}
/**
数据源处理
*/
- (void)datas {
SWWeakSelf
NSMutableArray *items = [NSMutableArray array];
self.name = SWFormItem_Add(@"姓名", nil, SWFormItemTypeInput, YES, YES, UIKeyboardTypeDefault);
self.name.showLength = YES;
[items addObject:_name];
self.age = SWFormItem_Add(@"年龄", nil, SWFormItemTypeInput, YES, YES, UIKeyboardTypeNumberPad);
self.age.maxInputLength = 3;
self.age.unit = @"岁";
[items addObject:_age];
self.price = SWFormItem_Add(@"价格", nil, SWFormItemTypeInput, YES, YES, UIKeyboardTypeNumberPad);
self.price.maxInputLength = 5;
self.price.itemUnitType = SWFormItemUnitTypeYuan;
[items addObject:_price];
self.gender = SWFormItem_Add(@"性别", nil, SWFormItemTypeSelect, NO, YES, UIKeyboardTypeDefault);
self.gender.itemSelectCompletion = ^(SWFormItem *item) {
UIActionSheet *actionSheet = [[UIActionSheet alloc]initWithTitle:[NSString stringWithFormat:@"请选择%@",item.title] delegate:weakSelf cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:nil];
for (int i = 0; i < weakSelf.genders.count; i++) {
[actionSheet addButtonWithTitle:weakSelf.genders[i]];
}
actionSheet.tag = 10;
[actionSheet showInView:weakSelf.view];
};
[items addObject:_gender];
self.intro = SWFormItem_Add(@"个人简介", @"这是个人简介", SWFormItemTypeTextViewInput, YES, NO, UIKeyboardTypeDefault);
self.intro.showLength = YES;
[items addObject:_intro];
self.image = SWFormItem_Add(@"附件", nil, SWFormItemTypeImage, YES, NO, UIKeyboardTypeDefault);
self.image.images = @[@"http://imgsrc.baidu.com/image/c0%3Dpixel_huitu%2C0%2C0%2C294%2C40/sign=f04093d6da00baa1ae214ffb2e68dc7e/34fae6cd7b899e5160ce642e49a7d933c8950d43.jpg", @"http://imgsrc.baidu.com/image/c0%3Dpixel_huitu%2C0%2C0%2C294%2C40/sign=b360ab28790e0cf3b4fa46bb633e9773/e850352ac65c10387071c8f8b9119313b07e89f8.jpg", @""];
[items addObject:_image];
SWFormSectionItem *sectionItem = SWSectionItem(items);
// sectionItem.headerHeight = 10;
// sectionItem.footerView = [self footerView];
// sectionItem.footerHeight = 80;
[self.mutableItems addObject:sectionItem];
self.formTableView.tableFooterView = [self footerView];
}
/**
创建footer
*/
- (UIView *)footerView {
UIView *footer = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 80)];
UIButton *submitBtn = [UIButton buttonWithType:UIButtonTypeSystem];
submitBtn.bounds = CGRectMake(0, 0, 100, 40);
submitBtn.center = footer.center;
submitBtn.backgroundColor = [UIColor orangeColor];
[submitBtn setTitle:@"提交" forState:UIControlStateNormal];
[submitBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[submitBtn addTarget:self action:@selector(submitAction) forControlEvents:UIControlEventTouchUpInside];
[footer addSubview:submitBtn];
return footer;
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (actionSheet.tag == 10) {
if (buttonIndex != 0) {
self.gender.info = self.genders[buttonIndex-1];
[self.formTableView reloadData];
}
}
}
- (void)submitAction {
[SWFormHandler sw_checkFormNullDataWithWithDatas:self.mutableItems success:^{
NSLog(@"selectImages === %@", self.image.selectImages);
//NSLog(@"images === %@", image.images);
NSLog(@"gender === %@", self.gender.info);
NSLog(@"name === %@", self.name.info);
} failure:^(NSString *error) {
NSLog(@"error====%@",error);
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
================================================
FILE: SWFormExample/SWFormExample/SWFormExample.pch
================================================
//
// SWFormExample.pch
// SWFormExample
//
// Created by zijin on 2018/6/28.
// Copyright © 2018年 selwyn. All rights reserved.
//
#ifndef SWFormExample_pch
#define SWFormExample_pch
#import
// Include any system framework and library headers here that should be included in all compilation units.
// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file.
#endif /* SWFormExample_pch */
================================================
FILE: SWFormExample/SWFormExample/SWFormInfoController.h
================================================
//
// SWFormInfoController.h
// SWFormDemo
//
// Created by zijin on 2018/5/31.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormBaseController.h"
@interface SWFormInfoController : SWFormBaseController
@end
================================================
FILE: SWFormExample/SWFormExample/SWFormInfoController.m
================================================
//
// SWFormInfoController.m
// SWFormDemo
//
// Created by zijin on 2018/5/31.
// Copyright © 2018年 selwyn. All rights reserved.
//
#import "SWFormInfoController.h"
#import "SWForm.h"
typedef void(^EditCompletion)(void);
@interface SWFormInfoController ()
@property (nonatomic, strong) UIButton *editBtn;
@property (nonatomic, copy)EditCompletion editCompletion;
@property (nonatomic, assign) BOOL isEditing;
@end
@implementation SWFormInfoController
- (void)dealloc
{
NSLog(@"dealloc");
}
- (void)viewDidLoad {
[super viewDidLoad];
self.editBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[self.editBtn setTitle:@"编辑" forState:UIControlStateNormal];
[self.editBtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
self.editBtn.titleLabel.font = [UIFont systemFontOfSize:16];
[self.editBtn sizeToFit];
[self.editBtn addTarget:self action:@selector(editeAction) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *editItem = [[UIBarButtonItem alloc]initWithCustomView:self.editBtn];
self.navigationItem.rightBarButtonItem = editItem;
[self datas];
}
/**
数据源处理
*/
- (void)datas {
NSMutableArray *items = [NSMutableArray array];
SWFormItem *name = SWFormItem_Info(@"姓名", @"selwyn", SWFormItemTypeInput);
name.keyboardType = UIReturnKeyDefault;
[items addObject:name];
SWFormItem *age = SWFormItem_Info(@"年龄", @"14", SWFormItemTypeInput);
age.keyboardType = UIKeyboardTypeNumberPad;
[items addObject:age];
SWFormItem *image = SWFormItem_Info(@"图片附件", nil, SWFormItemTypeImage);
image.images = @[@"http://imgsrc.baidu.com/image/c0%3Dpixel_huitu%2C0%2C0%2C294%2C40/sign=f04093d6da00baa1ae214ffb2e68dc7e/34fae6cd7b899e5160ce642e49a7d933c8950d43.jpg", @"http://imgsrc.baidu.com/image/c0%3Dpixel_huitu%2C0%2C0%2C294%2C40/sign=b360ab28790e0cf3b4fa46bb633e9773/e850352ac65c10387071c8f8b9119313b07e89f8.jpg", @""];
[items addObject:image];
SWFormSectionItem *sectionItem = SWSectionItem(items);
[self.mutableItems addObject:sectionItem];
__weak typeof(self) weakSelf = self;
// 编辑按钮点击事件回调
self.editCompletion = ^{
weakSelf.isEditing = !weakSelf.isEditing;
name.editable = weakSelf.isEditing;
age.editable = weakSelf.isEditing;
image.editable = weakSelf.isEditing;
if (weakSelf.isEditing) {
NSLog(@"====编辑中====");
}
else {
NSLog(@"====完成编辑====");
}
[weakSelf.formTableView reloadData];
};
}
- (void)editeAction {
self.editCompletion();
}
- (void)setIsEditing:(BOOL)isEditing {
_isEditing = isEditing;
[self.editBtn setTitle:isEditing ? @"完成":@"编辑" forState:UIControlStateNormal];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/.svn/all-wcprops
================================================
K 25
svn:wc:ra_dav:version-url
V 138
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MBProgressHUD
END
MBProgressHUD.m
K 25
svn:wc:ra_dav:version-url
V 154
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MBProgressHUD/MBProgressHUD.m
END
Factory.m
K 25
svn:wc:ra_dav:version-url
V 148
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MBProgressHUD/Factory.m
END
MBProgressHUD+Add.m
K 25
svn:wc:ra_dav:version-url
V 158
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MBProgressHUD/MBProgressHUD+Add.m
END
MacroDef.h
K 25
svn:wc:ra_dav:version-url
V 149
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MBProgressHUD/MacroDef.h
END
MBProgressHUD.h
K 25
svn:wc:ra_dav:version-url
V 154
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MBProgressHUD/MBProgressHUD.h
END
Factory.h
K 25
svn:wc:ra_dav:version-url
V 148
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MBProgressHUD/Factory.h
END
MBProgressHUD+Add.h
K 25
svn:wc:ra_dav:version-url
V 158
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MBProgressHUD/MBProgressHUD+Add.h
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/.svn/entries
================================================
10
dir
4265
http://bishanwen@121.43.163.28:9090/zhuku/repo/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MBProgressHUD
http://bishanwen@121.43.163.28:9090/zhuku/repo
2017-12-28T02:01:09.072372Z
4261
zhoujun
5738001b-f13b-4815-ac0f-2e2f54dd1d22
MBProgressHUD.m
file
2018-01-16T02:17:09.000000Z
12ee8d84563c68f1cae528ca2e5ea0e4
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
32691
Factory.m
file
2018-01-16T02:17:09.000000Z
ab116194093c76bfdc0d38fc131e8ff6
2017-12-28T02:01:09.072372Z
4261
zhoujun
5537
MBProgressHUD+Add.m
file
2018-01-16T02:17:09.000000Z
237db6ed122879af9aae1098c88834ea
2017-12-28T02:01:09.072372Z
4261
zhoujun
1660
MacroDef.h
file
2018-01-16T02:17:09.000000Z
8f92dd22bc0d8a9358b8564928ab03e1
2017-12-28T02:01:09.072372Z
4261
zhoujun
4598
MBProgressHUD.h
file
2018-01-16T02:17:09.000000Z
8263e84d74c876541cc34ee6e55d5461
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
16269
Factory.h
file
2018-01-16T02:17:09.000000Z
c5c59e89c1da627eeb8e2cfa0aee584e
2017-12-28T02:01:09.072372Z
4261
zhoujun
829
MBProgressHUD+Add.h
file
2018-01-16T02:17:09.000000Z
526d61b0b3d23cd979694b3d43b80003
2017-12-28T02:01:09.072372Z
4261
zhoujun
398
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/.svn/prop-base/MBProgressHUD.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/.svn/prop-base/MBProgressHUD.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/.svn/text-base/Factory.h.svn-base
================================================
//
// Factory.h
// DongDong
//
// Created by jmsre on 15/11/25.
// Copyright © 2015年 wangyang. All rights reserved.
//
#import
#import
#import "MBProgressHUD.h"
@interface Factory : NSObject
// 返回文本size
+ (CGSize)sizeForText:(NSString *)text withFont:(UIFont *)font contraint:(CGSize)constraint;
//提示:
+ (void)MBProgressHUD:(NSString *)text Time:(NSUInteger)time;
+ (void)MBProgressHUDUseblock:(void (^)(MBProgressHUD *hud))block;
// 校验手机号
+ (BOOL)isMobileNumber:(NSString *)mobileNum;
+ (BOOL)isChinaMobile:(NSString *)phoneNum;
+ (BOOL)isChinaUnicom:(NSString *)phoneNum;
+ (BOOL)isChinaTelecom:(NSString *)phoneNum;
+ (NSString *)getPhoneNumType:(NSString *)phoneNum;
//身份证号
+ (BOOL) validateIdentityCard: (NSString *)identityCard;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/.svn/text-base/Factory.m.svn-base
================================================
//
// Factory.m
// DongDong
//
// Created by jmsre on 15/11/25.
// Copyright © 2015年 wangyang. All rights reserved.
//
#import "Factory.h"
#import "MacroDef.h"
@implementation Factory
// 返回文本size
+ (CGSize)sizeForText:(NSString *)text withFont:(UIFont *)font contraint:(CGSize)constraint{
CGSize size = CGSizeZero;
if (IOS7_OR_LATER)
{
size = [text boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading
attributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil]
context:nil].size;
}
// else
// {
// size = [text sizeWithFont:font constrainedToSize:constraint
// lineBreakMode:NSLineBreakByWordWrapping];
// }
return size;
}
//提示:
+ (void)MBProgressHUD:(NSString *)text Time:(NSUInteger)time{
MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithView:[UIApplication sharedApplication].windows[0]];
[[UIApplication sharedApplication].windows[0] addSubview:HUD];
HUD.labelText = text;
HUD.mode = MBProgressHUDModeText;
//指定距离中心点的X轴和Y轴的偏移量,如果不指定则在屏幕中间显示
// HUD.yOffset = 150.0f;
// HUD.xOffset = 100.0f;
__block unsigned int tttt = (unsigned int)time;
[HUD showAnimated:YES whileExecutingBlock:^{
sleep(tttt);
} completionBlock:^{
[HUD removeFromSuperview];
}];
}
+ (void)MBProgressHUDUseblock:(void (^)(MBProgressHUD *hud))block{
MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithView:[UIApplication sharedApplication].windows[0]];
[[UIApplication sharedApplication].windows[0] addSubview:HUD];
HUD.labelText = @"正在加载...";
HUD.mode = MBProgressHUDModeIndeterminate;
//指定距离中心点的X轴和Y轴的偏移量,如果不指定则在屏幕中间显示
// HUD.yOffset = 150.0f;
// HUD.xOffset = 100.0f;
[HUD showAnimated:YES whileExecutingBlock:^{
// sleep(1);
dispatch_sync(dispatch_get_main_queue(), ^{
block(HUD);
});
} completionBlock:^{
[HUD removeFromSuperview];
}];
}
// 校验手机号
+ (BOOL)isMobileNumber:(NSString *)mobileNum
{
if (mobileNum.length != 11)
{
return NO;
}
/**
* 手机号码:
* 13[0-9], 14[5,7], 15[0, 1, 2, 3, 5, 6, 7, 8, 9], 17[6, 7, 8], 18[0-9], 170[0-9]
* 移动号段: 134,135,136,137,138,139,150,151,152,157,158,159,182,183,184,187,188,147,178,1705
* 联通号段: 130,131,132,155,156,185,186,145,176,1709
* 电信号段: 133,153,180,181,189,177,1700
*/
NSString *MOBILE = @"^1(3[0-9]|4[57]|5[0-35-9]|8[0-9]|70)\\d{8}$";
/**
* 中国移动:China Mobile
* 134,135,136,137,138,139,150,151,152,157,158,159,182,183,184,187,188,147,178,1705
*/
NSString *CM = @"(^1(3[4-9]|4[7]|5[0-27-9]|7[8]|8[2-478])\\d{8}$)|(^1705\\d{7}$)";
/**
* 中国联通:China Unicom
* 130,131,132,155,156,185,186,145,176,1709
*/
NSString *CU = @"(^1(3[0-2]|4[5]|5[56]|7[6]|8[56])\\d{8}$)|(^1709\\d{7}$)";
/**
* 中国电信:China Telecom
* 133,153,180,181,189,177,1700
*/
NSString *CT = @"(^1(33|53|77|8[019])\\d{8}$)|(^1700\\d{7}$)";
NSPredicate *regextestmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", MOBILE];
NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];
NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];
NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];
if (([regextestmobile evaluateWithObject:mobileNum] == YES)
|| ([regextestcm evaluateWithObject:mobileNum] == YES)
|| ([regextestct evaluateWithObject:mobileNum] == YES)
|| ([regextestcu evaluateWithObject:mobileNum] == YES))
{
return YES;
}
else
{
return NO;
}
}
+ (BOOL)isChinaMobile:(NSString *)phoneNum
{
NSString *CM = @"(^1(3[4-9]|4[7]|5[0-27-9]|7[8]|8[2-478])\\d{8}$)|(^1705\\d{7}$)";
NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];
return [regextestcm evaluateWithObject:phoneNum];
}
+ (BOOL)isChinaUnicom:(NSString *)phoneNum
{
NSString *CU = @"(^1(3[0-2]|4[5]|5[56]|7[6]|8[56])\\d{8}$)|(^1709\\d{7}$)";
NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];
return [regextestcu evaluateWithObject:phoneNum];
}
+ (BOOL)isChinaTelecom:(NSString *)phoneNum
{
NSString *CT = @"(^1(33|53|77|8[019])\\d{8}$)|(^1700\\d{7}$)";
NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];
return [regextestct evaluateWithObject:phoneNum];
}
+ (NSString *)getPhoneNumType:(NSString *)phoneNum
{
return [self isChinaMobile:phoneNum]? @"中国移动": ([self isChinaUnicom:phoneNum]? @"中国联通":([self isChinaTelecom:phoneNum]? @"中国电信": @"未知"));
}
//身份证号
+ (BOOL) validateIdentityCard: (NSString *)identityCard
{
BOOL flag;
if (identityCard.length <= 0) {
flag = NO;
return flag;
}
NSString *regex2 = @"^(\\d{14}|\\d{17})(\\d|[xX])$";
NSPredicate *identityCardPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex2];
return [identityCardPredicate evaluateWithObject:identityCard];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/.svn/text-base/MBProgressHUD+Add.h.svn-base
================================================
//
// MBProgressHUD+Add.h
// 视频客户端
//
// Created by mj on 13-4-18.
// Copyright (c) 2013年 itcast. All rights reserved.
//
#import "MBProgressHUD.h"
@interface MBProgressHUD (Add)
+ (void)showError:(NSString *)error toView:(UIView *)view;
+ (void)showSuccess:(NSString *)success toView:(UIView *)view;
+ (MBProgressHUD *)showMessag:(NSString *)message toView:(UIView *)view;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/.svn/text-base/MBProgressHUD+Add.m.svn-base
================================================
//
// MBProgressHUD+Add.m
// 视频客户端
//
// Created by mj on 13-4-18.
// Copyright (c) 2013年 itcast. All rights reserved.
//
#import "MBProgressHUD+Add.h"
@implementation MBProgressHUD (Add)
#pragma mark 显示信息
+ (void)show:(NSString *)text icon:(NSString *)icon view:(UIView *)view
{
if (view == nil) view = [UIApplication sharedApplication].keyWindow;
// 快速显示一个提示信息
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
hud.labelText = text;
// 设置图片
hud.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"MBProgressHUD.bundle/%@", icon]]];
// 再设置模式
hud.mode = MBProgressHUDModeCustomView;
// 隐藏时候从父控件中移除
hud.removeFromSuperViewOnHide = YES;
// 1秒之后再消失
[hud hide:YES afterDelay:0.7];
}
#pragma mark 显示错误信息
+ (void)showError:(NSString *)error toView:(UIView *)view{
[self show:error icon:@"error.png" view:view];
}
+ (void)showSuccess:(NSString *)success toView:(UIView *)view
{
[self show:success icon:@"success.png" view:view];
}
#pragma mark 显示一些信息
+ (MBProgressHUD *)showMessag:(NSString *)message toView:(UIView *)view {
if (view == nil) view = [UIApplication sharedApplication].keyWindow;
// 快速显示一个提示信息
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
hud.labelText = message;
// 隐藏时候从父控件中移除
hud.removeFromSuperViewOnHide = YES;
// YES代表需要蒙版效果
hud.dimBackground = YES;
return hud;
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/.svn/text-base/MBProgressHUD.h.svn-base
================================================
//
// MBProgressHUD.h
// Version 0.8
// Created by Matej Bukovinski on 2.4.09.
//
// This code is distributed under the terms and conditions of the MIT license.
// Copyright (c) 2013 Matej Bukovinski
//
// 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.
#import
#import
#import
@protocol MBProgressHUDDelegate;
typedef enum {
/** Progress is shown using an UIActivityIndicatorView. This is the default. */
MBProgressHUDModeIndeterminate,
/** Progress is shown using a round, pie-chart like, progress view. */
MBProgressHUDModeDeterminate,
/** Progress is shown using a horizontal progress bar */
MBProgressHUDModeDeterminateHorizontalBar,
/** Progress is shown using a ring-shaped progress view. */
MBProgressHUDModeAnnularDeterminate,
/** Shows a custom view */
MBProgressHUDModeCustomView,
/** Shows only labels */
MBProgressHUDModeText
} MBProgressHUDMode;
typedef enum {
/** Opacity animation */
MBProgressHUDAnimationFade,
/** Opacity + scale animation */
MBProgressHUDAnimationZoom,
MBProgressHUDAnimationZoomOut = MBProgressHUDAnimationZoom,
MBProgressHUDAnimationZoomIn
} MBProgressHUDAnimation;
#ifndef MB_INSTANCETYPE
#if __has_feature(objc_instancetype)
#define MB_INSTANCETYPE instancetype
#else
#define MB_INSTANCETYPE id
#endif
#endif
#ifndef MB_STRONG
#if __has_feature(objc_arc)
#define MB_STRONG strong
#else
#define MB_STRONG retain
#endif
#endif
#ifndef MB_WEAK
#if __has_feature(objc_arc_weak)
#define MB_WEAK weak
#elif __has_feature(objc_arc)
#define MB_WEAK unsafe_unretained
#else
#define MB_WEAK assign
#endif
#endif
#if NS_BLOCKS_AVAILABLE
typedef void (^MBProgressHUDCompletionBlock)();
#endif
/**
* Displays a simple HUD window containing a progress indicator and two optional labels for short messages.
*
* This is a simple drop-in class for displaying a progress HUD view similar to Apple's private UIProgressHUD class.
* The MBProgressHUD window spans over the entire space given to it by the initWithFrame constructor and catches all
* user input on this region, thereby preventing the user operations on components below the view. The HUD itself is
* drawn centered as a rounded semi-transparent view which resizes depending on the user specified content.
*
* This view supports four modes of operation:
* - MBProgressHUDModeIndeterminate - shows a UIActivityIndicatorView
* - MBProgressHUDModeDeterminate - shows a custom round progress indicator
* - MBProgressHUDModeAnnularDeterminate - shows a custom annular progress indicator
* - MBProgressHUDModeCustomView - shows an arbitrary, user specified view (@see customView)
*
* All three modes can have optional labels assigned:
* - If the labelText property is set and non-empty then a label containing the provided content is placed below the
* indicator view.
* - If also the detailsLabelText property is set then another label is placed below the first label.
*/
@interface MBProgressHUD : UIView
/**
* Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:.
*
* @param view The view that the HUD will be added to
* @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use
* animations while appearing.
* @return A reference to the created HUD.
*
* @see hideHUDForView:animated:
* @see animationType
*/
+ (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated;
/**
* Finds the top-most HUD subview and hides it. The counterpart to this method is showHUDAddedTo:animated:.
*
* @param view The view that is going to be searched for a HUD subview.
* @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
* animations while disappearing.
* @return YES if a HUD was found and removed, NO otherwise.
*
* @see showHUDAddedTo:animated:
* @see animationType
*/
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated;
/**
* Finds all the HUD subviews and hides them.
*
* @param view The view that is going to be searched for HUD subviews.
* @param animated If set to YES the HUDs will disappear using the current animationType. If set to NO the HUDs will not use
* animations while disappearing.
* @return the number of HUDs found and removed.
*
* @see hideHUDForView:animated:
* @see animationType
*/
+ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated;
/**
* Finds the top-most HUD subview and returns it.
*
* @param view The view that is going to be searched.
* @return A reference to the last HUD subview discovered.
*/
+ (MB_INSTANCETYPE)HUDForView:(UIView *)view;
/**
* Finds all HUD subviews and returns them.
*
* @param view The view that is going to be searched.
* @return All found HUD views (array of MBProgressHUD objects).
*/
+ (NSArray *)allHUDsForView:(UIView *)view;
/**
* A convenience constructor that initializes the HUD with the window's bounds. Calls the designated constructor with
* window.bounds as the parameter.
*
* @param window The window instance that will provide the bounds for the HUD. Should be the same instance as
* the HUD's superview (i.e., the window that the HUD will be added to).
*/
- (id)initWithWindow:(UIWindow *)window;
/**
* A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with
* view.bounds as the parameter
*
* @param view The view instance that will provide the bounds for the HUD. Should be the same instance as
* the HUD's superview (i.e., the view that the HUD will be added to).
*/
- (id)initWithView:(UIView *)view;
/**
* Display the HUD. You need to make sure that the main thread completes its run loop soon after this method call so
* the user interface can be updated. Call this method when your task is already set-up to be executed in a new thread
* (e.g., when using something like NSOperation or calling an asynchronous call like NSURLRequest).
*
* @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use
* animations while appearing.
*
* @see animationType
*/
- (void)show:(BOOL)animated;
/**
* Hide the HUD. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to
* hide the HUD when your task completes.
*
* @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
* animations while disappearing.
*
* @see animationType
*/
- (void)hide:(BOOL)animated;
/**
* Hide the HUD after a delay. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to
* hide the HUD when your task completes.
*
* @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
* animations while disappearing.
* @param delay Delay in seconds until the HUD is hidden.
*
* @see animationType
*/
- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay;
/**
* Shows the HUD while a background task is executing in a new thread, then hides the HUD.
*
* This method also takes care of autorelease pools so your method does not have to be concerned with setting up a
* pool.
*
* @param method The method to be executed while the HUD is shown. This method will be executed in a new thread.
* @param target The object that the target method belongs to.
* @param object An optional object to be passed to the method.
* @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will not use
* animations while (dis)appearing.
*/
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated;
#if NS_BLOCKS_AVAILABLE
/**
* Shows the HUD while a block is executing on a background queue, then hides the HUD.
*
* @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
*/
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block;
/**
* Shows the HUD while a block is executing on a background queue, then hides the HUD.
*
* @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
*/
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(MBProgressHUDCompletionBlock)completion;
/**
* Shows the HUD while a block is executing on the specified dispatch queue, then hides the HUD.
*
* @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
*/
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue;
/**
* Shows the HUD while a block is executing on the specified dispatch queue, executes completion block on the main queue, and then hides the HUD.
*
* @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will
* not use animations while (dis)appearing.
* @param block The block to be executed while the HUD is shown.
* @param queue The dispatch queue on which the block should be executed.
* @param completion The block to be executed on completion.
*
* @see completionBlock
*/
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue
completionBlock:(MBProgressHUDCompletionBlock)completion;
/**
* A block that gets called after the HUD was completely hidden.
*/
@property (copy) MBProgressHUDCompletionBlock completionBlock;
#endif
/**
* MBProgressHUD operation mode. The default is MBProgressHUDModeIndeterminate.
*
* @see MBProgressHUDMode
*/
@property (assign) MBProgressHUDMode mode;
/**
* The animation type that should be used when the HUD is shown and hidden.
*
* @see MBProgressHUDAnimation
*/
@property (assign) MBProgressHUDAnimation animationType;
/**
* The UIView (e.g., a UIImageView) to be shown when the HUD is in MBProgressHUDModeCustomView.
* For best results use a 37 by 37 pixel view (so the bounds match the built in indicator bounds).
*/
@property (MB_STRONG) UIView *customView;
/**
* The HUD delegate object.
*
* @see MBProgressHUDDelegate
*/
@property (MB_WEAK) id delegate;
/**
* An optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit
* the entire text. If the text is too long it will get clipped by displaying "..." at the end. If left unchanged or
* set to @"", then no message is displayed.
*/
@property (copy) NSString *labelText;
/**
* An optional details message displayed below the labelText message. This message is displayed only if the labelText
* property is also set and is different from an empty string (@""). The details text can span multiple lines.
*/
@property (copy) NSString *detailsLabelText;
/**
* The opacity of the HUD window. Defaults to 0.8 (80% opacity).
*/
@property (assign) float opacity;
/**
* The color of the HUD window. Defaults to black. If this property is set, color is set using
* this UIColor and the opacity property is not used. using retain because performing copy on
* UIColor base colors (like [UIColor greenColor]) cause problems with the copyZone.
*/
@property (MB_STRONG) UIColor *color;
/**
* The x-axis offset of the HUD relative to the centre of the superview.
*/
@property (assign) float xOffset;
/**
* The y-axis offset of the HUD relative to the centre of the superview.
*/
@property (assign) float yOffset;
/**
* The amount of space between the HUD edge and the HUD elements (labels, indicators or custom views).
* Defaults to 20.0
*/
@property (assign) float margin;
/**
* Cover the HUD background view with a radial gradient.
*/
@property (assign) BOOL dimBackground;
/*
* Grace period is the time (in seconds) that the invoked method may be run without
* showing the HUD. If the task finishes before the grace time runs out, the HUD will
* not be shown at all.
* This may be used to prevent HUD display for very short tasks.
* Defaults to 0 (no grace time).
* Grace time functionality is only supported when the task status is known!
* @see taskInProgress
*/
@property (assign) float graceTime;
/**
* The minimum time (in seconds) that the HUD is shown.
* This avoids the problem of the HUD being shown and than instantly hidden.
* Defaults to 0 (no minimum show time).
*/
@property (assign) float minShowTime;
/**
* Indicates that the executed operation is in progress. Needed for correct graceTime operation.
* If you don't set a graceTime (different than 0.0) this does nothing.
* This property is automatically set when using showWhileExecuting:onTarget:withObject:animated:.
* When threading is done outside of the HUD (i.e., when the show: and hide: methods are used directly),
* you need to set this property when your task starts and completes in order to have normal graceTime
* functionality.
*/
@property (assign) BOOL taskInProgress;
/**
* Removes the HUD from its parent view when hidden.
* Defaults to NO.
*/
@property (assign) BOOL removeFromSuperViewOnHide;
/**
* Font to be used for the main label. Set this property if the default is not adequate.
*/
@property (MB_STRONG) UIFont* labelFont;
/**
* Font to be used for the details label. Set this property if the default is not adequate.
*/
@property (MB_STRONG) UIFont* detailsLabelFont;
/**
* The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0.
*/
@property (assign) float progress;
/**
* The minimum size of the HUD bezel. Defaults to CGSizeZero (no minimum size).
*/
@property (assign) CGSize minSize;
/**
* Force the HUD dimensions to be equal if possible.
*/
@property (assign, getter = isSquare) BOOL square;
@end
@protocol MBProgressHUDDelegate
@optional
/**
* Called after the HUD was fully hidden from the screen.
*/
- (void)hudWasHidden:(MBProgressHUD *)hud;
@end
/**
* A progress view for showing definite progress by filling up a circle (pie chart).
*/
@interface MBRoundProgressView : UIView
/**
* Progress (0.0 to 1.0)
*/
@property (nonatomic, assign) float progress;
/**
* Indicator progress color.
* Defaults to white [UIColor whiteColor]
*/
@property (nonatomic, MB_STRONG) UIColor *progressTintColor;
/**
* Indicator background (non-progress) color.
* Defaults to translucent white (alpha 0.1)
*/
@property (nonatomic, MB_STRONG) UIColor *backgroundTintColor;
/*
* Display mode - NO = round or YES = annular. Defaults to round.
*/
@property (nonatomic, assign, getter = isAnnular) BOOL annular;
@end
/**
* A flat bar progress view.
*/
@interface MBBarProgressView : UIView
/**
* Progress (0.0 to 1.0)
*/
@property (nonatomic, assign) float progress;
/**
* Bar border line color.
* Defaults to white [UIColor whiteColor].
*/
@property (nonatomic, MB_STRONG) UIColor *lineColor;
/**
* Bar background color.
* Defaults to clear [UIColor clearColor];
*/
@property (nonatomic, MB_STRONG) UIColor *progressRemainingColor;
/**
* Bar progress color.
* Defaults to white [UIColor whiteColor].
*/
@property (nonatomic, MB_STRONG) UIColor *progressColor;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/.svn/text-base/MBProgressHUD.m.svn-base
================================================
//
// MBProgressHUD.m
// Version 0.8
// Created by Matej Bukovinski on 2.4.09.
//
#import "MBProgressHUD.h"
#if __has_feature(objc_arc)
#define MB_AUTORELEASE(exp) exp
#define MB_RELEASE(exp) exp
#define MB_RETAIN(exp) exp
#else
#define MB_AUTORELEASE(exp) [exp autorelease]
#define MB_RELEASE(exp) [exp release]
#define MB_RETAIN(exp) [exp retain]
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000
#define MBLabelAlignmentCenter NSTextAlignmentCenter
#else
#define MBLabelAlignmentCenter UITextAlignmentCenter
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
#define MB_TEXTSIZE(text, font) [text length] > 0 ? [text \
sizeWithAttributes:@{NSFontAttributeName:font}] : CGSizeZero;
#else
#define MB_TEXTSIZE(text, font) [text length] > 0 ? [text sizeWithFont:font] : CGSizeZero;
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
#define MB_MULTILINE_TEXTSIZE(text, font, maxSize, mode) [text length] > 0 ? [text \
boundingRectWithSize:maxSize options:(NSStringDrawingUsesLineFragmentOrigin) \
attributes:@{NSFontAttributeName:font} context:nil].size : CGSizeZero;
#else
#define MB_MULTILINE_TEXTSIZE(text, font, maxSize, mode) [text length] > 0 ? [text \
sizeWithFont:font constrainedToSize:maxSize lineBreakMode:mode] : CGSizeZero;
#endif
static const CGFloat kPadding = 4.f;
static const CGFloat kLabelFontSize = 16.f;
static const CGFloat kDetailsLabelFontSize = 12.f;
@interface MBProgressHUD ()
- (void)setupLabels;
- (void)registerForKVO;
- (void)unregisterFromKVO;
- (NSArray *)observableKeypaths;
- (void)registerForNotifications;
- (void)unregisterFromNotifications;
- (void)updateUIForKeypath:(NSString *)keyPath;
- (void)hideUsingAnimation:(BOOL)animated;
- (void)showUsingAnimation:(BOOL)animated;
- (void)done;
- (void)updateIndicators;
- (void)handleGraceTimer:(NSTimer *)theTimer;
- (void)handleMinShowTimer:(NSTimer *)theTimer;
- (void)setTransformForCurrentOrientation:(BOOL)animated;
- (void)cleanUp;
- (void)launchExecution;
- (void)deviceOrientationDidChange:(NSNotification *)notification;
- (void)hideDelayed:(NSNumber *)animated;
@property (atomic, MB_STRONG) UIView *indicator;
@property (atomic, MB_STRONG) NSTimer *graceTimer;
@property (atomic, MB_STRONG) NSTimer *minShowTimer;
@property (atomic, MB_STRONG) NSDate *showStarted;
@property (atomic, assign) CGSize size;
@end
@implementation MBProgressHUD {
BOOL useAnimation;
SEL methodForExecution;
id targetForExecution;
id objectForExecution;
UILabel *label;
UILabel *detailsLabel;
BOOL isFinished;
CGAffineTransform rotationTransform;
}
#pragma mark - Properties
@synthesize animationType;
@synthesize delegate;
@synthesize opacity;
@synthesize color;
@synthesize labelFont;
@synthesize detailsLabelFont;
@synthesize indicator;
@synthesize xOffset;
@synthesize yOffset;
@synthesize minSize;
@synthesize square;
@synthesize margin;
@synthesize dimBackground;
@synthesize graceTime;
@synthesize minShowTime;
@synthesize graceTimer;
@synthesize minShowTimer;
@synthesize taskInProgress;
@synthesize removeFromSuperViewOnHide;
@synthesize customView;
@synthesize showStarted;
@synthesize mode;
@synthesize labelText;
@synthesize detailsLabelText;
@synthesize progress;
@synthesize size;
#if NS_BLOCKS_AVAILABLE
@synthesize completionBlock;
#endif
#pragma mark - Class methods
+ (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
MBProgressHUD *hud = [[self alloc] initWithView:view];
[view addSubview:hud];
[hud show:animated];
return MB_AUTORELEASE(hud);
}
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated {
MBProgressHUD *hud = [self HUDForView:view];
if (hud != nil) {
hud.removeFromSuperViewOnHide = YES;
[hud hide:animated];
return YES;
}
return NO;
}
+ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated {
NSArray *huds = [MBProgressHUD allHUDsForView:view];
for (MBProgressHUD *hud in huds) {
hud.removeFromSuperViewOnHide = YES;
[hud hide:animated];
}
return [huds count];
}
+ (MB_INSTANCETYPE)HUDForView:(UIView *)view {
NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
for (UIView *subview in subviewsEnum) {
if ([subview isKindOfClass:self]) {
return (MBProgressHUD *)subview;
}
}
return nil;
}
+ (NSArray *)allHUDsForView:(UIView *)view {
NSMutableArray *huds = [NSMutableArray array];
NSArray *subviews = view.subviews;
for (UIView *aView in subviews) {
if ([aView isKindOfClass:self]) {
[huds addObject:aView];
}
}
return [NSArray arrayWithArray:huds];
}
#pragma mark - Lifecycle
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Set default values for properties
self.animationType = MBProgressHUDAnimationFade;
self.mode = MBProgressHUDModeIndeterminate;
self.labelText = nil;
self.detailsLabelText = nil;
self.opacity = 0.8f;
self.color = nil;
self.labelFont = [UIFont boldSystemFontOfSize:kLabelFontSize];
self.detailsLabelFont = [UIFont boldSystemFontOfSize:kDetailsLabelFontSize];
self.xOffset = 0.0f;
self.yOffset = 0.0f;
self.dimBackground = NO;
self.margin = 20.0f;
self.graceTime = 0.0f;
self.minShowTime = 0.0f;
self.removeFromSuperViewOnHide = NO;
self.minSize = CGSizeZero;
self.square = NO;
self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin
| UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
// Transparent background
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
// Make it invisible for now
self.alpha = 0.0f;
taskInProgress = NO;
rotationTransform = CGAffineTransformIdentity;
[self setupLabels];
[self updateIndicators];
[self registerForKVO];
[self registerForNotifications];
}
return self;
}
- (id)initWithView:(UIView *)view {
// NSAssert(view, @"View must not be nil.");
return [self initWithFrame:view.bounds];
}
- (id)initWithWindow:(UIWindow *)window {
return [self initWithView:window];
}
- (void)dealloc {
[self unregisterFromNotifications];
[self unregisterFromKVO];
#if !__has_feature(objc_arc)
[color release];
[indicator release];
[label release];
[detailsLabel release];
[labelText release];
[detailsLabelText release];
[graceTimer release];
[minShowTimer release];
[showStarted release];
[customView release];
#if NS_BLOCKS_AVAILABLE
[completionBlock release];
#endif
[super dealloc];
#endif
}
#pragma mark - Show & hide
- (void)show:(BOOL)animated {
useAnimation = animated;
// If the grace time is set postpone the HUD display
if (self.graceTime > 0.0) {
self.graceTimer = [NSTimer scheduledTimerWithTimeInterval:self.graceTime target:self
selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
}
// ... otherwise show the HUD imediately
else {
[self setNeedsDisplay];
[self showUsingAnimation:useAnimation];
}
}
- (void)hide:(BOOL)animated {
useAnimation = animated;
// If the minShow time is set, calculate how long the hud was shown,
// and pospone the hiding operation if necessary
if (self.minShowTime > 0.0 && showStarted) {
NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:showStarted];
if (interv < self.minShowTime) {
self.minShowTimer = [NSTimer scheduledTimerWithTimeInterval:(self.minShowTime - interv) target:self
selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO];
return;
}
}
// ... otherwise hide the HUD immediately
[self hideUsingAnimation:useAnimation];
}
- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay {
[self performSelector:@selector(hideDelayed:) withObject:[NSNumber numberWithBool:animated] afterDelay:delay];
}
- (void)hideDelayed:(NSNumber *)animated {
[self hide:[animated boolValue]];
}
#pragma mark - Timer callbacks
- (void)handleGraceTimer:(NSTimer *)theTimer {
// Show the HUD only if the task is still running
if (taskInProgress) {
[self setNeedsDisplay];
[self showUsingAnimation:useAnimation];
}
}
- (void)handleMinShowTimer:(NSTimer *)theTimer {
[self hideUsingAnimation:useAnimation];
}
#pragma mark - View Hierrarchy
- (void)didMoveToSuperview {
// We need to take care of rotation ourselfs if we're adding the HUD to a window
if ([self.superview isKindOfClass:[UIWindow class]]) {
[self setTransformForCurrentOrientation:NO];
}
}
#pragma mark - Internal show & hide operations
- (void)showUsingAnimation:(BOOL)animated {
if (animated && animationType == MBProgressHUDAnimationZoomIn) {
self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f));
} else if (animated && animationType == MBProgressHUDAnimationZoomOut) {
self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f));
}
self.showStarted = [NSDate date];
// Fade in
if (animated) {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.30];
self.alpha = 1.0f;
if (animationType == MBProgressHUDAnimationZoomIn || animationType == MBProgressHUDAnimationZoomOut) {
self.transform = rotationTransform;
}
[UIView commitAnimations];
}
else {
self.alpha = 1.0f;
}
}
- (void)hideUsingAnimation:(BOOL)animated {
// Fade out
if (animated && showStarted) {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.30];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationFinished:finished:context:)];
// 0.02 prevents the hud from passing through touches during the animation the hud will get completely hidden
// in the done method
if (animationType == MBProgressHUDAnimationZoomIn) {
self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f));
} else if (animationType == MBProgressHUDAnimationZoomOut) {
self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f));
}
self.alpha = 0.02f;
[UIView commitAnimations];
}
else {
self.alpha = 0.0f;
[self done];
}
self.showStarted = nil;
}
- (void)animationFinished:(NSString *)animationID finished:(BOOL)finished context:(void*)context {
[self done];
}
- (void)done {
isFinished = YES;
self.alpha = 0.0f;
if (removeFromSuperViewOnHide) {
[self removeFromSuperview];
}
#if NS_BLOCKS_AVAILABLE
if (self.completionBlock) {
self.completionBlock();
self.completionBlock = NULL;
}
#endif
if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
[delegate performSelector:@selector(hudWasHidden:) withObject:self];
}
}
#pragma mark - Threading
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
methodForExecution = method;
targetForExecution = MB_RETAIN(target);
objectForExecution = MB_RETAIN(object);
// Launch execution in new thread
self.taskInProgress = YES;
[NSThread detachNewThreadSelector:@selector(launchExecution) toTarget:self withObject:nil];
// Show HUD view
[self show:animated];
}
#if NS_BLOCKS_AVAILABLE
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
[self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL];
}
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(void (^)())completion {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
[self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:completion];
}
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue {
[self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL];
}
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue
completionBlock:(MBProgressHUDCompletionBlock)completion {
self.taskInProgress = YES;
self.completionBlock = completion;
dispatch_async(queue, ^(void) {
block();
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self cleanUp];
});
});
[self show:animated];
}
#endif
- (void)launchExecution {
@autoreleasepool {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
// Start executing the requested task
[targetForExecution performSelector:methodForExecution withObject:objectForExecution];
#pragma clang diagnostic pop
// Task completed, update view in main thread (note: view operations should
// be done only in the main thread)
[self performSelectorOnMainThread:@selector(cleanUp) withObject:nil waitUntilDone:NO];
}
}
- (void)cleanUp {
taskInProgress = NO;
#if !__has_feature(objc_arc)
[targetForExecution release];
[objectForExecution release];
#else
targetForExecution = nil;
objectForExecution = nil;
#endif
[self hide:useAnimation];
}
#pragma mark - UI
- (void)setupLabels {
label = [[UILabel alloc] initWithFrame:self.bounds];
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = MBLabelAlignmentCenter;
label.opaque = NO;
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor whiteColor];
label.font = self.labelFont;
label.text = self.labelText;
[self addSubview:label];
detailsLabel = [[UILabel alloc] initWithFrame:self.bounds];
detailsLabel.font = self.detailsLabelFont;
detailsLabel.adjustsFontSizeToFitWidth = NO;
detailsLabel.textAlignment = MBLabelAlignmentCenter;
detailsLabel.opaque = NO;
detailsLabel.backgroundColor = [UIColor clearColor];
detailsLabel.textColor = [UIColor whiteColor];
detailsLabel.numberOfLines = 0;
detailsLabel.font = self.detailsLabelFont;
detailsLabel.text = self.detailsLabelText;
[self addSubview:detailsLabel];
}
- (void)updateIndicators {
BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]];
BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]];
if (mode == MBProgressHUDModeIndeterminate && !isActivityIndicator) {
// Update to indeterminate indicator
[indicator removeFromSuperview];
self.indicator = MB_AUTORELEASE([[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]);
[(UIActivityIndicatorView *)indicator startAnimating];
[self addSubview:indicator];
}
else if (mode == MBProgressHUDModeDeterminateHorizontalBar) {
// Update to bar determinate indicator
[indicator removeFromSuperview];
self.indicator = MB_AUTORELEASE([[MBBarProgressView alloc] init]);
[self addSubview:indicator];
}
else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) {
if (!isRoundIndicator) {
// Update to determinante indicator
[indicator removeFromSuperview];
self.indicator = MB_AUTORELEASE([[MBRoundProgressView alloc] init]);
[self addSubview:indicator];
}
if (mode == MBProgressHUDModeAnnularDeterminate) {
[(MBRoundProgressView *)indicator setAnnular:YES];
}
}
else if (mode == MBProgressHUDModeCustomView && customView != indicator) {
// Update custom view indicator
[indicator removeFromSuperview];
self.indicator = customView;
[self addSubview:indicator];
} else if (mode == MBProgressHUDModeText) {
[indicator removeFromSuperview];
self.indicator = nil;
}
}
#pragma mark - Layout
- (void)layoutSubviews {
// Entirely cover the parent view
UIView *parent = self.superview;
if (parent) {
self.frame = parent.bounds;
}
CGRect bounds = self.bounds;
// Determine the total widt and height needed
CGFloat maxWidth = bounds.size.width - 4 * margin;
CGSize totalSize = CGSizeZero;
CGRect indicatorF = indicator.bounds;
indicatorF.size.width = MIN(indicatorF.size.width, maxWidth);
totalSize.width = MAX(totalSize.width, indicatorF.size.width);
totalSize.height += indicatorF.size.height;
CGSize labelSize = MB_TEXTSIZE(label.text, label.font);
labelSize.width = MIN(labelSize.width, maxWidth);
totalSize.width = MAX(totalSize.width, labelSize.width);
totalSize.height += labelSize.height;
if (labelSize.height > 0.f && indicatorF.size.height > 0.f) {
totalSize.height += kPadding;
}
CGFloat remainingHeight = bounds.size.height - totalSize.height - kPadding - 4 * margin;
CGSize maxSize = CGSizeMake(maxWidth, remainingHeight);
CGSize detailsLabelSize = MB_MULTILINE_TEXTSIZE(detailsLabel.text, detailsLabel.font, maxSize, detailsLabel.lineBreakMode);
totalSize.width = MAX(totalSize.width, detailsLabelSize.width);
totalSize.height += detailsLabelSize.height;
if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) {
totalSize.height += kPadding;
}
totalSize.width += 2 * margin;
totalSize.height += 2 * margin;
// Position elements
CGFloat yPos = roundf(((bounds.size.height - totalSize.height) / 2)) + margin + yOffset;
CGFloat xPos = xOffset;
indicatorF.origin.y = yPos;
indicatorF.origin.x = roundf((bounds.size.width - indicatorF.size.width) / 2) + xPos;
indicator.frame = indicatorF;
yPos += indicatorF.size.height;
if (labelSize.height > 0.f && indicatorF.size.height > 0.f) {
yPos += kPadding;
}
CGRect labelF;
labelF.origin.y = yPos;
labelF.origin.x = roundf((bounds.size.width - labelSize.width) / 2) + xPos;
labelF.size = labelSize;
label.frame = labelF;
yPos += labelF.size.height;
if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) {
yPos += kPadding;
}
CGRect detailsLabelF;
detailsLabelF.origin.y = yPos;
detailsLabelF.origin.x = roundf((bounds.size.width - detailsLabelSize.width) / 2) + xPos;
detailsLabelF.size = detailsLabelSize;
detailsLabel.frame = detailsLabelF;
// Enforce minsize and quare rules
if (square) {
CGFloat max = MAX(totalSize.width, totalSize.height);
if (max <= bounds.size.width - 2 * margin) {
totalSize.width = max;
}
if (max <= bounds.size.height - 2 * margin) {
totalSize.height = max;
}
}
if (totalSize.width < minSize.width) {
totalSize.width = minSize.width;
}
if (totalSize.height < minSize.height) {
totalSize.height = minSize.height;
}
self.size = totalSize;
}
#pragma mark BG Drawing
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
if (self.dimBackground) {
//Gradient colours
size_t gradLocationsNum = 2;
CGFloat gradLocations[2] = {0.0f, 1.0f};
CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum);
CGColorSpaceRelease(colorSpace);
//Gradient center
CGPoint gradCenter= CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
//Gradient radius
float gradRadius = MIN(self.bounds.size.width , self.bounds.size.height) ;
//Gradient draw
CGContextDrawRadialGradient (context, gradient, gradCenter,
0, gradCenter, gradRadius,
kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradient);
}
// Set background rect color
if (self.color) {
CGContextSetFillColorWithColor(context, self.color.CGColor);
} else {
CGContextSetGrayFillColor(context, 0.0f, self.opacity);
}
// Center HUD
CGRect allRect = self.bounds;
// Draw rounded HUD backgroud rect
CGRect boxRect = CGRectMake(roundf((allRect.size.width - size.width) / 2) + self.xOffset,
roundf((allRect.size.height - size.height) / 2) + self.yOffset, size.width, size.height);
float radius = 10.0f;
CGContextBeginPath(context);
CGContextMoveToPoint(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect));
CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMinY(boxRect) + radius, radius, 3 * (float)M_PI / 2, 0, 0);
CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMaxY(boxRect) - radius, radius, 0, (float)M_PI / 2, 0);
CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMaxY(boxRect) - radius, radius, (float)M_PI / 2, (float)M_PI, 0);
CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect) + radius, radius, (float)M_PI, 3 * (float)M_PI / 2, 0);
CGContextClosePath(context);
CGContextFillPath(context);
UIGraphicsPopContext();
}
#pragma mark - KVO
- (void)registerForKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
}
}
- (void)unregisterFromKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self removeObserver:self forKeyPath:keyPath];
}
}
- (NSArray *)observableKeypaths {
return [NSArray arrayWithObjects:@"mode", @"customView", @"labelText", @"labelFont",
@"detailsLabelText", @"detailsLabelFont", @"progress", nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(updateUIForKeypath:) withObject:keyPath waitUntilDone:NO];
} else {
[self updateUIForKeypath:keyPath];
}
}
- (void)updateUIForKeypath:(NSString *)keyPath {
if ([keyPath isEqualToString:@"mode"] || [keyPath isEqualToString:@"customView"]) {
[self updateIndicators];
} else if ([keyPath isEqualToString:@"labelText"]) {
label.text = self.labelText;
} else if ([keyPath isEqualToString:@"labelFont"]) {
label.font = self.labelFont;
} else if ([keyPath isEqualToString:@"detailsLabelText"]) {
detailsLabel.text = self.detailsLabelText;
} else if ([keyPath isEqualToString:@"detailsLabelFont"]) {
detailsLabel.font = self.detailsLabelFont;
} else if ([keyPath isEqualToString:@"progress"]) {
if ([indicator respondsToSelector:@selector(setProgress:)]) {
[(id)indicator setProgress:progress];
}
return;
}
[self setNeedsLayout];
[self setNeedsDisplay];
}
#pragma mark - Notifications
- (void)registerForNotifications {
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(deviceOrientationDidChange:)
name:UIDeviceOrientationDidChangeNotification object:nil];
}
- (void)unregisterFromNotifications {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)deviceOrientationDidChange:(NSNotification *)notification {
UIView *superview = self.superview;
if (!superview) {
return;
} else if ([superview isKindOfClass:[UIWindow class]]) {
[self setTransformForCurrentOrientation:YES];
} else {
self.bounds = self.superview.bounds;
[self setNeedsDisplay];
}
}
- (void)setTransformForCurrentOrientation:(BOOL)animated {
// Stay in sync with the superview
if (self.superview) {
self.bounds = self.superview.bounds;
[self setNeedsDisplay];
}
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
CGFloat radians = 0;
if (UIInterfaceOrientationIsLandscape(orientation)) {
if (orientation == UIInterfaceOrientationLandscapeLeft) { radians = -(CGFloat)M_PI_2; }
else { radians = (CGFloat)M_PI_2; }
// Window coordinates differ!
self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width);
} else {
if (orientation == UIInterfaceOrientationPortraitUpsideDown) { radians = (CGFloat)M_PI; }
else { radians = 0; }
}
rotationTransform = CGAffineTransformMakeRotation(radians);
if (animated) {
[UIView beginAnimations:nil context:nil];
}
[self setTransform:rotationTransform];
if (animated) {
[UIView commitAnimations];
}
}
@end
@implementation MBRoundProgressView
#pragma mark - Lifecycle
- (id)init {
return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)];
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
_progress = 0.f;
_annular = NO;
_progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f];
_backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f];
[self registerForKVO];
}
return self;
}
- (void)dealloc {
[self unregisterFromKVO];
#if !__has_feature(objc_arc)
[_progressTintColor release];
[_backgroundTintColor release];
[super dealloc];
#endif
}
#pragma mark - Drawing
- (void)drawRect:(CGRect)rect {
CGRect allRect = self.bounds;
CGRect circleRect = CGRectInset(allRect, 2.0f, 2.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
if (_annular) {
// Draw background
CGFloat lineWidth = 5.f;
UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath];
processBackgroundPath.lineWidth = lineWidth;
processBackgroundPath.lineCapStyle = kCGLineCapRound;
CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
CGFloat radius = (self.bounds.size.width - lineWidth)/2;
CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
CGFloat endAngle = (2 * (float)M_PI) + startAngle;
[processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
[_backgroundTintColor set];
[processBackgroundPath stroke];
// Draw progress
UIBezierPath *processPath = [UIBezierPath bezierPath];
processPath.lineCapStyle = kCGLineCapRound;
processPath.lineWidth = lineWidth;
endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
[processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
[_progressTintColor set];
[processPath stroke];
} else {
// Draw background
[_progressTintColor setStroke];
[_backgroundTintColor setFill];
CGContextSetLineWidth(context, 2.0f);
CGContextFillEllipseInRect(context, circleRect);
CGContextStrokeEllipseInRect(context, circleRect);
// Draw progress
CGPoint center = CGPointMake(allRect.size.width / 2, allRect.size.height / 2);
CGFloat radius = (allRect.size.width - 4) / 2;
CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
CGFloat endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // white
CGContextMoveToPoint(context, center.x, center.y);
CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0);
CGContextClosePath(context);
CGContextFillPath(context);
}
}
#pragma mark - KVO
- (void)registerForKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
}
}
- (void)unregisterFromKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self removeObserver:self forKeyPath:keyPath];
}
}
- (NSArray *)observableKeypaths {
return [NSArray arrayWithObjects:@"progressTintColor", @"backgroundTintColor", @"progress", @"annular", nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
[self setNeedsDisplay];
}
@end
@implementation MBBarProgressView
#pragma mark - Lifecycle
- (id)init {
return [self initWithFrame:CGRectMake(.0f, .0f, 120.0f, 20.0f)];
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_progress = 0.f;
_lineColor = [UIColor whiteColor];
_progressColor = [UIColor whiteColor];
_progressRemainingColor = [UIColor clearColor];
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
[self registerForKVO];
}
return self;
}
- (void)dealloc {
[self unregisterFromKVO];
#if !__has_feature(objc_arc)
[_lineColor release];
[_progressColor release];
[_progressRemainingColor release];
[super dealloc];
#endif
}
#pragma mark - Drawing
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// setup properties
CGContextSetLineWidth(context, 2);
CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]);
CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]);
// draw line border
float radius = (rect.size.height / 2) - 2;
CGContextMoveToPoint(context, 2, rect.size.height/2);
CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2);
CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius);
CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius);
CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2);
CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
CGContextFillPath(context);
// draw progress background
CGContextMoveToPoint(context, 2, rect.size.height/2);
CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2);
CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius);
CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius);
CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2);
CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
CGContextStrokePath(context);
// setup to draw progress color
CGContextSetFillColorWithColor(context, [_progressColor CGColor]);
radius = radius - 2;
float amount = self.progress * rect.size.width;
// if progress is in the middle area
if (amount >= radius + 4 && amount <= (rect.size.width - radius - 4)) {
// top
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
CGContextAddLineToPoint(context, amount, 4);
CGContextAddLineToPoint(context, amount, radius + 4);
// bottom
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
CGContextAddLineToPoint(context, amount, rect.size.height - 4);
CGContextAddLineToPoint(context, amount, radius + 4);
CGContextFillPath(context);
}
// progress is in the right arc
else if (amount > radius + 4) {
float x = amount - (rect.size.width - radius - 4);
// top
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
CGContextAddLineToPoint(context, rect.size.width - radius - 4, 4);
float angle = -acos(x/radius);
if (isnan(angle)) angle = 0;
CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, M_PI, angle, 0);
CGContextAddLineToPoint(context, amount, rect.size.height/2);
// bottom
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
CGContextAddLineToPoint(context, rect.size.width - radius - 4, rect.size.height - 4);
angle = acos(x/radius);
if (isnan(angle)) angle = 0;
CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, -M_PI, angle, 1);
CGContextAddLineToPoint(context, amount, rect.size.height/2);
CGContextFillPath(context);
}
// progress is in the left arc
else if (amount < radius + 4 && amount > 0) {
// top
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
// bottom
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
CGContextFillPath(context);
}
}
#pragma mark - KVO
- (void)registerForKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
}
}
- (void)unregisterFromKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self removeObserver:self forKeyPath:keyPath];
}
}
- (NSArray *)observableKeypaths {
return [NSArray arrayWithObjects:@"lineColor", @"progressRemainingColor", @"progressColor", @"progress", nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
[self setNeedsDisplay];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/.svn/text-base/MacroDef.h.svn-base
================================================
//
// MacroDef.h
// DongDong
//
// Created by jmsre on 15/11/25.
// Copyright © 2015年 wangyang. All rights reserved.
//
#ifndef MacroDef_h
#define MacroDef_h
//数据存储
#define UserDefaultsGet(Key) [[NSUserDefaults standardUserDefaults] objectForKey:Key]
#define UserDefaultsSave(Value,Key) {[[NSUserDefaults standardUserDefaults] setObject:Value forKey:Key]; [[NSUserDefaults standardUserDefaults] synchronize];}
#define UserDefaultsRemove(Key) [[NSUserDefaults standardUserDefaults] removeObjectForKey:Key]
// IOS 版本
#define NLSystemVersionGreaterOrEqualThan(version) ([[[UIDevice currentDevice] systemVersion] floatValue] >= version)
#define IOS7_OR_LATER NLSystemVersionGreaterOrEqualThan(7.0)
#define iOS8 ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0)
#define iOS7 ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0)
#define iOS9 ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0)
//View相关
#define Point(Xpos, Ypos) CGPointMake(Xpos, Ypos)
#define Size(Width, Height) CGSizeMake(Width, Height)
#define Frame(Xpos, Ypos, Width, Height) CGRectMake(Xpos, Ypos, Width, Height)
#define Xpos origin.x
#define Ypos origin.y
#define Width size.width
#define Height size.height
//Window相关
#define Screen_Width [UIScreen mainScreen].bounds.size.width
#define Screen_Height [UIScreen mainScreen].bounds.size.height
//设置View圆角
#define setViewCorner(view,radius) {view.layer.cornerRadius = radius; view.layer.masksToBounds = YES;}
//设置颜色
#define ColorRGBA(R,G,B,A) [UIColor colorWithRed:R / 255.0 green:G / 255.0 blue:B / 255.0 alpha:A]
#define ColorRGB(R,G,B) [UIColor colorWithRed:R / 255.0 green:G / 255.0 blue:B / 255.0 alpha:1.0]
//透明色
#define ClearColor [UIColor clearColor]
//字体
#define Font(FontSize) [UIFont systemFontOfSize:FontSize]
#define BoldFont(FontSize) [UIFont boldSystemFontOfSize:FontSize]
//变量属性
#define Strong @property(nonatomic, strong)
#define Weak @property(nonatomic, weak)
#define Retain @property(nonatomic, retain)
#define Copy @property(nonatomic, copy)
#define Assign @property(nonatomic, assign)
#define StrongWithIB @property(nonatomic, strong) IBOutlet
#define WeakWithIB @property(nonatomic, weak) IBOutlet
#define RetainWithIB @property(nonatomic, retain) IBOutlet
//block self
#define WeakSelf __weak typeof(self) wself = self;
#define iPhone5 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(320, 568), [[UIScreen mainScreen] bounds].size) : NO)
#define iPhone6 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(375, 667), [[UIScreen mainScreen] bounds].size) : NO)
#define iPhone6p ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(414, 736), [[UIScreen mainScreen] bounds].size) : NO)
//泛型转换成NSString
#define StringFromId(idType) [NSString stringWithFormat:@"%@",idType]
//整形转换成NSString
#define StringFromInt(idType) [NSString stringWithFormat:@"%ld",idType]
//explant
// RGB的颜色方法
#define kUIColorFromRGB(rgbValue) [UIColor \
colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
#define kUIColorFromRGBA(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 UI_MAIN_COLOR [UIColor colorWithRed:0.733 green:0.655 blue:0.424 alpha:1.000]
#define YDScreenWidth [UIScreen mainScreen].bounds.size.width
#define YDScreenHeight [UIScreen mainScreen].bounds.size.height
//#define Width YDScreenWidth/YDScreenHeight
#define YDBackGroundColor kUIColorFromRGB(0xf0f0f0)
#define YDTableSeperateLineColor kUIColorFromRGB(0xe1e1e1)
#define YDRedColor kUIColorFromRGB(0xe40000)
#define YDYellowColor kUIColorFromRGB(0xf2bd57)
#define YDAlert(message) [[[UIAlertView alloc]initWithTitle:nil message:message \
delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定",nil, nil]show]
#endif /* MacroDef_h */
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/Factory.h
================================================
//
// Factory.h
// DongDong
//
// Created by jmsre on 15/11/25.
// Copyright © 2015年 wangyang. All rights reserved.
//
#import
#import
#import "MBProgressHUD.h"
@interface Factory : NSObject
// 返回文本size
+ (CGSize)sizeForText:(NSString *)text withFont:(UIFont *)font contraint:(CGSize)constraint;
//提示:
+ (void)MBProgressHUD:(NSString *)text Time:(NSUInteger)time;
+ (void)MBProgressHUDUseblock:(void (^)(MBProgressHUD *hud))block;
// 校验手机号
+ (BOOL)isMobileNumber:(NSString *)mobileNum;
+ (BOOL)isChinaMobile:(NSString *)phoneNum;
+ (BOOL)isChinaUnicom:(NSString *)phoneNum;
+ (BOOL)isChinaTelecom:(NSString *)phoneNum;
+ (NSString *)getPhoneNumType:(NSString *)phoneNum;
//身份证号
+ (BOOL) validateIdentityCard: (NSString *)identityCard;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/Factory.m
================================================
//
// Factory.m
// DongDong
//
// Created by jmsre on 15/11/25.
// Copyright © 2015年 wangyang. All rights reserved.
//
#import "Factory.h"
#import "MacroDef.h"
@implementation Factory
// 返回文本size
+ (CGSize)sizeForText:(NSString *)text withFont:(UIFont *)font contraint:(CGSize)constraint{
CGSize size = CGSizeZero;
if (IOS7_OR_LATER)
{
size = [text boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading
attributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil]
context:nil].size;
}
// else
// {
// size = [text sizeWithFont:font constrainedToSize:constraint
// lineBreakMode:NSLineBreakByWordWrapping];
// }
return size;
}
//提示:
+ (void)MBProgressHUD:(NSString *)text Time:(NSUInteger)time{
MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithView:[UIApplication sharedApplication].windows[0]];
[[UIApplication sharedApplication].windows[0] addSubview:HUD];
HUD.labelText = text;
HUD.mode = MBProgressHUDModeText;
//指定距离中心点的X轴和Y轴的偏移量,如果不指定则在屏幕中间显示
// HUD.yOffset = 150.0f;
// HUD.xOffset = 100.0f;
__block unsigned int tttt = (unsigned int)time;
[HUD showAnimated:YES whileExecutingBlock:^{
sleep(tttt);
} completionBlock:^{
[HUD removeFromSuperview];
}];
}
+ (void)MBProgressHUDUseblock:(void (^)(MBProgressHUD *hud))block{
MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithView:[UIApplication sharedApplication].windows[0]];
[[UIApplication sharedApplication].windows[0] addSubview:HUD];
HUD.labelText = @"正在加载...";
HUD.mode = MBProgressHUDModeIndeterminate;
//指定距离中心点的X轴和Y轴的偏移量,如果不指定则在屏幕中间显示
// HUD.yOffset = 150.0f;
// HUD.xOffset = 100.0f;
[HUD showAnimated:YES whileExecutingBlock:^{
// sleep(1);
dispatch_sync(dispatch_get_main_queue(), ^{
block(HUD);
});
} completionBlock:^{
[HUD removeFromSuperview];
}];
}
// 校验手机号
+ (BOOL)isMobileNumber:(NSString *)mobileNum
{
if (mobileNum.length != 11)
{
return NO;
}
/**
* 手机号码:
* 13[0-9], 14[5,7], 15[0, 1, 2, 3, 5, 6, 7, 8, 9], 17[6, 7, 8], 18[0-9], 170[0-9]
* 移动号段: 134,135,136,137,138,139,150,151,152,157,158,159,182,183,184,187,188,147,178,1705
* 联通号段: 130,131,132,155,156,185,186,145,176,1709
* 电信号段: 133,153,180,181,189,177,1700
*/
NSString *MOBILE = @"^1(3[0-9]|4[57]|5[0-35-9]|8[0-9]|70)\\d{8}$";
/**
* 中国移动:China Mobile
* 134,135,136,137,138,139,150,151,152,157,158,159,182,183,184,187,188,147,178,1705
*/
NSString *CM = @"(^1(3[4-9]|4[7]|5[0-27-9]|7[8]|8[2-478])\\d{8}$)|(^1705\\d{7}$)";
/**
* 中国联通:China Unicom
* 130,131,132,155,156,185,186,145,176,1709
*/
NSString *CU = @"(^1(3[0-2]|4[5]|5[56]|7[6]|8[56])\\d{8}$)|(^1709\\d{7}$)";
/**
* 中国电信:China Telecom
* 133,153,180,181,189,177,1700
*/
NSString *CT = @"(^1(33|53|77|8[019])\\d{8}$)|(^1700\\d{7}$)";
NSPredicate *regextestmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", MOBILE];
NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];
NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];
NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];
if (([regextestmobile evaluateWithObject:mobileNum] == YES)
|| ([regextestcm evaluateWithObject:mobileNum] == YES)
|| ([regextestct evaluateWithObject:mobileNum] == YES)
|| ([regextestcu evaluateWithObject:mobileNum] == YES))
{
return YES;
}
else
{
return NO;
}
}
+ (BOOL)isChinaMobile:(NSString *)phoneNum
{
NSString *CM = @"(^1(3[4-9]|4[7]|5[0-27-9]|7[8]|8[2-478])\\d{8}$)|(^1705\\d{7}$)";
NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];
return [regextestcm evaluateWithObject:phoneNum];
}
+ (BOOL)isChinaUnicom:(NSString *)phoneNum
{
NSString *CU = @"(^1(3[0-2]|4[5]|5[56]|7[6]|8[56])\\d{8}$)|(^1709\\d{7}$)";
NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];
return [regextestcu evaluateWithObject:phoneNum];
}
+ (BOOL)isChinaTelecom:(NSString *)phoneNum
{
NSString *CT = @"(^1(33|53|77|8[019])\\d{8}$)|(^1700\\d{7}$)";
NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];
return [regextestct evaluateWithObject:phoneNum];
}
+ (NSString *)getPhoneNumType:(NSString *)phoneNum
{
return [self isChinaMobile:phoneNum]? @"中国移动": ([self isChinaUnicom:phoneNum]? @"中国联通":([self isChinaTelecom:phoneNum]? @"中国电信": @"未知"));
}
//身份证号
+ (BOOL) validateIdentityCard: (NSString *)identityCard
{
BOOL flag;
if (identityCard.length <= 0) {
flag = NO;
return flag;
}
NSString *regex2 = @"^(\\d{14}|\\d{17})(\\d|[xX])$";
NSPredicate *identityCardPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex2];
return [identityCardPredicate evaluateWithObject:identityCard];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/MBProgressHUD+Add.h
================================================
//
// MBProgressHUD+Add.h
// 视频客户端
//
// Created by mj on 13-4-18.
// Copyright (c) 2013年 itcast. All rights reserved.
//
#import "MBProgressHUD.h"
@interface MBProgressHUD (Add)
+ (void)showError:(NSString *)error toView:(UIView *)view;
+ (void)showSuccess:(NSString *)success toView:(UIView *)view;
+ (MBProgressHUD *)showMessag:(NSString *)message toView:(UIView *)view;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/MBProgressHUD+Add.m
================================================
//
// MBProgressHUD+Add.m
// 视频客户端
//
// Created by mj on 13-4-18.
// Copyright (c) 2013年 itcast. All rights reserved.
//
#import "MBProgressHUD+Add.h"
@implementation MBProgressHUD (Add)
#pragma mark 显示信息
+ (void)show:(NSString *)text icon:(NSString *)icon view:(UIView *)view
{
if (view == nil) view = [UIApplication sharedApplication].keyWindow;
// 快速显示一个提示信息
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
hud.labelText = text;
// 设置图片
hud.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"MBProgressHUD.bundle/%@", icon]]];
// 再设置模式
hud.mode = MBProgressHUDModeCustomView;
// 隐藏时候从父控件中移除
hud.removeFromSuperViewOnHide = YES;
// 1秒之后再消失
[hud hide:YES afterDelay:0.7];
}
#pragma mark 显示错误信息
+ (void)showError:(NSString *)error toView:(UIView *)view{
[self show:error icon:@"error.png" view:view];
}
+ (void)showSuccess:(NSString *)success toView:(UIView *)view
{
[self show:success icon:@"success.png" view:view];
}
#pragma mark 显示一些信息
+ (MBProgressHUD *)showMessag:(NSString *)message toView:(UIView *)view {
if (view == nil) view = [UIApplication sharedApplication].keyWindow;
// 快速显示一个提示信息
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
hud.labelText = message;
// 隐藏时候从父控件中移除
hud.removeFromSuperViewOnHide = YES;
// YES代表需要蒙版效果
hud.dimBackground = YES;
return hud;
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/MBProgressHUD.h
================================================
//
// MBProgressHUD.h
// Version 0.8
// Created by Matej Bukovinski on 2.4.09.
//
// This code is distributed under the terms and conditions of the MIT license.
// Copyright (c) 2013 Matej Bukovinski
//
// 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.
#import
#import
#import
@protocol MBProgressHUDDelegate;
typedef enum {
/** Progress is shown using an UIActivityIndicatorView. This is the default. */
MBProgressHUDModeIndeterminate,
/** Progress is shown using a round, pie-chart like, progress view. */
MBProgressHUDModeDeterminate,
/** Progress is shown using a horizontal progress bar */
MBProgressHUDModeDeterminateHorizontalBar,
/** Progress is shown using a ring-shaped progress view. */
MBProgressHUDModeAnnularDeterminate,
/** Shows a custom view */
MBProgressHUDModeCustomView,
/** Shows only labels */
MBProgressHUDModeText
} MBProgressHUDMode;
typedef enum {
/** Opacity animation */
MBProgressHUDAnimationFade,
/** Opacity + scale animation */
MBProgressHUDAnimationZoom,
MBProgressHUDAnimationZoomOut = MBProgressHUDAnimationZoom,
MBProgressHUDAnimationZoomIn
} MBProgressHUDAnimation;
#ifndef MB_INSTANCETYPE
#if __has_feature(objc_instancetype)
#define MB_INSTANCETYPE instancetype
#else
#define MB_INSTANCETYPE id
#endif
#endif
#ifndef MB_STRONG
#if __has_feature(objc_arc)
#define MB_STRONG strong
#else
#define MB_STRONG retain
#endif
#endif
#ifndef MB_WEAK
#if __has_feature(objc_arc_weak)
#define MB_WEAK weak
#elif __has_feature(objc_arc)
#define MB_WEAK unsafe_unretained
#else
#define MB_WEAK assign
#endif
#endif
#if NS_BLOCKS_AVAILABLE
typedef void (^MBProgressHUDCompletionBlock)();
#endif
/**
* Displays a simple HUD window containing a progress indicator and two optional labels for short messages.
*
* This is a simple drop-in class for displaying a progress HUD view similar to Apple's private UIProgressHUD class.
* The MBProgressHUD window spans over the entire space given to it by the initWithFrame constructor and catches all
* user input on this region, thereby preventing the user operations on components below the view. The HUD itself is
* drawn centered as a rounded semi-transparent view which resizes depending on the user specified content.
*
* This view supports four modes of operation:
* - MBProgressHUDModeIndeterminate - shows a UIActivityIndicatorView
* - MBProgressHUDModeDeterminate - shows a custom round progress indicator
* - MBProgressHUDModeAnnularDeterminate - shows a custom annular progress indicator
* - MBProgressHUDModeCustomView - shows an arbitrary, user specified view (@see customView)
*
* All three modes can have optional labels assigned:
* - If the labelText property is set and non-empty then a label containing the provided content is placed below the
* indicator view.
* - If also the detailsLabelText property is set then another label is placed below the first label.
*/
@interface MBProgressHUD : UIView
/**
* Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:.
*
* @param view The view that the HUD will be added to
* @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use
* animations while appearing.
* @return A reference to the created HUD.
*
* @see hideHUDForView:animated:
* @see animationType
*/
+ (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated;
/**
* Finds the top-most HUD subview and hides it. The counterpart to this method is showHUDAddedTo:animated:.
*
* @param view The view that is going to be searched for a HUD subview.
* @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
* animations while disappearing.
* @return YES if a HUD was found and removed, NO otherwise.
*
* @see showHUDAddedTo:animated:
* @see animationType
*/
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated;
/**
* Finds all the HUD subviews and hides them.
*
* @param view The view that is going to be searched for HUD subviews.
* @param animated If set to YES the HUDs will disappear using the current animationType. If set to NO the HUDs will not use
* animations while disappearing.
* @return the number of HUDs found and removed.
*
* @see hideHUDForView:animated:
* @see animationType
*/
+ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated;
/**
* Finds the top-most HUD subview and returns it.
*
* @param view The view that is going to be searched.
* @return A reference to the last HUD subview discovered.
*/
+ (MB_INSTANCETYPE)HUDForView:(UIView *)view;
/**
* Finds all HUD subviews and returns them.
*
* @param view The view that is going to be searched.
* @return All found HUD views (array of MBProgressHUD objects).
*/
+ (NSArray *)allHUDsForView:(UIView *)view;
/**
* A convenience constructor that initializes the HUD with the window's bounds. Calls the designated constructor with
* window.bounds as the parameter.
*
* @param window The window instance that will provide the bounds for the HUD. Should be the same instance as
* the HUD's superview (i.e., the window that the HUD will be added to).
*/
- (id)initWithWindow:(UIWindow *)window;
/**
* A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with
* view.bounds as the parameter
*
* @param view The view instance that will provide the bounds for the HUD. Should be the same instance as
* the HUD's superview (i.e., the view that the HUD will be added to).
*/
- (id)initWithView:(UIView *)view;
/**
* Display the HUD. You need to make sure that the main thread completes its run loop soon after this method call so
* the user interface can be updated. Call this method when your task is already set-up to be executed in a new thread
* (e.g., when using something like NSOperation or calling an asynchronous call like NSURLRequest).
*
* @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use
* animations while appearing.
*
* @see animationType
*/
- (void)show:(BOOL)animated;
/**
* Hide the HUD. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to
* hide the HUD when your task completes.
*
* @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
* animations while disappearing.
*
* @see animationType
*/
- (void)hide:(BOOL)animated;
/**
* Hide the HUD after a delay. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to
* hide the HUD when your task completes.
*
* @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use
* animations while disappearing.
* @param delay Delay in seconds until the HUD is hidden.
*
* @see animationType
*/
- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay;
/**
* Shows the HUD while a background task is executing in a new thread, then hides the HUD.
*
* This method also takes care of autorelease pools so your method does not have to be concerned with setting up a
* pool.
*
* @param method The method to be executed while the HUD is shown. This method will be executed in a new thread.
* @param target The object that the target method belongs to.
* @param object An optional object to be passed to the method.
* @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will not use
* animations while (dis)appearing.
*/
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated;
#if NS_BLOCKS_AVAILABLE
/**
* Shows the HUD while a block is executing on a background queue, then hides the HUD.
*
* @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
*/
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block;
/**
* Shows the HUD while a block is executing on a background queue, then hides the HUD.
*
* @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
*/
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(MBProgressHUDCompletionBlock)completion;
/**
* Shows the HUD while a block is executing on the specified dispatch queue, then hides the HUD.
*
* @see showAnimated:whileExecutingBlock:onQueue:completionBlock:
*/
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue;
/**
* Shows the HUD while a block is executing on the specified dispatch queue, executes completion block on the main queue, and then hides the HUD.
*
* @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will
* not use animations while (dis)appearing.
* @param block The block to be executed while the HUD is shown.
* @param queue The dispatch queue on which the block should be executed.
* @param completion The block to be executed on completion.
*
* @see completionBlock
*/
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue
completionBlock:(MBProgressHUDCompletionBlock)completion;
/**
* A block that gets called after the HUD was completely hidden.
*/
@property (copy) MBProgressHUDCompletionBlock completionBlock;
#endif
/**
* MBProgressHUD operation mode. The default is MBProgressHUDModeIndeterminate.
*
* @see MBProgressHUDMode
*/
@property (assign) MBProgressHUDMode mode;
/**
* The animation type that should be used when the HUD is shown and hidden.
*
* @see MBProgressHUDAnimation
*/
@property (assign) MBProgressHUDAnimation animationType;
/**
* The UIView (e.g., a UIImageView) to be shown when the HUD is in MBProgressHUDModeCustomView.
* For best results use a 37 by 37 pixel view (so the bounds match the built in indicator bounds).
*/
@property (MB_STRONG) UIView *customView;
/**
* The HUD delegate object.
*
* @see MBProgressHUDDelegate
*/
@property (MB_WEAK) id delegate;
/**
* An optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit
* the entire text. If the text is too long it will get clipped by displaying "..." at the end. If left unchanged or
* set to @"", then no message is displayed.
*/
@property (copy) NSString *labelText;
/**
* An optional details message displayed below the labelText message. This message is displayed only if the labelText
* property is also set and is different from an empty string (@""). The details text can span multiple lines.
*/
@property (copy) NSString *detailsLabelText;
/**
* The opacity of the HUD window. Defaults to 0.8 (80% opacity).
*/
@property (assign) float opacity;
/**
* The color of the HUD window. Defaults to black. If this property is set, color is set using
* this UIColor and the opacity property is not used. using retain because performing copy on
* UIColor base colors (like [UIColor greenColor]) cause problems with the copyZone.
*/
@property (MB_STRONG) UIColor *color;
/**
* The x-axis offset of the HUD relative to the centre of the superview.
*/
@property (assign) float xOffset;
/**
* The y-axis offset of the HUD relative to the centre of the superview.
*/
@property (assign) float yOffset;
/**
* The amount of space between the HUD edge and the HUD elements (labels, indicators or custom views).
* Defaults to 20.0
*/
@property (assign) float margin;
/**
* Cover the HUD background view with a radial gradient.
*/
@property (assign) BOOL dimBackground;
/*
* Grace period is the time (in seconds) that the invoked method may be run without
* showing the HUD. If the task finishes before the grace time runs out, the HUD will
* not be shown at all.
* This may be used to prevent HUD display for very short tasks.
* Defaults to 0 (no grace time).
* Grace time functionality is only supported when the task status is known!
* @see taskInProgress
*/
@property (assign) float graceTime;
/**
* The minimum time (in seconds) that the HUD is shown.
* This avoids the problem of the HUD being shown and than instantly hidden.
* Defaults to 0 (no minimum show time).
*/
@property (assign) float minShowTime;
/**
* Indicates that the executed operation is in progress. Needed for correct graceTime operation.
* If you don't set a graceTime (different than 0.0) this does nothing.
* This property is automatically set when using showWhileExecuting:onTarget:withObject:animated:.
* When threading is done outside of the HUD (i.e., when the show: and hide: methods are used directly),
* you need to set this property when your task starts and completes in order to have normal graceTime
* functionality.
*/
@property (assign) BOOL taskInProgress;
/**
* Removes the HUD from its parent view when hidden.
* Defaults to NO.
*/
@property (assign) BOOL removeFromSuperViewOnHide;
/**
* Font to be used for the main label. Set this property if the default is not adequate.
*/
@property (MB_STRONG) UIFont* labelFont;
/**
* Font to be used for the details label. Set this property if the default is not adequate.
*/
@property (MB_STRONG) UIFont* detailsLabelFont;
/**
* The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0.
*/
@property (assign) float progress;
/**
* The minimum size of the HUD bezel. Defaults to CGSizeZero (no minimum size).
*/
@property (assign) CGSize minSize;
/**
* Force the HUD dimensions to be equal if possible.
*/
@property (assign, getter = isSquare) BOOL square;
@end
@protocol MBProgressHUDDelegate
@optional
/**
* Called after the HUD was fully hidden from the screen.
*/
- (void)hudWasHidden:(MBProgressHUD *)hud;
@end
/**
* A progress view for showing definite progress by filling up a circle (pie chart).
*/
@interface MBRoundProgressView : UIView
/**
* Progress (0.0 to 1.0)
*/
@property (nonatomic, assign) float progress;
/**
* Indicator progress color.
* Defaults to white [UIColor whiteColor]
*/
@property (nonatomic, MB_STRONG) UIColor *progressTintColor;
/**
* Indicator background (non-progress) color.
* Defaults to translucent white (alpha 0.1)
*/
@property (nonatomic, MB_STRONG) UIColor *backgroundTintColor;
/*
* Display mode - NO = round or YES = annular. Defaults to round.
*/
@property (nonatomic, assign, getter = isAnnular) BOOL annular;
@end
/**
* A flat bar progress view.
*/
@interface MBBarProgressView : UIView
/**
* Progress (0.0 to 1.0)
*/
@property (nonatomic, assign) float progress;
/**
* Bar border line color.
* Defaults to white [UIColor whiteColor].
*/
@property (nonatomic, MB_STRONG) UIColor *lineColor;
/**
* Bar background color.
* Defaults to clear [UIColor clearColor];
*/
@property (nonatomic, MB_STRONG) UIColor *progressRemainingColor;
/**
* Bar progress color.
* Defaults to white [UIColor whiteColor].
*/
@property (nonatomic, MB_STRONG) UIColor *progressColor;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/MBProgressHUD.m
================================================
//
// MBProgressHUD.m
// Version 0.8
// Created by Matej Bukovinski on 2.4.09.
//
#import "MBProgressHUD.h"
#if __has_feature(objc_arc)
#define MB_AUTORELEASE(exp) exp
#define MB_RELEASE(exp) exp
#define MB_RETAIN(exp) exp
#else
#define MB_AUTORELEASE(exp) [exp autorelease]
#define MB_RELEASE(exp) [exp release]
#define MB_RETAIN(exp) [exp retain]
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000
#define MBLabelAlignmentCenter NSTextAlignmentCenter
#else
#define MBLabelAlignmentCenter UITextAlignmentCenter
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
#define MB_TEXTSIZE(text, font) [text length] > 0 ? [text \
sizeWithAttributes:@{NSFontAttributeName:font}] : CGSizeZero;
#else
#define MB_TEXTSIZE(text, font) [text length] > 0 ? [text sizeWithFont:font] : CGSizeZero;
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
#define MB_MULTILINE_TEXTSIZE(text, font, maxSize, mode) [text length] > 0 ? [text \
boundingRectWithSize:maxSize options:(NSStringDrawingUsesLineFragmentOrigin) \
attributes:@{NSFontAttributeName:font} context:nil].size : CGSizeZero;
#else
#define MB_MULTILINE_TEXTSIZE(text, font, maxSize, mode) [text length] > 0 ? [text \
sizeWithFont:font constrainedToSize:maxSize lineBreakMode:mode] : CGSizeZero;
#endif
static const CGFloat kPadding = 4.f;
static const CGFloat kLabelFontSize = 16.f;
static const CGFloat kDetailsLabelFontSize = 12.f;
@interface MBProgressHUD ()
- (void)setupLabels;
- (void)registerForKVO;
- (void)unregisterFromKVO;
- (NSArray *)observableKeypaths;
- (void)registerForNotifications;
- (void)unregisterFromNotifications;
- (void)updateUIForKeypath:(NSString *)keyPath;
- (void)hideUsingAnimation:(BOOL)animated;
- (void)showUsingAnimation:(BOOL)animated;
- (void)done;
- (void)updateIndicators;
- (void)handleGraceTimer:(NSTimer *)theTimer;
- (void)handleMinShowTimer:(NSTimer *)theTimer;
- (void)setTransformForCurrentOrientation:(BOOL)animated;
- (void)cleanUp;
- (void)launchExecution;
- (void)deviceOrientationDidChange:(NSNotification *)notification;
- (void)hideDelayed:(NSNumber *)animated;
@property (atomic, MB_STRONG) UIView *indicator;
@property (atomic, MB_STRONG) NSTimer *graceTimer;
@property (atomic, MB_STRONG) NSTimer *minShowTimer;
@property (atomic, MB_STRONG) NSDate *showStarted;
@property (atomic, assign) CGSize size;
@end
@implementation MBProgressHUD {
BOOL useAnimation;
SEL methodForExecution;
id targetForExecution;
id objectForExecution;
UILabel *label;
UILabel *detailsLabel;
BOOL isFinished;
CGAffineTransform rotationTransform;
}
#pragma mark - Properties
@synthesize animationType;
@synthesize delegate;
@synthesize opacity;
@synthesize color;
@synthesize labelFont;
@synthesize detailsLabelFont;
@synthesize indicator;
@synthesize xOffset;
@synthesize yOffset;
@synthesize minSize;
@synthesize square;
@synthesize margin;
@synthesize dimBackground;
@synthesize graceTime;
@synthesize minShowTime;
@synthesize graceTimer;
@synthesize minShowTimer;
@synthesize taskInProgress;
@synthesize removeFromSuperViewOnHide;
@synthesize customView;
@synthesize showStarted;
@synthesize mode;
@synthesize labelText;
@synthesize detailsLabelText;
@synthesize progress;
@synthesize size;
#if NS_BLOCKS_AVAILABLE
@synthesize completionBlock;
#endif
#pragma mark - Class methods
+ (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
MBProgressHUD *hud = [[self alloc] initWithView:view];
[view addSubview:hud];
[hud show:animated];
return MB_AUTORELEASE(hud);
}
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated {
MBProgressHUD *hud = [self HUDForView:view];
if (hud != nil) {
hud.removeFromSuperViewOnHide = YES;
[hud hide:animated];
return YES;
}
return NO;
}
+ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated {
NSArray *huds = [MBProgressHUD allHUDsForView:view];
for (MBProgressHUD *hud in huds) {
hud.removeFromSuperViewOnHide = YES;
[hud hide:animated];
}
return [huds count];
}
+ (MB_INSTANCETYPE)HUDForView:(UIView *)view {
NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
for (UIView *subview in subviewsEnum) {
if ([subview isKindOfClass:self]) {
return (MBProgressHUD *)subview;
}
}
return nil;
}
+ (NSArray *)allHUDsForView:(UIView *)view {
NSMutableArray *huds = [NSMutableArray array];
NSArray *subviews = view.subviews;
for (UIView *aView in subviews) {
if ([aView isKindOfClass:self]) {
[huds addObject:aView];
}
}
return [NSArray arrayWithArray:huds];
}
#pragma mark - Lifecycle
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Set default values for properties
self.animationType = MBProgressHUDAnimationFade;
self.mode = MBProgressHUDModeIndeterminate;
self.labelText = nil;
self.detailsLabelText = nil;
self.opacity = 0.8f;
self.color = nil;
self.labelFont = [UIFont boldSystemFontOfSize:kLabelFontSize];
self.detailsLabelFont = [UIFont boldSystemFontOfSize:kDetailsLabelFontSize];
self.xOffset = 0.0f;
self.yOffset = 0.0f;
self.dimBackground = NO;
self.margin = 20.0f;
self.graceTime = 0.0f;
self.minShowTime = 0.0f;
self.removeFromSuperViewOnHide = NO;
self.minSize = CGSizeZero;
self.square = NO;
self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin
| UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
// Transparent background
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
// Make it invisible for now
self.alpha = 0.0f;
taskInProgress = NO;
rotationTransform = CGAffineTransformIdentity;
[self setupLabels];
[self updateIndicators];
[self registerForKVO];
[self registerForNotifications];
}
return self;
}
- (id)initWithView:(UIView *)view {
// NSAssert(view, @"View must not be nil.");
return [self initWithFrame:view.bounds];
}
- (id)initWithWindow:(UIWindow *)window {
return [self initWithView:window];
}
- (void)dealloc {
[self unregisterFromNotifications];
[self unregisterFromKVO];
#if !__has_feature(objc_arc)
[color release];
[indicator release];
[label release];
[detailsLabel release];
[labelText release];
[detailsLabelText release];
[graceTimer release];
[minShowTimer release];
[showStarted release];
[customView release];
#if NS_BLOCKS_AVAILABLE
[completionBlock release];
#endif
[super dealloc];
#endif
}
#pragma mark - Show & hide
- (void)show:(BOOL)animated {
useAnimation = animated;
// If the grace time is set postpone the HUD display
if (self.graceTime > 0.0) {
self.graceTimer = [NSTimer scheduledTimerWithTimeInterval:self.graceTime target:self
selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
}
// ... otherwise show the HUD imediately
else {
[self setNeedsDisplay];
[self showUsingAnimation:useAnimation];
}
}
- (void)hide:(BOOL)animated {
useAnimation = animated;
// If the minShow time is set, calculate how long the hud was shown,
// and pospone the hiding operation if necessary
if (self.minShowTime > 0.0 && showStarted) {
NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:showStarted];
if (interv < self.minShowTime) {
self.minShowTimer = [NSTimer scheduledTimerWithTimeInterval:(self.minShowTime - interv) target:self
selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO];
return;
}
}
// ... otherwise hide the HUD immediately
[self hideUsingAnimation:useAnimation];
}
- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay {
[self performSelector:@selector(hideDelayed:) withObject:[NSNumber numberWithBool:animated] afterDelay:delay];
}
- (void)hideDelayed:(NSNumber *)animated {
[self hide:[animated boolValue]];
}
#pragma mark - Timer callbacks
- (void)handleGraceTimer:(NSTimer *)theTimer {
// Show the HUD only if the task is still running
if (taskInProgress) {
[self setNeedsDisplay];
[self showUsingAnimation:useAnimation];
}
}
- (void)handleMinShowTimer:(NSTimer *)theTimer {
[self hideUsingAnimation:useAnimation];
}
#pragma mark - View Hierrarchy
- (void)didMoveToSuperview {
// We need to take care of rotation ourselfs if we're adding the HUD to a window
if ([self.superview isKindOfClass:[UIWindow class]]) {
[self setTransformForCurrentOrientation:NO];
}
}
#pragma mark - Internal show & hide operations
- (void)showUsingAnimation:(BOOL)animated {
if (animated && animationType == MBProgressHUDAnimationZoomIn) {
self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f));
} else if (animated && animationType == MBProgressHUDAnimationZoomOut) {
self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f));
}
self.showStarted = [NSDate date];
// Fade in
if (animated) {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.30];
self.alpha = 1.0f;
if (animationType == MBProgressHUDAnimationZoomIn || animationType == MBProgressHUDAnimationZoomOut) {
self.transform = rotationTransform;
}
[UIView commitAnimations];
}
else {
self.alpha = 1.0f;
}
}
- (void)hideUsingAnimation:(BOOL)animated {
// Fade out
if (animated && showStarted) {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.30];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationFinished:finished:context:)];
// 0.02 prevents the hud from passing through touches during the animation the hud will get completely hidden
// in the done method
if (animationType == MBProgressHUDAnimationZoomIn) {
self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f));
} else if (animationType == MBProgressHUDAnimationZoomOut) {
self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f));
}
self.alpha = 0.02f;
[UIView commitAnimations];
}
else {
self.alpha = 0.0f;
[self done];
}
self.showStarted = nil;
}
- (void)animationFinished:(NSString *)animationID finished:(BOOL)finished context:(void*)context {
[self done];
}
- (void)done {
isFinished = YES;
self.alpha = 0.0f;
if (removeFromSuperViewOnHide) {
[self removeFromSuperview];
}
#if NS_BLOCKS_AVAILABLE
if (self.completionBlock) {
self.completionBlock();
self.completionBlock = NULL;
}
#endif
if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
[delegate performSelector:@selector(hudWasHidden:) withObject:self];
}
}
#pragma mark - Threading
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
methodForExecution = method;
targetForExecution = MB_RETAIN(target);
objectForExecution = MB_RETAIN(object);
// Launch execution in new thread
self.taskInProgress = YES;
[NSThread detachNewThreadSelector:@selector(launchExecution) toTarget:self withObject:nil];
// Show HUD view
[self show:animated];
}
#if NS_BLOCKS_AVAILABLE
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
[self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL];
}
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(void (^)())completion {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
[self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:completion];
}
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue {
[self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL];
}
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue
completionBlock:(MBProgressHUDCompletionBlock)completion {
self.taskInProgress = YES;
self.completionBlock = completion;
dispatch_async(queue, ^(void) {
block();
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self cleanUp];
});
});
[self show:animated];
}
#endif
- (void)launchExecution {
@autoreleasepool {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
// Start executing the requested task
[targetForExecution performSelector:methodForExecution withObject:objectForExecution];
#pragma clang diagnostic pop
// Task completed, update view in main thread (note: view operations should
// be done only in the main thread)
[self performSelectorOnMainThread:@selector(cleanUp) withObject:nil waitUntilDone:NO];
}
}
- (void)cleanUp {
taskInProgress = NO;
#if !__has_feature(objc_arc)
[targetForExecution release];
[objectForExecution release];
#else
targetForExecution = nil;
objectForExecution = nil;
#endif
[self hide:useAnimation];
}
#pragma mark - UI
- (void)setupLabels {
label = [[UILabel alloc] initWithFrame:self.bounds];
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = MBLabelAlignmentCenter;
label.opaque = NO;
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor whiteColor];
label.font = self.labelFont;
label.text = self.labelText;
[self addSubview:label];
detailsLabel = [[UILabel alloc] initWithFrame:self.bounds];
detailsLabel.font = self.detailsLabelFont;
detailsLabel.adjustsFontSizeToFitWidth = NO;
detailsLabel.textAlignment = MBLabelAlignmentCenter;
detailsLabel.opaque = NO;
detailsLabel.backgroundColor = [UIColor clearColor];
detailsLabel.textColor = [UIColor whiteColor];
detailsLabel.numberOfLines = 0;
detailsLabel.font = self.detailsLabelFont;
detailsLabel.text = self.detailsLabelText;
[self addSubview:detailsLabel];
}
- (void)updateIndicators {
BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]];
BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]];
if (mode == MBProgressHUDModeIndeterminate && !isActivityIndicator) {
// Update to indeterminate indicator
[indicator removeFromSuperview];
self.indicator = MB_AUTORELEASE([[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]);
[(UIActivityIndicatorView *)indicator startAnimating];
[self addSubview:indicator];
}
else if (mode == MBProgressHUDModeDeterminateHorizontalBar) {
// Update to bar determinate indicator
[indicator removeFromSuperview];
self.indicator = MB_AUTORELEASE([[MBBarProgressView alloc] init]);
[self addSubview:indicator];
}
else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) {
if (!isRoundIndicator) {
// Update to determinante indicator
[indicator removeFromSuperview];
self.indicator = MB_AUTORELEASE([[MBRoundProgressView alloc] init]);
[self addSubview:indicator];
}
if (mode == MBProgressHUDModeAnnularDeterminate) {
[(MBRoundProgressView *)indicator setAnnular:YES];
}
}
else if (mode == MBProgressHUDModeCustomView && customView != indicator) {
// Update custom view indicator
[indicator removeFromSuperview];
self.indicator = customView;
[self addSubview:indicator];
} else if (mode == MBProgressHUDModeText) {
[indicator removeFromSuperview];
self.indicator = nil;
}
}
#pragma mark - Layout
- (void)layoutSubviews {
// Entirely cover the parent view
UIView *parent = self.superview;
if (parent) {
self.frame = parent.bounds;
}
CGRect bounds = self.bounds;
// Determine the total widt and height needed
CGFloat maxWidth = bounds.size.width - 4 * margin;
CGSize totalSize = CGSizeZero;
CGRect indicatorF = indicator.bounds;
indicatorF.size.width = MIN(indicatorF.size.width, maxWidth);
totalSize.width = MAX(totalSize.width, indicatorF.size.width);
totalSize.height += indicatorF.size.height;
CGSize labelSize = MB_TEXTSIZE(label.text, label.font);
labelSize.width = MIN(labelSize.width, maxWidth);
totalSize.width = MAX(totalSize.width, labelSize.width);
totalSize.height += labelSize.height;
if (labelSize.height > 0.f && indicatorF.size.height > 0.f) {
totalSize.height += kPadding;
}
CGFloat remainingHeight = bounds.size.height - totalSize.height - kPadding - 4 * margin;
CGSize maxSize = CGSizeMake(maxWidth, remainingHeight);
CGSize detailsLabelSize = MB_MULTILINE_TEXTSIZE(detailsLabel.text, detailsLabel.font, maxSize, detailsLabel.lineBreakMode);
totalSize.width = MAX(totalSize.width, detailsLabelSize.width);
totalSize.height += detailsLabelSize.height;
if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) {
totalSize.height += kPadding;
}
totalSize.width += 2 * margin;
totalSize.height += 2 * margin;
// Position elements
CGFloat yPos = roundf(((bounds.size.height - totalSize.height) / 2)) + margin + yOffset;
CGFloat xPos = xOffset;
indicatorF.origin.y = yPos;
indicatorF.origin.x = roundf((bounds.size.width - indicatorF.size.width) / 2) + xPos;
indicator.frame = indicatorF;
yPos += indicatorF.size.height;
if (labelSize.height > 0.f && indicatorF.size.height > 0.f) {
yPos += kPadding;
}
CGRect labelF;
labelF.origin.y = yPos;
labelF.origin.x = roundf((bounds.size.width - labelSize.width) / 2) + xPos;
labelF.size = labelSize;
label.frame = labelF;
yPos += labelF.size.height;
if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) {
yPos += kPadding;
}
CGRect detailsLabelF;
detailsLabelF.origin.y = yPos;
detailsLabelF.origin.x = roundf((bounds.size.width - detailsLabelSize.width) / 2) + xPos;
detailsLabelF.size = detailsLabelSize;
detailsLabel.frame = detailsLabelF;
// Enforce minsize and quare rules
if (square) {
CGFloat max = MAX(totalSize.width, totalSize.height);
if (max <= bounds.size.width - 2 * margin) {
totalSize.width = max;
}
if (max <= bounds.size.height - 2 * margin) {
totalSize.height = max;
}
}
if (totalSize.width < minSize.width) {
totalSize.width = minSize.width;
}
if (totalSize.height < minSize.height) {
totalSize.height = minSize.height;
}
self.size = totalSize;
}
#pragma mark BG Drawing
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
if (self.dimBackground) {
//Gradient colours
size_t gradLocationsNum = 2;
CGFloat gradLocations[2] = {0.0f, 1.0f};
CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum);
CGColorSpaceRelease(colorSpace);
//Gradient center
CGPoint gradCenter= CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
//Gradient radius
float gradRadius = MIN(self.bounds.size.width , self.bounds.size.height) ;
//Gradient draw
CGContextDrawRadialGradient (context, gradient, gradCenter,
0, gradCenter, gradRadius,
kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradient);
}
// Set background rect color
if (self.color) {
CGContextSetFillColorWithColor(context, self.color.CGColor);
} else {
CGContextSetGrayFillColor(context, 0.0f, self.opacity);
}
// Center HUD
CGRect allRect = self.bounds;
// Draw rounded HUD backgroud rect
CGRect boxRect = CGRectMake(roundf((allRect.size.width - size.width) / 2) + self.xOffset,
roundf((allRect.size.height - size.height) / 2) + self.yOffset, size.width, size.height);
float radius = 10.0f;
CGContextBeginPath(context);
CGContextMoveToPoint(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect));
CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMinY(boxRect) + radius, radius, 3 * (float)M_PI / 2, 0, 0);
CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMaxY(boxRect) - radius, radius, 0, (float)M_PI / 2, 0);
CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMaxY(boxRect) - radius, radius, (float)M_PI / 2, (float)M_PI, 0);
CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect) + radius, radius, (float)M_PI, 3 * (float)M_PI / 2, 0);
CGContextClosePath(context);
CGContextFillPath(context);
UIGraphicsPopContext();
}
#pragma mark - KVO
- (void)registerForKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
}
}
- (void)unregisterFromKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self removeObserver:self forKeyPath:keyPath];
}
}
- (NSArray *)observableKeypaths {
return [NSArray arrayWithObjects:@"mode", @"customView", @"labelText", @"labelFont",
@"detailsLabelText", @"detailsLabelFont", @"progress", nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(updateUIForKeypath:) withObject:keyPath waitUntilDone:NO];
} else {
[self updateUIForKeypath:keyPath];
}
}
- (void)updateUIForKeypath:(NSString *)keyPath {
if ([keyPath isEqualToString:@"mode"] || [keyPath isEqualToString:@"customView"]) {
[self updateIndicators];
} else if ([keyPath isEqualToString:@"labelText"]) {
label.text = self.labelText;
} else if ([keyPath isEqualToString:@"labelFont"]) {
label.font = self.labelFont;
} else if ([keyPath isEqualToString:@"detailsLabelText"]) {
detailsLabel.text = self.detailsLabelText;
} else if ([keyPath isEqualToString:@"detailsLabelFont"]) {
detailsLabel.font = self.detailsLabelFont;
} else if ([keyPath isEqualToString:@"progress"]) {
if ([indicator respondsToSelector:@selector(setProgress:)]) {
[(id)indicator setProgress:progress];
}
return;
}
[self setNeedsLayout];
[self setNeedsDisplay];
}
#pragma mark - Notifications
- (void)registerForNotifications {
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(deviceOrientationDidChange:)
name:UIDeviceOrientationDidChangeNotification object:nil];
}
- (void)unregisterFromNotifications {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)deviceOrientationDidChange:(NSNotification *)notification {
UIView *superview = self.superview;
if (!superview) {
return;
} else if ([superview isKindOfClass:[UIWindow class]]) {
[self setTransformForCurrentOrientation:YES];
} else {
self.bounds = self.superview.bounds;
[self setNeedsDisplay];
}
}
- (void)setTransformForCurrentOrientation:(BOOL)animated {
// Stay in sync with the superview
if (self.superview) {
self.bounds = self.superview.bounds;
[self setNeedsDisplay];
}
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
CGFloat radians = 0;
if (UIInterfaceOrientationIsLandscape(orientation)) {
if (orientation == UIInterfaceOrientationLandscapeLeft) { radians = -(CGFloat)M_PI_2; }
else { radians = (CGFloat)M_PI_2; }
// Window coordinates differ!
self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width);
} else {
if (orientation == UIInterfaceOrientationPortraitUpsideDown) { radians = (CGFloat)M_PI; }
else { radians = 0; }
}
rotationTransform = CGAffineTransformMakeRotation(radians);
if (animated) {
[UIView beginAnimations:nil context:nil];
}
[self setTransform:rotationTransform];
if (animated) {
[UIView commitAnimations];
}
}
@end
@implementation MBRoundProgressView
#pragma mark - Lifecycle
- (id)init {
return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)];
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
_progress = 0.f;
_annular = NO;
_progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f];
_backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f];
[self registerForKVO];
}
return self;
}
- (void)dealloc {
[self unregisterFromKVO];
#if !__has_feature(objc_arc)
[_progressTintColor release];
[_backgroundTintColor release];
[super dealloc];
#endif
}
#pragma mark - Drawing
- (void)drawRect:(CGRect)rect {
CGRect allRect = self.bounds;
CGRect circleRect = CGRectInset(allRect, 2.0f, 2.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
if (_annular) {
// Draw background
CGFloat lineWidth = 5.f;
UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath];
processBackgroundPath.lineWidth = lineWidth;
processBackgroundPath.lineCapStyle = kCGLineCapRound;
CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
CGFloat radius = (self.bounds.size.width - lineWidth)/2;
CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
CGFloat endAngle = (2 * (float)M_PI) + startAngle;
[processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
[_backgroundTintColor set];
[processBackgroundPath stroke];
// Draw progress
UIBezierPath *processPath = [UIBezierPath bezierPath];
processPath.lineCapStyle = kCGLineCapRound;
processPath.lineWidth = lineWidth;
endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
[processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
[_progressTintColor set];
[processPath stroke];
} else {
// Draw background
[_progressTintColor setStroke];
[_backgroundTintColor setFill];
CGContextSetLineWidth(context, 2.0f);
CGContextFillEllipseInRect(context, circleRect);
CGContextStrokeEllipseInRect(context, circleRect);
// Draw progress
CGPoint center = CGPointMake(allRect.size.width / 2, allRect.size.height / 2);
CGFloat radius = (allRect.size.width - 4) / 2;
CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees
CGFloat endAngle = (self.progress * 2 * (float)M_PI) + startAngle;
CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // white
CGContextMoveToPoint(context, center.x, center.y);
CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0);
CGContextClosePath(context);
CGContextFillPath(context);
}
}
#pragma mark - KVO
- (void)registerForKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
}
}
- (void)unregisterFromKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self removeObserver:self forKeyPath:keyPath];
}
}
- (NSArray *)observableKeypaths {
return [NSArray arrayWithObjects:@"progressTintColor", @"backgroundTintColor", @"progress", @"annular", nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
[self setNeedsDisplay];
}
@end
@implementation MBBarProgressView
#pragma mark - Lifecycle
- (id)init {
return [self initWithFrame:CGRectMake(.0f, .0f, 120.0f, 20.0f)];
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_progress = 0.f;
_lineColor = [UIColor whiteColor];
_progressColor = [UIColor whiteColor];
_progressRemainingColor = [UIColor clearColor];
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
[self registerForKVO];
}
return self;
}
- (void)dealloc {
[self unregisterFromKVO];
#if !__has_feature(objc_arc)
[_lineColor release];
[_progressColor release];
[_progressRemainingColor release];
[super dealloc];
#endif
}
#pragma mark - Drawing
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// setup properties
CGContextSetLineWidth(context, 2);
CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]);
CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]);
// draw line border
float radius = (rect.size.height / 2) - 2;
CGContextMoveToPoint(context, 2, rect.size.height/2);
CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2);
CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius);
CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius);
CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2);
CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
CGContextFillPath(context);
// draw progress background
CGContextMoveToPoint(context, 2, rect.size.height/2);
CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2);
CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius);
CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius);
CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2);
CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
CGContextStrokePath(context);
// setup to draw progress color
CGContextSetFillColorWithColor(context, [_progressColor CGColor]);
radius = radius - 2;
float amount = self.progress * rect.size.width;
// if progress is in the middle area
if (amount >= radius + 4 && amount <= (rect.size.width - radius - 4)) {
// top
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
CGContextAddLineToPoint(context, amount, 4);
CGContextAddLineToPoint(context, amount, radius + 4);
// bottom
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
CGContextAddLineToPoint(context, amount, rect.size.height - 4);
CGContextAddLineToPoint(context, amount, radius + 4);
CGContextFillPath(context);
}
// progress is in the right arc
else if (amount > radius + 4) {
float x = amount - (rect.size.width - radius - 4);
// top
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
CGContextAddLineToPoint(context, rect.size.width - radius - 4, 4);
float angle = -acos(x/radius);
if (isnan(angle)) angle = 0;
CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, M_PI, angle, 0);
CGContextAddLineToPoint(context, amount, rect.size.height/2);
// bottom
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
CGContextAddLineToPoint(context, rect.size.width - radius - 4, rect.size.height - 4);
angle = acos(x/radius);
if (isnan(angle)) angle = 0;
CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, -M_PI, angle, 1);
CGContextAddLineToPoint(context, amount, rect.size.height/2);
CGContextFillPath(context);
}
// progress is in the left arc
else if (amount < radius + 4 && amount > 0) {
// top
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
// bottom
CGContextMoveToPoint(context, 4, rect.size.height/2);
CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
CGContextFillPath(context);
}
}
#pragma mark - KVO
- (void)registerForKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
}
}
- (void)unregisterFromKVO {
for (NSString *keyPath in [self observableKeypaths]) {
[self removeObserver:self forKeyPath:keyPath];
}
}
- (NSArray *)observableKeypaths {
return [NSArray arrayWithObjects:@"lineColor", @"progressRemainingColor", @"progressColor", @"progress", nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
[self setNeedsDisplay];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MBProgressHUD/MacroDef.h
================================================
//
// MacroDef.h
// DongDong
//
// Created by jmsre on 15/11/25.
// Copyright © 2015年 wangyang. All rights reserved.
//
#ifndef MacroDef_h
#define MacroDef_h
//数据存储
#define UserDefaultsGet(Key) [[NSUserDefaults standardUserDefaults] objectForKey:Key]
#define UserDefaultsSave(Value,Key) {[[NSUserDefaults standardUserDefaults] setObject:Value forKey:Key]; [[NSUserDefaults standardUserDefaults] synchronize];}
#define UserDefaultsRemove(Key) [[NSUserDefaults standardUserDefaults] removeObjectForKey:Key]
// IOS 版本
#define NLSystemVersionGreaterOrEqualThan(version) ([[[UIDevice currentDevice] systemVersion] floatValue] >= version)
#define IOS7_OR_LATER NLSystemVersionGreaterOrEqualThan(7.0)
#define iOS8 ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0)
#define iOS7 ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0)
#define iOS9 ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0)
//View相关
#define Point(Xpos, Ypos) CGPointMake(Xpos, Ypos)
#define Size(Width, Height) CGSizeMake(Width, Height)
#define Frame(Xpos, Ypos, Width, Height) CGRectMake(Xpos, Ypos, Width, Height)
#define Xpos origin.x
#define Ypos origin.y
#define Width size.width
#define Height size.height
//Window相关
#define Screen_Width [UIScreen mainScreen].bounds.size.width
#define Screen_Height [UIScreen mainScreen].bounds.size.height
//设置View圆角
#define setViewCorner(view,radius) {view.layer.cornerRadius = radius; view.layer.masksToBounds = YES;}
//设置颜色
#define ColorRGBA(R,G,B,A) [UIColor colorWithRed:R / 255.0 green:G / 255.0 blue:B / 255.0 alpha:A]
#define ColorRGB(R,G,B) [UIColor colorWithRed:R / 255.0 green:G / 255.0 blue:B / 255.0 alpha:1.0]
//透明色
#define ClearColor [UIColor clearColor]
//字体
#define Font(FontSize) [UIFont systemFontOfSize:FontSize]
#define BoldFont(FontSize) [UIFont boldSystemFontOfSize:FontSize]
//变量属性
#define Strong @property(nonatomic, strong)
#define Weak @property(nonatomic, weak)
#define Retain @property(nonatomic, retain)
#define Copy @property(nonatomic, copy)
#define Assign @property(nonatomic, assign)
#define StrongWithIB @property(nonatomic, strong) IBOutlet
#define WeakWithIB @property(nonatomic, weak) IBOutlet
#define RetainWithIB @property(nonatomic, retain) IBOutlet
//block self
#define WeakSelf __weak typeof(self) wself = self;
#define iPhone5 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(320, 568), [[UIScreen mainScreen] bounds].size) : NO)
#define iPhone6 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(375, 667), [[UIScreen mainScreen] bounds].size) : NO)
#define iPhone6p ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(414, 736), [[UIScreen mainScreen] bounds].size) : NO)
//泛型转换成NSString
#define StringFromId(idType) [NSString stringWithFormat:@"%@",idType]
//整形转换成NSString
#define StringFromInt(idType) [NSString stringWithFormat:@"%ld",idType]
//explant
// RGB的颜色方法
#define kUIColorFromRGB(rgbValue) [UIColor \
colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
#define kUIColorFromRGBA(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 UI_MAIN_COLOR [UIColor colorWithRed:0.733 green:0.655 blue:0.424 alpha:1.000]
#define YDScreenWidth [UIScreen mainScreen].bounds.size.width
#define YDScreenHeight [UIScreen mainScreen].bounds.size.height
//#define Width YDScreenWidth/YDScreenHeight
#define YDBackGroundColor kUIColorFromRGB(0xf0f0f0)
#define YDTableSeperateLineColor kUIColorFromRGB(0xe1e1e1)
#define YDRedColor kUIColorFromRGB(0xe40000)
#define YDYellowColor kUIColorFromRGB(0xf2bd57)
#define YDAlert(message) [[[UIAlertView alloc]initWithTitle:nil message:message \
delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定",nil, nil]show]
#endif /* MacroDef_h */
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/.svn/all-wcprops
================================================
K 25
svn:wc:ra_dav:version-url
V 139
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/.svn/entries
================================================
10
dir
4265
http://bishanwen@121.43.163.28:9090/zhuku/repo/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser
http://bishanwen@121.43.163.28:9090/zhuku/repo
2017-12-28T02:01:09.072372Z
4261
zhoujun
5738001b-f13b-4815-ac0f-2e2f54dd1d22
MWPhotoBrowser.bundle
dir
Libraries
dir
Classes
dir
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/all-wcprops
================================================
K 25
svn:wc:ra_dav:version-url
V 147
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes
END
MWGridCell.h
K 25
svn:wc:ra_dav:version-url
V 160
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWGridCell.h
END
MWPhotoBrowserPrivate.h
K 25
svn:wc:ra_dav:version-url
V 171
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWPhotoBrowserPrivate.h
END
MWCommon.h
K 25
svn:wc:ra_dav:version-url
V 158
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWCommon.h
END
MWCaptionView.h
K 25
svn:wc:ra_dav:version-url
V 163
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWCaptionView.h
END
MWPhoto.h
K 25
svn:wc:ra_dav:version-url
V 157
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWPhoto.h
END
MWGridViewController.m
K 25
svn:wc:ra_dav:version-url
V 170
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWGridViewController.m
END
MWGridCell.m
K 25
svn:wc:ra_dav:version-url
V 160
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWGridCell.m
END
MWZoomingScrollView.h
K 25
svn:wc:ra_dav:version-url
V 169
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWZoomingScrollView.h
END
MWPhotoBrowser.h
K 25
svn:wc:ra_dav:version-url
V 164
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWPhotoBrowser.h
END
MWCaptionView.m
K 25
svn:wc:ra_dav:version-url
V 163
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWCaptionView.m
END
MWPhoto.m
K 25
svn:wc:ra_dav:version-url
V 157
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWPhoto.m
END
MWZoomingScrollView.m
K 25
svn:wc:ra_dav:version-url
V 169
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWZoomingScrollView.m
END
MWPhotoBrowser.m
K 25
svn:wc:ra_dav:version-url
V 164
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWPhotoBrowser.m
END
MWTapDetectingView.h
K 25
svn:wc:ra_dav:version-url
V 168
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWTapDetectingView.h
END
MWTapDetectingImageView.h
K 25
svn:wc:ra_dav:version-url
V 173
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWTapDetectingImageView.h
END
MWPhotoProtocol.h
K 25
svn:wc:ra_dav:version-url
V 165
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWPhotoProtocol.h
END
MWTapDetectingView.m
K 25
svn:wc:ra_dav:version-url
V 168
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWTapDetectingView.m
END
MWTapDetectingImageView.m
K 25
svn:wc:ra_dav:version-url
V 173
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWTapDetectingImageView.m
END
MWGridViewController.h
K 25
svn:wc:ra_dav:version-url
V 170
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes/MWGridViewController.h
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/entries
================================================
10
dir
4265
http://bishanwen@121.43.163.28:9090/zhuku/repo/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Classes
http://bishanwen@121.43.163.28:9090/zhuku/repo
2017-12-28T02:01:09.072372Z
4261
zhoujun
5738001b-f13b-4815-ac0f-2e2f54dd1d22
MWGridCell.h
file
2018-01-16T02:17:08.000000Z
34186a4c7099286098eb6b2373907521
2017-12-28T02:01:09.072372Z
4261
zhoujun
513
MWPhotoBrowserPrivate.h
file
2018-01-16T02:17:08.000000Z
38c2a0cf73c0f3b96f772f3f31483ab3
2017-12-28T02:01:09.072372Z
4261
zhoujun
4124
MWCommon.h
file
2018-01-16T02:17:08.000000Z
8dd3eb112fd24cb0ca123ec8cce0f5f7
2017-12-28T02:01:09.072372Z
4261
zhoujun
860
MWCaptionView.h
file
2018-01-16T02:17:08.000000Z
8cd79e144643706fe1ac7e18575ee954
2017-12-28T02:01:09.072372Z
4261
zhoujun
1049
MWPhoto.h
file
2018-01-16T02:17:08.000000Z
7e15b96f5e24690a5f3dd2f71cb879e0
2017-12-28T02:01:09.072372Z
4261
zhoujun
1115
MWGridViewController.m
file
2018-01-16T02:17:08.000000Z
220a487aea3e89a26d9e9c5d7e9ef9ec
2017-12-28T02:01:09.072372Z
4261
zhoujun
6860
MWGridCell.m
file
2018-01-16T02:17:08.000000Z
2a9188daa4fb749b19ca1135b0d651ed
2017-12-28T02:01:09.072372Z
4261
zhoujun
7473
MWZoomingScrollView.h
file
2018-01-16T02:17:08.000000Z
02b452080daba5fc255dfab7454ef0c0
2017-12-28T02:01:09.072372Z
4261
zhoujun
825
MWCaptionView.m
file
2018-01-16T02:17:08.000000Z
6cc2c173051997862bfb7474f6ec5a9b
2017-12-28T02:01:09.072372Z
4261
zhoujun
4261
MWPhotoBrowser.h
file
2018-01-16T02:17:08.000000Z
77be5874dda84f7ac44f3e7c5262958c
2017-12-28T02:01:09.072372Z
4261
zhoujun
2740
MWPhoto.m
file
2018-01-16T02:17:08.000000Z
322c3e72bdc37a477510864a32d3c052
2017-12-28T02:01:09.072372Z
4261
zhoujun
8171
MWZoomingScrollView.m
file
2018-01-16T02:17:08.000000Z
fd6c240013b6e5980828e41ef11f99d4
2017-12-28T02:01:09.072372Z
4261
zhoujun
13890
MWPhotoBrowser.m
file
2018-01-16T02:17:08.000000Z
16d5e5f06538f89d95b79eb03b261b4f
2017-12-28T02:01:09.072372Z
4261
zhoujun
65011
MWTapDetectingView.h
file
2018-01-16T02:17:08.000000Z
c850353b037533c0a564cd2c98733e47
2017-12-28T02:01:09.072372Z
4261
zhoujun
585
MWTapDetectingImageView.h
file
2018-01-16T02:17:08.000000Z
cf0fa140523c0632634b9acd838b7f17
2017-12-28T02:01:09.072372Z
4261
zhoujun
660
MWPhotoProtocol.h
file
2018-01-16T02:17:08.000000Z
336d0a20459f212682fd13bf7c4f9f8e
2017-12-28T02:01:09.072372Z
4261
zhoujun
2545
MWTapDetectingView.m
file
2018-01-16T02:17:08.000000Z
4ace419528d1d670d3e29a4846b7c87e
2017-12-28T02:01:09.072372Z
4261
zhoujun
1375
MWTapDetectingImageView.m
file
2018-01-16T02:17:08.000000Z
9d34506531945dd6587f1c30af162766
2017-12-28T02:01:09.072372Z
4261
zhoujun
1681
MWGridViewController.h
file
2018-01-16T02:17:08.000000Z
11ed0a1cc58a19c41492cdd6e558ea5f
2017-12-28T02:01:09.072372Z
4261
zhoujun
410
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWCaptionView.h.svn-base
================================================
//
// MWCaptionView.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 30/12/2011.
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//
#import
#import "MWPhotoProtocol.h"
@interface MWCaptionView : UIToolbar
// Init
- (id)initWithPhoto:(id)photo;
// To create your own custom caption view, subclass this view
// and override the following two methods (as well as any other
// UIView methods that you see fit):
// Override -setupCaption so setup your subviews and customise the appearance
// of your custom caption
// You can access the photo's data by accessing the _photo ivar
// If you need more data per photo then simply subclass MWPhoto and return your
// subclass to the photo browsers -photoBrowser:photoAtIndex: delegate method
- (void)setupCaption;
// Override -sizeThatFits: and return a CGSize specifying the height of your
// custom caption view. With width property is ignored and the caption is displayed
// the full width of the screen
- (CGSize)sizeThatFits:(CGSize)size;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWCaptionView.m.svn-base
================================================
//
// MWCaptionView.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 30/12/2011.
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//
#import "MWCommon.h"
#import "MWCaptionView.h"
#import "MWPhoto.h"
static const CGFloat labelPadding = 10;
// Private
@interface MWCaptionView () {
id _photo;
UILabel *_label;
}
@end
@implementation MWCaptionView
- (id)initWithPhoto:(id)photo {
self = [super initWithFrame:CGRectMake(0, 0, 320, 44)]; // Random initial frame
if (self) {
self.userInteractionEnabled = NO;
_photo = photo;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7")) {
// Use iOS 7 blurry goodness
self.barStyle = UIBarStyleBlackTranslucent;
self.tintColor = nil;
self.barTintColor = nil;
self.barStyle = UIBarStyleBlackTranslucent;
[self setBackgroundImage:nil forToolbarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault];
} else {
// Transparent black with no gloss
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [[UIColor colorWithWhite:0 alpha:0.6] CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setBackgroundImage:image forToolbarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault];
}
self.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin;
[self setupCaption];
}
return self;
}
- (CGSize)sizeThatFits:(CGSize)size {
CGFloat maxHeight = 9999;
if (_label.numberOfLines > 0) maxHeight = _label.font.leading*_label.numberOfLines;
CGSize textSize;
if ([NSString instancesRespondToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) {
textSize = [_label.text boundingRectWithSize:CGSizeMake(size.width - labelPadding*2, maxHeight)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:_label.font}
context:nil].size;
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
textSize = [_label.text sizeWithFont:_label.font
constrainedToSize:CGSizeMake(size.width - labelPadding*2, maxHeight)
lineBreakMode:_label.lineBreakMode];
#pragma clang diagnostic pop
}
return CGSizeMake(size.width, textSize.height + labelPadding * 2);
}
- (void)setupCaption {
_label = [[UILabel alloc] initWithFrame:CGRectIntegral(CGRectMake(labelPadding, 0,
self.bounds.size.width-labelPadding*2,
self.bounds.size.height))];
_label.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
_label.opaque = NO;
_label.backgroundColor = [UIColor clearColor];
if (SYSTEM_VERSION_LESS_THAN(@"6")) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
_label.textAlignment = UITextAlignmentCenter;
_label.lineBreakMode = UILineBreakModeWordWrap;
#pragma clang diagnostic pop
} else {
_label.textAlignment = NSTextAlignmentCenter;
_label.lineBreakMode = NSLineBreakByWordWrapping;
}
_label.numberOfLines = 0;
_label.textColor = [UIColor whiteColor];
if (SYSTEM_VERSION_LESS_THAN(@"7")) {
// Shadow on 6 and below
_label.shadowColor = [UIColor blackColor];
_label.shadowOffset = CGSizeMake(1, 1);
}
_label.font = [UIFont systemFontOfSize:17];
if ([_photo respondsToSelector:@selector(caption)]) {
_label.text = [_photo caption] ? [_photo caption] : @" ";
}
[self addSubview:_label];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWCommon.h.svn-base
================================================
//
// MWPreprocessor.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 01/10/2013.
//
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWGridCell.h.svn-base
================================================
//
// MWGridCell.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 08/10/2013.
//
//
#import
#import "MWPhoto.h"
#import "MWGridViewController.h"
#import "PSTCollectionView.h"
@interface MWGridCell : PSTCollectionViewCell {}
@property (nonatomic, weak) MWGridViewController *gridController;
@property (nonatomic) NSUInteger index;
@property (nonatomic) id photo;
@property (nonatomic) BOOL selectionMode;
@property (nonatomic) BOOL isSelected;
- (void)displayImage;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWGridCell.m.svn-base
================================================
//
// MWGridCell.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 08/10/2013.
//
//
#import "MWGridCell.h"
#import "MWCommon.h"
#import "MWPhotoBrowserPrivate.h"
#import "DACircularProgressView.h"
@interface MWGridCell () {
UIImageView *_imageView;
UIImageView *_loadingError;
DACircularProgressView *_loadingIndicator;
UIButton *_selectedButton;
}
@end
@implementation MWGridCell
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Grey background
self.backgroundColor = [UIColor colorWithWhite:0.12 alpha:1];
// Image
_imageView = [UIImageView new];
_imageView.frame = self.bounds;
_imageView.contentMode = UIViewContentModeScaleAspectFill;
_imageView.clipsToBounds = YES;
_imageView.autoresizesSubviews = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self addSubview:_imageView];
// Selection button
_selectedButton = [UIButton buttonWithType:UIButtonTypeCustom];
_selectedButton.contentMode = UIViewContentModeTopRight;
_selectedButton.adjustsImageWhenHighlighted = NO;
[_selectedButton setImage:nil forState:UIControlStateNormal];
[_selectedButton setImage:[UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageSelectedSmallOff.png"] forState:UIControlStateNormal];
[_selectedButton setImage:[UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageSelectedSmallOn.png"] forState:UIControlStateSelected];
[_selectedButton addTarget:self action:@selector(selectionButtonPressed) forControlEvents:UIControlEventTouchDown];
_selectedButton.hidden = YES;
_selectedButton.frame = CGRectMake(0, 0, 44, 44);
[self addSubview:_selectedButton];
// Loading indicator
_loadingIndicator = [[DACircularProgressView alloc] initWithFrame:CGRectMake(0, 0, 40.0f, 40.0f)];
_loadingIndicator.userInteractionEnabled = NO;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7")) {
_loadingIndicator.thicknessRatio = 0.1;
_loadingIndicator.roundedCorners = NO;
} else {
_loadingIndicator.thicknessRatio = 0.2;
_loadingIndicator.roundedCorners = YES;
}
[self addSubview:_loadingIndicator];
// Listen for photo loading notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(setProgressFromNotification:)
name:MWPHOTO_PROGRESS_NOTIFICATION
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleMWPhotoLoadingDidEndNotification:)
name:MWPHOTO_LOADING_DID_END_NOTIFICATION
object:nil];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - View
- (void)layoutSubviews {
[super layoutSubviews];
_imageView.frame = self.bounds;
_loadingIndicator.frame = CGRectMake(floorf((self.bounds.size.width - _loadingIndicator.frame.size.width) / 2.),
floorf((self.bounds.size.height - _loadingIndicator.frame.size.height) / 2),
_loadingIndicator.frame.size.width,
_loadingIndicator.frame.size.height);
_selectedButton.frame = CGRectMake(self.bounds.size.width - _selectedButton.frame.size.width - 0,
0, _selectedButton.frame.size.width, _selectedButton.frame.size.height);
}
#pragma mark - Cell
- (void)prepareForReuse {
_photo = nil;
_gridController = nil;
_imageView.image = nil;
_loadingIndicator.progress = 0;
_selectedButton.hidden = YES;
[self hideImageFailure];
[super prepareForReuse];
}
#pragma mark - Image Handling
- (void)setPhoto:(id )photo {
_photo = photo;
if (_photo) {
if (![_photo underlyingImage]) {
[self showLoadingIndicator];
} else {
[self hideLoadingIndicator];
}
} else {
[self showImageFailure];
}
}
- (void)displayImage {
_imageView.image = [_photo underlyingImage];
_selectedButton.hidden = !_selectionMode;
[self hideImageFailure];
}
#pragma mark - Selection
- (void)setSelectionMode:(BOOL)selectionMode {
_selectionMode = selectionMode;
}
- (void)setIsSelected:(BOOL)isSelected {
_isSelected = isSelected;
_selectedButton.selected = isSelected;
}
- (void)selectionButtonPressed {
_selectedButton.selected = !_selectedButton.selected;
[_gridController.browser setPhotoSelected:_selectedButton.selected atIndex:_index];
}
#pragma mark - Touches
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
_imageView.alpha = 0.6;
[super touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
_imageView.alpha = 1;
[super touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
_imageView.alpha = 1;
[super touchesCancelled:touches withEvent:event];
}
#pragma mark Indicators
- (void)hideLoadingIndicator {
_loadingIndicator.hidden = YES;
}
- (void)showLoadingIndicator {
_loadingIndicator.progress = 0;
_loadingIndicator.hidden = NO;
[self hideImageFailure];
}
- (void)showImageFailure {
if (!_loadingError) {
_loadingError = [UIImageView new];
_loadingError.image = [UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageError.png"];
_loadingError.userInteractionEnabled = NO;
[_loadingError sizeToFit];
[self addSubview:_loadingError];
}
[self hideLoadingIndicator];
_imageView.image = nil;
_loadingError.frame = CGRectMake(floorf((self.bounds.size.width - _loadingError.frame.size.width) / 2.),
floorf((self.bounds.size.height - _loadingError.frame.size.height) / 2),
_loadingError.frame.size.width,
_loadingError.frame.size.height);
}
- (void)hideImageFailure {
if (_loadingError) {
[_loadingError removeFromSuperview];
_loadingError = nil;
}
}
#pragma mark - Notifications
- (void)setProgressFromNotification:(NSNotification *)notification {
NSDictionary *dict = [notification object];
id photoWithProgress = [dict objectForKey:@"photo"];
if (photoWithProgress == _photo) {
// NSLog(@"%f", [[dict valueForKey:@"progress"] floatValue]);
float progress = [[dict valueForKey:@"progress"] floatValue];
_loadingIndicator.progress = MAX(MIN(1, progress), 0);
}
}
- (void)handleMWPhotoLoadingDidEndNotification:(NSNotification *)notification {
id photo = [notification object];
if (photo == _photo) {
if ([photo underlyingImage]) {
// Successful load
[self displayImage];
} else {
// Failed to load
[self showImageFailure];
}
[self hideLoadingIndicator];
}
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWGridViewController.h.svn-base
================================================
//
// MWGridViewController.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 08/10/2013.
//
//
#import
#import "MWPhotoBrowser.h"
#import "PSTCollectionView.h"
@interface MWGridViewController : PSTCollectionViewController {}
@property (nonatomic, assign) MWPhotoBrowser *browser;
@property (nonatomic) BOOL selectionMode;
@property (nonatomic) CGPoint initialContentOffset;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWGridViewController.m.svn-base
================================================
//
// MWGridViewController.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 08/10/2013.
//
//
#import "MWGridViewController.h"
#import "MWGridCell.h"
#import "MWPhotoBrowserPrivate.h"
#import "MWCommon.h"
@interface MWGridViewController () {
// Store margins for current setup
CGFloat _margin, _gutter, _marginL, _gutterL, _columns, _columnsL;
}
@end
@implementation MWGridViewController
- (id)init {
if ((self = [super initWithCollectionViewLayout:[PSTCollectionViewFlowLayout new]])) {
// Defaults
_columns = 3, _columnsL = 4;
_margin = 0, _gutter = 1;
_marginL = 0, _gutterL = 1;
// For pixel perfection...
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// iPad
_columns = 6, _columnsL = 8;
_margin = 1, _gutter = 2;
_marginL = 1, _gutterL = 2;
} else if ([UIScreen mainScreen].bounds.size.height == 480) {
// iPhone 3.5 inch
_columns = 3, _columnsL = 4;
_margin = 0, _gutter = 1;
_marginL = 1, _gutterL = 2;
} else {
// iPhone 4 inch
_columns = 3, _columnsL = 5;
_margin = 0, _gutter = 1;
_marginL = 0, _gutterL = 2;
}
_initialContentOffset = CGPointMake(0, CGFLOAT_MAX);
}
return self;
}
#pragma mark - View
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView registerClass:[MWGridCell class] forCellWithReuseIdentifier:@"GridCell"];
self.collectionView.alwaysBounceVertical = YES;
self.collectionView.backgroundColor = [UIColor blackColor];
}
- (void)viewWillDisappear:(BOOL)animated {
// Cancel outstanding loading
NSArray *visibleCells = [self.collectionView visibleCells];
if (visibleCells) {
for (MWGridCell *cell in visibleCells) {
[cell.photo cancelAnyLoading];
}
}
[super viewWillDisappear:animated];
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[self performLayout];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// Move to previous content offset
if (_initialContentOffset.y != CGFLOAT_MAX) {
self.collectionView.contentOffset = _initialContentOffset;
}
CGPoint currentContentOffset = self.collectionView.contentOffset;
// Get scroll position to have the current photo on screen
if (_browser.numberOfPhotos > 0) {
NSIndexPath *currentPhotoIndexPath = [NSIndexPath indexPathForItem:_browser.currentIndex inSection:0];
[self.collectionView scrollToItemAtIndexPath:currentPhotoIndexPath atScrollPosition:PSTCollectionViewScrollPositionNone animated:NO];
}
CGPoint offsetToShowCurrent = self.collectionView.contentOffset;
// Only commit to using the scrolled position if it differs from the initial content offset
if (!CGPointEqualToPoint(offsetToShowCurrent, currentContentOffset)) {
// Use offset to show current
self.collectionView.contentOffset = offsetToShowCurrent;
} else {
// Stick with initial
self.collectionView.contentOffset = currentContentOffset;
}
}
- (void)performLayout {
UINavigationBar *navBar = self.navigationController.navigationBar;
CGFloat yAdjust = 0;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7") && !self.browser.wantsFullScreenLayout) yAdjust = -20;
#endif
self.collectionView.contentInset = UIEdgeInsetsMake(navBar.frame.origin.y + navBar.frame.size.height + [self getGutter] + yAdjust, 0, 0, 0);
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self.collectionView reloadData];
[self performLayout]; // needed for iOS 5 & 6
}
#pragma mark - Layout
- (CGFloat)getColumns {
if ((UIInterfaceOrientationIsPortrait(self.interfaceOrientation))) {
return _columns;
} else {
return _columnsL;
}
}
- (CGFloat)getMargin {
if ((UIInterfaceOrientationIsPortrait(self.interfaceOrientation))) {
return _margin;
} else {
return _marginL;
}
}
- (CGFloat)getGutter {
if ((UIInterfaceOrientationIsPortrait(self.interfaceOrientation))) {
return _gutter;
} else {
return _gutterL;
}
}
#pragma mark - Collection View
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {
return [_browser numberOfPhotos];
}
- (PSTCollectionViewCell *)collectionView:(PSTCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MWGridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"GridCell" forIndexPath:indexPath];
if (!cell) {
cell = [[MWGridCell alloc] init];
}
id photo = [_browser thumbPhotoAtIndex:indexPath.row];
cell.photo = photo;
cell.gridController = self;
cell.selectionMode = _selectionMode;
cell.isSelected = [_browser photoIsSelectedAtIndex:indexPath.row];
cell.index = indexPath.row;
UIImage *img = [_browser imageForPhoto:photo];
if (img) {
[cell displayImage];
} else {
[photo loadUnderlyingImageAndNotify];
}
return cell;
}
- (void)collectionView:(PSTCollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[_browser setCurrentPhotoIndex:indexPath.row];
[_browser hideGrid];
}
- (void)collectionView:(PSTCollectionView *)collectionView didEndDisplayingCell:(PSTCollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
[((MWGridCell *)cell).photo cancelAnyLoading];
}
- (CGSize)collectionView:(PSTCollectionView *)collectionView layout:(PSTCollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat margin = [self getMargin];
CGFloat gutter = [self getGutter];
CGFloat columns = [self getColumns];
CGFloat value = floorf(((self.view.bounds.size.width - (columns - 1) * gutter - 2 * margin) / columns));
return CGSizeMake(value, value);
}
- (CGFloat)collectionView:(PSTCollectionView *)collectionView layout:(PSTCollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
return [self getGutter];
}
- (CGFloat)collectionView:(PSTCollectionView *)collectionView layout:(PSTCollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return [self getGutter];
}
- (UIEdgeInsets)collectionView:(PSTCollectionView *)collectionView layout:(PSTCollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
CGFloat margin = [self getMargin];
return UIEdgeInsetsMake(margin, margin, margin, margin);
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWPhoto.h.svn-base
================================================
//
// MWPhoto.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 17/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import
#import "MWPhotoProtocol.h"
// This class models a photo/image and it's caption
// If you want to handle photos, caching, decompression
// yourself then you can simply ensure your custom data model
// conforms to MWPhotoProtocol
@interface MWPhoto : NSObject
@property (nonatomic, strong) NSString *caption;
@property (nonatomic, readonly) UIImage *image;
@property (nonatomic, readonly) NSURL *photoURL;
@property (nonatomic, readonly) NSString *filePath __attribute__((deprecated("Use photoURL"))); // Depreciated
+ (MWPhoto *)photoWithImage:(UIImage *)image;
+ (MWPhoto *)photoWithFilePath:(NSString *)path __attribute__((deprecated("Use photoWithURL: with a file URL"))); // Depreciated
+ (MWPhoto *)photoWithURL:(NSURL *)url;
- (id)initWithImage:(UIImage *)image;
- (id)initWithURL:(NSURL *)url;
- (id)initWithFilePath:(NSString *)path __attribute__((deprecated("Use initWithURL: with a file URL"))); // Depreciated
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWPhoto.m.svn-base
================================================
//
// MWPhoto.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 17/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import "MWPhoto.h"
#import "MWPhotoBrowser.h"
#import "EMSDWebImageDecoder.h"
#import "EMSDWebImageManager.h"
#import "EMSDWebImageOperation.h"
#import
@interface MWPhoto () {
BOOL _loadingInProgress;
id _webImageOperation;
}
- (void)imageLoadingComplete;
@end
@implementation MWPhoto
@synthesize underlyingImage = _underlyingImage; // synth property from protocol
#pragma mark - Class Methods
+ (MWPhoto *)photoWithImage:(UIImage *)image {
return [[MWPhoto alloc] initWithImage:image];
}
// Deprecated
+ (MWPhoto *)photoWithFilePath:(NSString *)path {
return [MWPhoto photoWithURL:[NSURL fileURLWithPath:path]];
}
+ (MWPhoto *)photoWithURL:(NSURL *)url {
return [[MWPhoto alloc] initWithURL:url];
}
#pragma mark - Init
- (id)initWithImage:(UIImage *)image {
if ((self = [super init])) {
_image = image;
}
return self;
}
// Deprecated
- (id)initWithFilePath:(NSString *)path {
if ((self = [super init])) {
_photoURL = [NSURL fileURLWithPath:path];
}
return self;
}
- (id)initWithURL:(NSURL *)url {
if ((self = [super init])) {
_photoURL = [url copy];
}
return self;
}
#pragma mark - MWPhoto Protocol Methods
- (UIImage *)underlyingImage {
return _underlyingImage;
}
- (void)loadUnderlyingImageAndNotify {
// NSAssert([[NSThread currentThread] isMainThread], @"This method must be called on the main thread.");
if (_loadingInProgress) return;
_loadingInProgress = YES;
@try {
if (self.underlyingImage) {
[self imageLoadingComplete];
} else {
[self performLoadUnderlyingImageAndNotify];
}
}
@catch (NSException *exception) {
self.underlyingImage = nil;
_loadingInProgress = NO;
[self imageLoadingComplete];
}
@finally {
}
}
// Set the underlyingImage
- (void)performLoadUnderlyingImageAndNotify {
// Get underlying image
if (_image) {
// We have UIImage!
self.underlyingImage = _image;
[self imageLoadingComplete];
} else if (_photoURL) {
// Check what type of url it is
if ([[[_photoURL scheme] lowercaseString] isEqualToString:@"assets-library"]) {
// Load from asset library async
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@autoreleasepool {
@try {
ALAssetsLibrary *assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:self->_photoURL
resultBlock:^(ALAsset *asset){
ALAssetRepresentation *rep = [asset defaultRepresentation];
CGImageRef iref = [rep fullScreenImage];
if (iref) {
self.underlyingImage = [UIImage imageWithCGImage:iref];
}
[self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
}
failureBlock:^(NSError *error) {
self.underlyingImage = nil;
MWLog(@"Photo from asset library error: %@",error);
[self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
}];
} @catch (NSException *e) {
MWLog(@"Photo from asset library error: %@", e);
[self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
}
}
});
} else if ([_photoURL isFileReferenceURL]) {
// Load from local file async
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@autoreleasepool {
@try {
self.underlyingImage = [UIImage imageWithContentsOfFile:self->_photoURL.path];
if (!self->_underlyingImage) {
MWLog(@"Error loading photo from path: %@", _photoURL.path);
}
} @finally {
[self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
}
}
});
} else {
// Load async from web (using SDWebImage)
@try {
EMSDWebImageManager *manager = [EMSDWebImageManager sharedManager];
_webImageOperation = [manager downloadImageWithURL:_photoURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
if (expectedSize > 0) {
float progress = receivedSize / (float)expectedSize;
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:progress], @"progress",
self, @"photo", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:MWPHOTO_PROGRESS_NOTIFICATION object:dict];
}
}
completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (error) {
MWLog(@"SDWebImage failed to download image: %@", error);
}
self->_webImageOperation = nil;
self.underlyingImage = image;
[self imageLoadingComplete];
}];
} @catch (NSException *e) {
MWLog(@"Photo from web: %@", e);
_webImageOperation = nil;
[self imageLoadingComplete];
}
}
} else {
// Failed - no source
@throw [NSException exceptionWithName:nil reason:nil userInfo:nil];
}
}
// Release if we can get it again from path or url
- (void)unloadUnderlyingImage {
_loadingInProgress = NO;
self.underlyingImage = nil;
}
- (void)imageLoadingComplete {
// NSAssert([[NSThread currentThread] isMainThread], @"This method must be called on the main thread.");
// Complete so notify
_loadingInProgress = NO;
// Notify on next run loop
[self performSelector:@selector(postCompleteNotification) withObject:nil afterDelay:0];
}
- (void)postCompleteNotification {
[[NSNotificationCenter defaultCenter] postNotificationName:MWPHOTO_LOADING_DID_END_NOTIFICATION
object:self];
}
- (void)cancelAnyLoading {
if (_webImageOperation) {
[_webImageOperation cancel];
_loadingInProgress = NO;
}
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWPhotoBrowser.h.svn-base
================================================
//
// MWPhotoBrowser.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 14/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import
#import
#import "MWPhoto.h"
#import "MWPhotoProtocol.h"
#import "MWCaptionView.h"
// Debug Logging
#if 0 // Set to 1 to enable debug logging
#define MWLog(x, ...) NSLog(x, ## __VA_ARGS__);
#else
#define MWLog(x, ...)
#endif
@class MWPhotoBrowser;
@protocol MWPhotoBrowserDelegate
- (NSUInteger)numberOfPhotosInPhotoBrowser:(MWPhotoBrowser *)photoBrowser;
- (id )photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index;
@optional
- (id )photoBrowser:(MWPhotoBrowser *)photoBrowser thumbPhotoAtIndex:(NSUInteger)index;
- (MWCaptionView *)photoBrowser:(MWPhotoBrowser *)photoBrowser captionViewForPhotoAtIndex:(NSUInteger)index;
- (NSString *)photoBrowser:(MWPhotoBrowser *)photoBrowser titleForPhotoAtIndex:(NSUInteger)index;
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser didDisplayPhotoAtIndex:(NSUInteger)index;
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser actionButtonPressedForPhotoAtIndex:(NSUInteger)index;
- (BOOL)photoBrowser:(MWPhotoBrowser *)photoBrowser isPhotoSelectedAtIndex:(NSUInteger)index;
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index selectedChanged:(BOOL)selected;
- (void)photoBrowserDidFinishModalPresentation:(MWPhotoBrowser *)photoBrowser;
@end
@interface MWPhotoBrowser : UIViewController
@property (nonatomic, weak) IBOutlet id delegate;
@property (nonatomic) BOOL zoomPhotosToFill;
@property (nonatomic) BOOL displayNavArrows;
@property (nonatomic) BOOL displayActionButton;
@property (nonatomic) BOOL displaySelectionButtons;
@property (nonatomic) BOOL alwaysShowControls;
@property (nonatomic) BOOL enableGrid;
@property (nonatomic) BOOL enableSwipeToDismiss;
@property (nonatomic) BOOL startOnGrid;
@property (nonatomic) NSUInteger delayToHideElements;
@property (nonatomic, readonly) NSUInteger currentIndex;
// Init
- (id)initWithPhotos:(NSArray *)photosArray __attribute__((deprecated("Use initWithDelegate: instead"))); // Depreciated
- (id)initWithDelegate:(id )delegate;
// Reloads the photo browser and refetches data
- (void)reloadData;
// Set page that photo browser starts on
- (void)setCurrentPhotoIndex:(NSUInteger)index;
- (void)setInitialPageIndex:(NSUInteger)index __attribute__((deprecated("Use setCurrentPhotoIndex: instead"))); // Depreciated
// Navigation
- (void)showNextPhotoAnimated:(BOOL)animated;
- (void)showPreviousPhotoAnimated:(BOOL)animated;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWPhotoBrowser.m.svn-base
================================================
//
// MWPhotoBrowser.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 14/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import
#import "MWCommon.h"
#import "MWPhotoBrowser.h"
#import "MWPhotoBrowserPrivate.h"
#import "EMSDImageCache.h"
#define PADDING 10
#define ACTION_SHEET_OLD_ACTIONS 2000
@implementation MWPhotoBrowser
#pragma mark - Init
- (id)init {
if ((self = [super init])) {
[self _initialisation];
}
return self;
}
- (id)initWithDelegate:(id )delegate {
if ((self = [self init])) {
_delegate = delegate;
}
return self;
}
- (id)initWithPhotos:(NSArray *)photosArray {
if ((self = [self init])) {
_depreciatedPhotoData = photosArray;
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super initWithCoder:decoder])) {
[self _initialisation];
}
return self;
}
- (void)_initialisation {
// Defaults
NSNumber *isVCBasedStatusBarAppearanceNum = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"];
if (isVCBasedStatusBarAppearanceNum) {
_isVCBasedStatusBarAppearance = isVCBasedStatusBarAppearanceNum.boolValue;
} else {
_isVCBasedStatusBarAppearance = YES; // default
}
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7")) self.wantsFullScreenLayout = YES;
#endif
self.hidesBottomBarWhenPushed = YES;
_hasBelongedToViewController = NO;
_photoCount = NSNotFound;
_previousLayoutBounds = CGRectZero;
_currentPageIndex = 0;
_previousPageIndex = NSUIntegerMax;
_displayActionButton = YES;
_displayNavArrows = NO;
_zoomPhotosToFill = YES;
_performingLayout = NO; // Reset on view did appear
_rotating = NO;
_viewIsActive = NO;
_enableGrid = YES;
_startOnGrid = NO;
_enableSwipeToDismiss = YES;
_delayToHideElements = 5;
_visiblePages = [[NSMutableSet alloc] init];
_recycledPages = [[NSMutableSet alloc] init];
_photos = [[NSMutableArray alloc] init];
_thumbPhotos = [[NSMutableArray alloc] init];
_currentGridContentOffset = CGPointMake(0, CGFLOAT_MAX);
_didSavePreviousStateOfNavBar = NO;
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]){
self.automaticallyAdjustsScrollViewInsets = NO;
}
// Listen for MWPhoto notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleMWPhotoLoadingDidEndNotification:)
name:MWPHOTO_LOADING_DID_END_NOTIFICATION
object:nil];
}
- (void)dealloc {
_pagingScrollView.delegate = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self releaseAllUnderlyingPhotos:NO];
[[EMSDImageCache sharedImageCache] clearMemory]; // clear memory
}
- (void)releaseAllUnderlyingPhotos:(BOOL)preserveCurrent {
// Create a copy in case this array is modified while we are looping through
// Release photos
NSArray *copy = [_photos copy];
for (id p in copy) {
if (p != [NSNull null]) {
if (preserveCurrent && p == [self photoAtIndex:self.currentIndex]) {
continue; // skip current
}
[p unloadUnderlyingImage];
}
}
// Release thumbs
copy = [_thumbPhotos copy];
for (id p in copy) {
if (p != [NSNull null]) {
[p unloadUnderlyingImage];
}
}
}
- (void)didReceiveMemoryWarning {
// Release any cached data, images, etc that aren't in use.
[self releaseAllUnderlyingPhotos:YES];
[_recycledPages removeAllObjects];
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
#pragma mark - View Loading
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
// Validate grid settings
if (_startOnGrid) _enableGrid = YES;
if (_enableGrid) {
_enableGrid = [_delegate respondsToSelector:@selector(photoBrowser:thumbPhotoAtIndex:)];
}
if (!_enableGrid) _startOnGrid = NO;
// View
self.view.backgroundColor = [UIColor blackColor];
self.view.clipsToBounds = YES;
// Setup paging scrolling view
CGRect pagingScrollViewFrame = [self frameForPagingScrollView];
_pagingScrollView = [[UIScrollView alloc] initWithFrame:pagingScrollViewFrame];
_pagingScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_pagingScrollView.pagingEnabled = YES;
_pagingScrollView.delegate = self;
_pagingScrollView.showsHorizontalScrollIndicator = NO;
_pagingScrollView.showsVerticalScrollIndicator = NO;
_pagingScrollView.backgroundColor = [UIColor blackColor];
_pagingScrollView.contentSize = [self contentSizeForPagingScrollView];
[self.view addSubview:_pagingScrollView];
// Toolbar
_toolbar = [[UIToolbar alloc] initWithFrame:[self frameForToolbarAtOrientation:self.interfaceOrientation]];
_toolbar.tintColor = SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7") ? HJColor(0xfafafa) : nil;
if ([_toolbar respondsToSelector:@selector(setBarTintColor:)]) {
_toolbar.barTintColor = nil;
}
if ([[UIToolbar class] respondsToSelector:@selector(appearance)]) {
[_toolbar setBackgroundImage:nil forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
[_toolbar setBackgroundImage:nil forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsLandscapePhone];
}
_toolbar.barStyle = UIBarStyleBlackTranslucent;
_toolbar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
// Toolbar Items
if (self.displayNavArrows) {
NSString *arrowPathFormat;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7")) {
arrowPathFormat = @"MWPhotoBrowser.bundle/images/UIBarButtonItemArrowOutline%@.png";
} else {
arrowPathFormat = @"MWPhotoBrowser.bundle/images/UIBarButtonItemArrow%@.png";
}
_previousButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:arrowPathFormat, @"Left"]] style:UIBarButtonItemStylePlain target:self action:@selector(gotoPreviousPage)];
_nextButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:arrowPathFormat, @"Right"]] style:UIBarButtonItemStylePlain target:self action:@selector(gotoNextPage)];
}
if (self.displayActionButton) {
_actionButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(actionButtonPressed:)];
}
// Update
[self reloadData];
// Swipe to dismiss
if (_enableSwipeToDismiss) {
UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(doneButtonPressed:)];
swipeGesture.direction = UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionUp;
[self.view addGestureRecognizer:swipeGesture];
}
// Super
[super viewDidLoad];
}
- (void)performLayout {
// Setup
_performingLayout = YES;
NSUInteger numberOfPhotos = [self numberOfPhotos];
// Setup pages
[_visiblePages removeAllObjects];
[_recycledPages removeAllObjects];
// Navigation buttons
if ([self.navigationController.viewControllers objectAtIndex:0] == self) {
// We're first on stack so show done button
_doneButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"完成", nil) style:UIBarButtonItemStylePlain target:self action:@selector(doneButtonPressed:)];
// Set appearance
if ([UIBarButtonItem respondsToSelector:@selector(appearance)]) {
[_doneButton setBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[_doneButton setBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
[_doneButton setBackgroundImage:nil forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[_doneButton setBackgroundImage:nil forState:UIControlStateHighlighted barMetrics:UIBarMetricsLandscapePhone];
[_doneButton setTitleTextAttributes:[NSDictionary dictionary] forState:UIControlStateNormal];
[_doneButton setTitleTextAttributes:[NSDictionary dictionary] forState:UIControlStateHighlighted];
}
self.navigationItem.rightBarButtonItem = _doneButton;
} else {
// We're not first so show back button
UIViewController *previousViewController = [self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2];
NSString *backButtonTitle = previousViewController.navigationItem.backBarButtonItem ? previousViewController.navigationItem.backBarButtonItem.title : previousViewController.title;
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle:backButtonTitle style:UIBarButtonItemStylePlain target:nil action:nil];
// Appearance
if ([UIBarButtonItem respondsToSelector:@selector(appearance)]) {
[newBackButton setBackButtonBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[newBackButton setBackButtonBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
[newBackButton setBackButtonBackgroundImage:nil forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[newBackButton setBackButtonBackgroundImage:nil forState:UIControlStateHighlighted barMetrics:UIBarMetricsLandscapePhone];
[newBackButton setTitleTextAttributes:[NSDictionary dictionary] forState:UIControlStateNormal];
[newBackButton setTitleTextAttributes:[NSDictionary dictionary] forState:UIControlStateHighlighted];
}
_previousViewControllerBackButton = previousViewController.navigationItem.backBarButtonItem; // remember previous
previousViewController.navigationItem.backBarButtonItem = newBackButton;
}
// Toolbar items
BOOL hasItems = NO;
UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:self action:nil];
fixedSpace.width = 32; // To balance action button
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
NSMutableArray *items = [[NSMutableArray alloc] init];
// Left button - Grid
if (_enableGrid) {
hasItems = YES;
NSString *buttonName = @"UIBarButtonItemGrid";
if (SYSTEM_VERSION_LESS_THAN(@"7")) buttonName = @"UIBarButtonItemGridiOS6";
[items addObject:[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"MWPhotoBrowser.bundle/images/%@.png", buttonName]] style:UIBarButtonItemStylePlain target:self action:@selector(showGridAnimated)]];
} else {
[items addObject:fixedSpace];
}
// Middle - Nav
if (_previousButton && _nextButton && numberOfPhotos > 1) {
hasItems = YES;
[items addObject:flexSpace];
[items addObject:_previousButton];
[items addObject:flexSpace];
[items addObject:_nextButton];
[items addObject:flexSpace];
} else {
[items addObject:flexSpace];
}
// Right - Action
if (_actionButton && !(!hasItems && !self.navigationItem.rightBarButtonItem)) {
[items addObject:_actionButton];
} else {
// We're not showing the toolbar so try and show in top right
if (_actionButton)
self.navigationItem.rightBarButtonItem = _actionButton;
[items addObject:fixedSpace];
}
// Toolbar visibility
[_toolbar setItems:items];
BOOL hideToolbar = YES;
for (UIBarButtonItem* item in _toolbar.items) {
if (item != fixedSpace && item != flexSpace) {
hideToolbar = NO;
break;
}
}
if (hideToolbar) {
[_toolbar removeFromSuperview];
} else {
[self.view addSubview:_toolbar];
}
// Update nav
[self updateNavigation];
// Content offset
_pagingScrollView.contentOffset = [self contentOffsetForPageAtIndex:_currentPageIndex];
[self tilePages];
_performingLayout = NO;
}
// Release any retained subviews of the main view.
- (void)viewDidUnload {
_currentPageIndex = 0;
_pagingScrollView = nil;
_visiblePages = nil;
_recycledPages = nil;
_toolbar = nil;
_previousButton = nil;
_nextButton = nil;
_progressHUD = nil;
[super viewDidUnload];
}
- (BOOL)presentingViewControllerPrefersStatusBarHidden {
UIViewController *presenting = self.presentingViewController;
if (presenting) {
if ([presenting isKindOfClass:[UINavigationController class]]) {
presenting = [(UINavigationController *)presenting topViewController];
}
} else {
// We're in a navigation controller so get previous one!
if (self.navigationController && self.navigationController.viewControllers.count > 1) {
presenting = [self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2];
}
}
if (presenting) {
return [presenting prefersStatusBarHidden];
} else {
return NO;
}
}
#pragma mark - Appearance
- (void)viewWillAppear:(BOOL)animated {
// Super
[super viewWillAppear:animated];
// Status bar
if ([UIViewController instancesRespondToSelector:@selector(prefersStatusBarHidden)]) {
_leaveStatusBarAlone = [self presentingViewControllerPrefersStatusBarHidden];
} else {
_leaveStatusBarAlone = [UIApplication sharedApplication].statusBarHidden;
}
if (CGRectEqualToRect([[UIApplication sharedApplication] statusBarFrame], CGRectZero)) {
// If the frame is zero then definitely leave it alone
_leaveStatusBarAlone = YES;
}
BOOL fullScreen = YES;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7")) fullScreen = self.wantsFullScreenLayout;
#endif
if (!_leaveStatusBarAlone && fullScreen && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
_previousStatusBarStyle = [[UIApplication sharedApplication] statusBarStyle];
if (SYSTEM_VERSION_LESS_THAN(@"7")) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent animated:animated];
#pragma clang diagnostic push
} else {
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:animated];
}
}
// Navigation bar appearance
if (!_viewIsActive && [self.navigationController.viewControllers objectAtIndex:0] != self) {
[self storePreviousNavBarAppearance];
}
[self setNavBarAppearance:animated];
// Update UI
[self hideControlsAfterDelay];
// Initial appearance
if (!_viewHasAppearedInitially) {
if (_startOnGrid) {
[self showGrid:NO];
}
_viewHasAppearedInitially = YES;
}
}
- (void)viewWillDisappear:(BOOL)animated {
// Check that we're being popped for good
if ([self.navigationController.viewControllers objectAtIndex:0] != self &&
![self.navigationController.viewControllers containsObject:self]) {
// State
_viewIsActive = NO;
// Bar state / appearance
[self restorePreviousNavBarAppearance:animated];
}
// Controls
[self.navigationController.navigationBar.layer removeAllAnimations]; // Stop all animations on nav bar
[NSObject cancelPreviousPerformRequestsWithTarget:self]; // Cancel any pending toggles from taps
[self setControlsHidden:NO animated:NO permanent:YES];
// Status bar
BOOL fullScreen = YES;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7")) fullScreen = self.wantsFullScreenLayout;
#endif
if (!_leaveStatusBarAlone && fullScreen && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
[[UIApplication sharedApplication] setStatusBarStyle:_previousStatusBarStyle animated:animated];
}
// Super
[super viewWillDisappear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
_viewIsActive = YES;
}
- (void)willMoveToParentViewController:(UIViewController *)parent {
if (parent && _hasBelongedToViewController) {
[NSException raise:@"MWPhotoBrowser Instance Reuse" format:@"MWPhotoBrowser instances cannot be reused."];
}
}
- (void)didMoveToParentViewController:(UIViewController *)parent {
if (!parent) _hasBelongedToViewController = YES;
}
#pragma mark - Nav Bar Appearance
- (void)setNavBarAppearance:(BOOL)animated {
[self.navigationController setNavigationBarHidden:NO animated:animated];
UINavigationBar *navBar = self.navigationController.navigationBar;
navBar.tintColor = SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7") ? HJColor(0xfafafa) : nil;
if ([navBar respondsToSelector:@selector(setBarTintColor:)]) {
navBar.barTintColor = nil;
navBar.shadowImage = nil;
}
navBar.translucent = YES;
navBar.barStyle = UIBarStyleBlackTranslucent;
if ([[UINavigationBar class] respondsToSelector:@selector(appearance)]) {
[navBar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
[navBar setBackgroundImage:nil forBarMetrics:UIBarMetricsLandscapePhone];
}
}
- (void)storePreviousNavBarAppearance {
_didSavePreviousStateOfNavBar = YES;
if ([UINavigationBar instancesRespondToSelector:@selector(barTintColor)]) {
_previousNavBarBarTintColor = self.navigationController.navigationBar.barTintColor;
}
_previousNavBarTranslucent = self.navigationController.navigationBar.translucent;
_previousNavBarTintColor = self.navigationController.navigationBar.tintColor;
_previousNavBarHidden = self.navigationController.navigationBarHidden;
_previousNavBarStyle = self.navigationController.navigationBar.barStyle;
if ([[UINavigationBar class] respondsToSelector:@selector(appearance)]) {
_previousNavigationBarBackgroundImageDefault = [self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsDefault];
_previousNavigationBarBackgroundImageLandscapePhone = [self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsLandscapePhone];
}
}
- (void)restorePreviousNavBarAppearance:(BOOL)animated {
if (_didSavePreviousStateOfNavBar) {
[self.navigationController setNavigationBarHidden:_previousNavBarHidden animated:animated];
UINavigationBar *navBar = self.navigationController.navigationBar;
navBar.tintColor = _previousNavBarTintColor;
navBar.translucent = _previousNavBarTranslucent;
if ([UINavigationBar instancesRespondToSelector:@selector(barTintColor)]) {
navBar.barTintColor = _previousNavBarBarTintColor;
}
navBar.barStyle = _previousNavBarStyle;
if ([[UINavigationBar class] respondsToSelector:@selector(appearance)]) {
[navBar setBackgroundImage:_previousNavigationBarBackgroundImageDefault forBarMetrics:UIBarMetricsDefault];
[navBar setBackgroundImage:_previousNavigationBarBackgroundImageLandscapePhone forBarMetrics:UIBarMetricsLandscapePhone];
}
// Restore back button if we need to
if (_previousViewControllerBackButton) {
UIViewController *previousViewController = [self.navigationController topViewController]; // We've disappeared so previous is now top
previousViewController.navigationItem.backBarButtonItem = _previousViewControllerBackButton;
_previousViewControllerBackButton = nil;
}
}
}
#pragma mark - Layout
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[self layoutVisiblePages];
}
- (void)layoutVisiblePages {
// Flag
_performingLayout = YES;
// Toolbar
_toolbar.frame = [self frameForToolbarAtOrientation:self.interfaceOrientation];
// Remember index
NSUInteger indexPriorToLayout = _currentPageIndex;
// Get paging scroll view frame to determine if anything needs changing
CGRect pagingScrollViewFrame = [self frameForPagingScrollView];
// Frame needs changing
if (!_skipNextPagingScrollViewPositioning) {
_pagingScrollView.frame = pagingScrollViewFrame;
}
_skipNextPagingScrollViewPositioning = NO;
// Recalculate contentSize based on current orientation
_pagingScrollView.contentSize = [self contentSizeForPagingScrollView];
// Adjust frames and configuration of each visible page
for (MWZoomingScrollView *page in _visiblePages) {
NSUInteger index = page.index;
page.frame = [self frameForPageAtIndex:index];
if (page.captionView) {
page.captionView.frame = [self frameForCaptionView:page.captionView atIndex:index];
}
if (page.selectedButton) {
page.selectedButton.frame = [self frameForSelectedButton:page.selectedButton atIndex:index];
}
// Adjust scales if bounds has changed since last time
if (!CGRectEqualToRect(_previousLayoutBounds, self.view.bounds)) {
// Update zooms for new bounds
[page setMaxMinZoomScalesForCurrentBounds];
_previousLayoutBounds = self.view.bounds;
}
}
// Adjust contentOffset to preserve page location based on values collected prior to location
_pagingScrollView.contentOffset = [self contentOffsetForPageAtIndex:indexPriorToLayout];
[self didStartViewingPageAtIndex:_currentPageIndex]; // initial
// Reset
_currentPageIndex = indexPriorToLayout;
_performingLayout = NO;
}
#pragma mark - Rotation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
// Remember page index before rotation
_pageIndexBeforeRotation = _currentPageIndex;
_rotating = YES;
// In iOS 7 the nav bar gets shown after rotation, but might as well do this for everything!
if ([self areControlsHidden]) {
// Force hidden
self.navigationController.navigationBarHidden = YES;
}
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
// Perform layout
_currentPageIndex = _pageIndexBeforeRotation;
// Delay control holding
[self hideControlsAfterDelay];
// Layout
[self layoutVisiblePages];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
_rotating = NO;
// Ensure nav bar isn't re-displayed
if ([self areControlsHidden]) {
self.navigationController.navigationBarHidden = NO;
self.navigationController.navigationBar.alpha = 0;
}
}
#pragma mark - Data
- (NSUInteger)currentIndex {
return _currentPageIndex;
}
- (void)reloadData {
// Reset
_photoCount = NSNotFound;
// Get data
NSUInteger numberOfPhotos = [self numberOfPhotos];
[self releaseAllUnderlyingPhotos:YES];
[_photos removeAllObjects];
[_thumbPhotos removeAllObjects];
for (int i = 0; i < numberOfPhotos; i++) {
[_photos addObject:[NSNull null]];
[_thumbPhotos addObject:[NSNull null]];
}
// Update current page index
if (numberOfPhotos > 0) {
_currentPageIndex = MAX(0, MIN(_currentPageIndex, numberOfPhotos - 1));
} else {
_currentPageIndex = 0;
}
// Update layout
if ([self isViewLoaded]) {
while (_pagingScrollView.subviews.count) {
[[_pagingScrollView.subviews lastObject] removeFromSuperview];
}
[self performLayout];
[self.view setNeedsLayout];
}
}
- (NSUInteger)numberOfPhotos {
if (_photoCount == NSNotFound) {
if ([_delegate respondsToSelector:@selector(numberOfPhotosInPhotoBrowser:)]) {
_photoCount = [_delegate numberOfPhotosInPhotoBrowser:self];
} else if (_depreciatedPhotoData) {
_photoCount = _depreciatedPhotoData.count;
}
}
if (_photoCount == NSNotFound) _photoCount = 0;
return _photoCount;
}
- (id)photoAtIndex:(NSUInteger)index {
id photo = nil;
if (index < _photos.count) {
if ([_photos objectAtIndex:index] == [NSNull null]) {
if ([_delegate respondsToSelector:@selector(photoBrowser:photoAtIndex:)]) {
photo = [_delegate photoBrowser:self photoAtIndex:index];
} else if (_depreciatedPhotoData && index < _depreciatedPhotoData.count) {
photo = [_depreciatedPhotoData objectAtIndex:index];
}
if (photo) [_photos replaceObjectAtIndex:index withObject:photo];
} else {
photo = [_photos objectAtIndex:index];
}
}
return photo;
}
- (id)thumbPhotoAtIndex:(NSUInteger)index {
id photo = nil;
if (index < _thumbPhotos.count) {
if ([_thumbPhotos objectAtIndex:index] == [NSNull null]) {
if ([_delegate respondsToSelector:@selector(photoBrowser:thumbPhotoAtIndex:)]) {
photo = [_delegate photoBrowser:self thumbPhotoAtIndex:index];
}
if (photo) [_thumbPhotos replaceObjectAtIndex:index withObject:photo];
} else {
photo = [_thumbPhotos objectAtIndex:index];
}
}
return photo;
}
- (MWCaptionView *)captionViewForPhotoAtIndex:(NSUInteger)index {
MWCaptionView *captionView = nil;
if ([_delegate respondsToSelector:@selector(photoBrowser:captionViewForPhotoAtIndex:)]) {
captionView = [_delegate photoBrowser:self captionViewForPhotoAtIndex:index];
} else {
id photo = [self photoAtIndex:index];
if ([photo respondsToSelector:@selector(caption)]) {
if ([photo caption]) captionView = [[MWCaptionView alloc] initWithPhoto:photo];
}
}
captionView.alpha = [self areControlsHidden] ? 0 : 1; // Initial alpha
return captionView;
}
- (BOOL)photoIsSelectedAtIndex:(NSUInteger)index {
BOOL value = NO;
if (_displaySelectionButtons) {
if ([self.delegate respondsToSelector:@selector(photoBrowser:isPhotoSelectedAtIndex:)]) {
value = [self.delegate photoBrowser:self isPhotoSelectedAtIndex:index];
}
}
return value;
}
- (void)setPhotoSelected:(BOOL)selected atIndex:(NSUInteger)index {
if (_displaySelectionButtons) {
if ([self.delegate respondsToSelector:@selector(photoBrowser:photoAtIndex:selectedChanged:)]) {
[self.delegate photoBrowser:self photoAtIndex:index selectedChanged:selected];
}
}
}
- (UIImage *)imageForPhoto:(id)photo {
if (photo) {
// Get image or obtain in background
if ([photo underlyingImage]) {
return [photo underlyingImage];
} else {
[photo loadUnderlyingImageAndNotify];
}
}
return nil;
}
- (void)loadAdjacentPhotosIfNecessary:(id)photo {
MWZoomingScrollView *page = [self pageDisplayingPhoto:photo];
if (page) {
// If page is current page then initiate loading of previous and next pages
NSUInteger pageIndex = page.index;
if (_currentPageIndex == pageIndex) {
if (pageIndex > 0) {
// Preload index - 1
id photo = [self photoAtIndex:pageIndex-1];
if (![photo underlyingImage]) {
[photo loadUnderlyingImageAndNotify];
MWLog(@"Pre-loading image at index %lu", (unsigned long)pageIndex-1);
}
}
if (pageIndex < [self numberOfPhotos] - 1) {
// Preload index + 1
id photo = [self photoAtIndex:pageIndex+1];
if (![photo underlyingImage]) {
[photo loadUnderlyingImageAndNotify];
MWLog(@"Pre-loading image at index %lu", (unsigned long)pageIndex+1);
}
}
}
}
}
#pragma mark - MWPhoto Loading Notification
- (void)handleMWPhotoLoadingDidEndNotification:(NSNotification *)notification {
id photo = [notification object];
MWZoomingScrollView *page = [self pageDisplayingPhoto:photo];
if (page) {
if ([photo underlyingImage]) {
// Successful load
[page displayImage];
[self loadAdjacentPhotosIfNecessary:photo];
} else {
// Failed to load
[page displayImageFailure];
}
// Update nav
[self updateNavigation];
}
}
#pragma mark - Paging
- (void)tilePages {
// Calculate which pages should be visible
// Ignore padding as paging bounces encroach on that
// and lead to false page loads
CGRect visibleBounds = _pagingScrollView.bounds;
NSInteger iFirstIndex = (NSInteger)floorf((CGRectGetMinX(visibleBounds)+PADDING*2) / CGRectGetWidth(visibleBounds));
NSInteger iLastIndex = (NSInteger)floorf((CGRectGetMaxX(visibleBounds)-PADDING*2-1) / CGRectGetWidth(visibleBounds));
if (iFirstIndex < 0) iFirstIndex = 0;
if (iFirstIndex > [self numberOfPhotos] - 1) iFirstIndex = [self numberOfPhotos] - 1;
if (iLastIndex < 0) iLastIndex = 0;
if (iLastIndex > [self numberOfPhotos] - 1) iLastIndex = [self numberOfPhotos] - 1;
// Recycle no longer needed pages
NSInteger pageIndex;
for (MWZoomingScrollView *page in _visiblePages) {
pageIndex = page.index;
if (pageIndex < (NSUInteger)iFirstIndex || pageIndex > (NSUInteger)iLastIndex) {
[_recycledPages addObject:page];
[page.captionView removeFromSuperview];
[page.selectedButton removeFromSuperview];
[page prepareForReuse];
[page removeFromSuperview];
MWLog(@"Removed page at index %lu", (unsigned long)pageIndex);
}
}
[_visiblePages minusSet:_recycledPages];
while (_recycledPages.count > 2) // Only keep 2 recycled pages
[_recycledPages removeObject:[_recycledPages anyObject]];
// Add missing pages
for (NSUInteger index = (NSUInteger)iFirstIndex; index <= (NSUInteger)iLastIndex; index++) {
if (![self isDisplayingPageForIndex:index]) {
// Add new page
MWZoomingScrollView *page = [self dequeueRecycledPage];
if (!page) {
page = [[MWZoomingScrollView alloc] initWithPhotoBrowser:self];
}
[_visiblePages addObject:page];
[self configurePage:page forIndex:index];
[_pagingScrollView addSubview:page];
MWLog(@"Added page at index %lu", (unsigned long)index);
// Add caption
MWCaptionView *captionView = [self captionViewForPhotoAtIndex:index];
if (captionView) {
captionView.frame = [self frameForCaptionView:captionView atIndex:index];
[_pagingScrollView addSubview:captionView];
page.captionView = captionView;
}
// Add selected button
if (self.displaySelectionButtons) {
UIButton *selectedButton = [UIButton buttonWithType:UIButtonTypeCustom];
[selectedButton setImage:[UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageSelectedOff.png"] forState:UIControlStateNormal];
[selectedButton setImage:[UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageSelectedOn.png"] forState:UIControlStateSelected];
[selectedButton sizeToFit];
selectedButton.adjustsImageWhenHighlighted = NO;
[selectedButton addTarget:self action:@selector(selectedButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
selectedButton.frame = [self frameForSelectedButton:selectedButton atIndex:index];
[_pagingScrollView addSubview:selectedButton];
page.selectedButton = selectedButton;
selectedButton.selected = [self photoIsSelectedAtIndex:index];
}
}
}
}
- (void)updateVisiblePageStates {
NSSet *copy = [_visiblePages copy];
for (MWZoomingScrollView *page in copy) {
// Update selection
page.selectedButton.selected = [self photoIsSelectedAtIndex:page.index];
}
}
- (BOOL)isDisplayingPageForIndex:(NSUInteger)index {
for (MWZoomingScrollView *page in _visiblePages)
if (page.index == index) return YES;
return NO;
}
- (MWZoomingScrollView *)pageDisplayedAtIndex:(NSUInteger)index {
MWZoomingScrollView *thePage = nil;
for (MWZoomingScrollView *page in _visiblePages) {
if (page.index == index) {
thePage = page; break;
}
}
return thePage;
}
- (MWZoomingScrollView *)pageDisplayingPhoto:(id)photo {
MWZoomingScrollView *thePage = nil;
for (MWZoomingScrollView *page in _visiblePages) {
if (page.photo == photo) {
thePage = page; break;
}
}
return thePage;
}
- (void)configurePage:(MWZoomingScrollView *)page forIndex:(NSUInteger)index {
page.frame = [self frameForPageAtIndex:index];
page.index = index;
page.photo = [self photoAtIndex:index];
}
- (MWZoomingScrollView *)dequeueRecycledPage {
MWZoomingScrollView *page = [_recycledPages anyObject];
if (page) {
[_recycledPages removeObject:page];
}
return page;
}
// Handle page changes
- (void)didStartViewingPageAtIndex:(NSUInteger)index {
if (![self numberOfPhotos]) {
// Show controls
[self setControlsHidden:NO animated:YES permanent:YES];
return;
}
// Release images further away than +/-1
NSUInteger i;
if (index > 0) {
// Release anything < index - 1
for (i = 0; i < index-1; i++) {
id photo = [_photos objectAtIndex:i];
if (photo != [NSNull null]) {
[photo unloadUnderlyingImage];
[_photos replaceObjectAtIndex:i withObject:[NSNull null]];
MWLog(@"Released underlying image at index %lu", (unsigned long)i);
}
}
}
if (index < [self numberOfPhotos] - 1) {
// Release anything > index + 1
for (i = index + 2; i < _photos.count; i++) {
id photo = [_photos objectAtIndex:i];
if (photo != [NSNull null]) {
[photo unloadUnderlyingImage];
[_photos replaceObjectAtIndex:i withObject:[NSNull null]];
MWLog(@"Released underlying image at index %lu", (unsigned long)i);
}
}
}
// Load adjacent images if needed and the photo is already
// loaded. Also called after photo has been loaded in background
id currentPhoto = [self photoAtIndex:index];
if ([currentPhoto underlyingImage]) {
// photo loaded so load ajacent now
[self loadAdjacentPhotosIfNecessary:currentPhoto];
}
// Notify delegate
if (index != _previousPageIndex) {
if ([_delegate respondsToSelector:@selector(photoBrowser:didDisplayPhotoAtIndex:)])
[_delegate photoBrowser:self didDisplayPhotoAtIndex:index];
_previousPageIndex = index;
}
// Update nav
[self updateNavigation];
}
#pragma mark - Frame Calculations
- (CGRect)frameForPagingScrollView {
CGRect frame = self.view.bounds;// [[UIScreen mainScreen] bounds];
frame.origin.x -= PADDING;
frame.size.width += (2 * PADDING);
return CGRectIntegral(frame);
}
- (CGRect)frameForPageAtIndex:(NSUInteger)index {
// We have to use our paging scroll view's bounds, not frame, to calculate the page placement. When the device is in
// landscape orientation, the frame will still be in portrait because the pagingScrollView is the root view controller's
// view, so its frame is in window coordinate space, which is never rotated. Its bounds, however, will be in landscape
// because it has a rotation transform applied.
CGRect bounds = _pagingScrollView.bounds;
CGRect pageFrame = bounds;
pageFrame.size.width -= (2 * PADDING);
pageFrame.origin.x = (bounds.size.width * index) + PADDING;
return CGRectIntegral(pageFrame);
}
- (CGSize)contentSizeForPagingScrollView {
// We have to use the paging scroll view's bounds to calculate the contentSize, for the same reason outlined above.
CGRect bounds = _pagingScrollView.bounds;
return CGSizeMake(bounds.size.width * [self numberOfPhotos], bounds.size.height);
}
- (CGPoint)contentOffsetForPageAtIndex:(NSUInteger)index {
CGFloat pageWidth = _pagingScrollView.bounds.size.width;
CGFloat newOffset = index * pageWidth;
return CGPointMake(newOffset, 0);
}
- (CGRect)frameForToolbarAtOrientation:(UIInterfaceOrientation)orientation {
CGFloat height = 44;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone &&
UIInterfaceOrientationIsLandscape(orientation)) height = 32;
return CGRectIntegral(CGRectMake(0, self.view.bounds.size.height - height, self.view.bounds.size.width, height));
}
- (CGRect)frameForCaptionView:(MWCaptionView *)captionView atIndex:(NSUInteger)index {
CGRect pageFrame = [self frameForPageAtIndex:index];
CGSize captionSize = [captionView sizeThatFits:CGSizeMake(pageFrame.size.width, 0)];
CGRect captionFrame = CGRectMake(pageFrame.origin.x,
pageFrame.size.height - captionSize.height - (_toolbar.superview?_toolbar.frame.size.height:0),
pageFrame.size.width,
captionSize.height);
return CGRectIntegral(captionFrame);
}
- (CGRect)frameForSelectedButton:(UIButton *)selectedButton atIndex:(NSUInteger)index {
CGRect pageFrame = [self frameForPageAtIndex:index];
CGFloat yOffset = 0;
if (![self areControlsHidden]) {
UINavigationBar *navBar = self.navigationController.navigationBar;
yOffset = navBar.frame.origin.y + navBar.frame.size.height;
}
CGFloat statusBarOffset = [[UIApplication sharedApplication] statusBarFrame].size.height;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7") && !self.wantsFullScreenLayout) statusBarOffset = 0;
#endif
CGRect captionFrame = CGRectMake(pageFrame.origin.x + pageFrame.size.width - 20 - selectedButton.frame.size.width,
statusBarOffset + yOffset,
selectedButton.frame.size.width,
selectedButton.frame.size.height);
return CGRectIntegral(captionFrame);
}
#pragma mark - UIScrollView Delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// Checks
if (!_viewIsActive || _performingLayout || _rotating) return;
// Tile pages
[self tilePages];
// Calculate current page
CGRect visibleBounds = _pagingScrollView.bounds;
NSInteger index = (NSInteger)(floorf(CGRectGetMidX(visibleBounds) / CGRectGetWidth(visibleBounds)));
if (index < 0) index = 0;
if (index > [self numberOfPhotos] - 1) index = [self numberOfPhotos] - 1;
NSUInteger previousCurrentPage = _currentPageIndex;
_currentPageIndex = index;
if (_currentPageIndex != previousCurrentPage) {
[self didStartViewingPageAtIndex:index];
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// Hide controls when dragging begins
[self setControlsHidden:YES animated:YES permanent:NO];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// Update nav when page changes
[self updateNavigation];
}
#pragma mark - Navigation
- (void)updateNavigation {
// Title
NSUInteger numberOfPhotos = [self numberOfPhotos];
if (_gridController) {
if (_gridController.selectionMode) {
self.title = NSLocalizedString(@"Select Photos", nil);
} else {
NSString *photosText;
if (numberOfPhotos == 1) {
photosText = NSLocalizedString(@"photo", @"Used in the context: '1 photo'");
} else {
photosText = NSLocalizedString(@"photos", @"Used in the context: '3 photos'");
}
self.title = [NSString stringWithFormat:@"%lu %@", (unsigned long)numberOfPhotos, photosText];
}
} else if (numberOfPhotos > 1) {
if ([_delegate respondsToSelector:@selector(photoBrowser:titleForPhotoAtIndex:)]) {
self.title = [_delegate photoBrowser:self titleForPhotoAtIndex:_currentPageIndex];
} else {
self.title = [NSString stringWithFormat:@"%lu %@ %lu", (unsigned long)(_currentPageIndex+1), NSLocalizedString(@"of", @"Used in the context: 'Showing 1 of 3 items'"), (unsigned long)numberOfPhotos];
}
} else {
self.title = nil;
}
// Buttons
_previousButton.enabled = (_currentPageIndex > 0);
_nextButton.enabled = (_currentPageIndex < numberOfPhotos - 1);
_actionButton.enabled = [[self photoAtIndex:_currentPageIndex] underlyingImage] != nil;
}
- (void)jumpToPageAtIndex:(NSUInteger)index animated:(BOOL)animated {
// Change page
if (index < [self numberOfPhotos]) {
CGRect pageFrame = [self frameForPageAtIndex:index];
[_pagingScrollView setContentOffset:CGPointMake(pageFrame.origin.x - PADDING, 0) animated:animated];
[self updateNavigation];
}
// Update timer to give more time
[self hideControlsAfterDelay];
}
- (void)gotoPreviousPage {
[self showPreviousPhotoAnimated:NO];
}
- (void)gotoNextPage {
[self showNextPhotoAnimated:NO];
}
- (void)showPreviousPhotoAnimated:(BOOL)animated {
[self jumpToPageAtIndex:_currentPageIndex-1 animated:animated];
}
- (void)showNextPhotoAnimated:(BOOL)animated {
[self jumpToPageAtIndex:_currentPageIndex+1 animated:animated];
}
#pragma mark - Interactions
- (void)selectedButtonTapped:(id)sender {
UIButton *selectedButton = (UIButton *)sender;
selectedButton.selected = !selectedButton.selected;
NSUInteger index = NSUIntegerMax;
for (MWZoomingScrollView *page in _visiblePages) {
if (page.selectedButton == selectedButton) {
index = page.index;
break;
}
}
if (index != NSUIntegerMax) {
[self setPhotoSelected:selectedButton.selected atIndex:index];
}
}
#pragma mark - Grid
- (void)showGridAnimated {
[self showGrid:YES];
}
- (void)showGrid:(BOOL)animated {
if (_gridController) return;
// Init grid controller
_gridController = [[MWGridViewController alloc] init];
_gridController.initialContentOffset = _currentGridContentOffset;
_gridController.browser = self;
_gridController.selectionMode = _displaySelectionButtons;
_gridController.view.frame = self.view.bounds;
_gridController.view.frame = CGRectOffset(_gridController.view.frame, 0, (self.startOnGrid ? -1 : 1) * self.view.bounds.size.height);
// Stop specific layout being triggered
_skipNextPagingScrollViewPositioning = YES;
// Add as a child view controller
[self addChildViewController:_gridController];
[self.view addSubview:_gridController.view];
// Hide action button on nav bar if it exists
if (self.navigationItem.rightBarButtonItem == _actionButton) {
_gridPreviousRightNavItem = _actionButton;
[self.navigationItem setRightBarButtonItem:nil animated:YES];
} else {
_gridPreviousRightNavItem = nil;
}
// Update
[self updateNavigation];
[self setControlsHidden:NO animated:YES permanent:YES];
// Animate grid in and photo scroller out
[UIView animateWithDuration:animated ? 0.3 : 0 animations:^(void) {
self->_gridController.view.frame = self.view.bounds;
CGRect newPagingFrame = [self frameForPagingScrollView];
newPagingFrame = CGRectOffset(newPagingFrame, 0, (self.startOnGrid ? 1 : -1) * newPagingFrame.size.height);
self->_pagingScrollView.frame = newPagingFrame;
} completion:^(BOOL finished) {
[self->_gridController didMoveToParentViewController:self];
}];
}
- (void)hideGrid {
if (!_gridController) return;
// Remember previous content offset
_currentGridContentOffset = _gridController.collectionView.contentOffset;
// Restore action button if it was removed
if (_gridPreviousRightNavItem == _actionButton && _actionButton) {
[self.navigationItem setRightBarButtonItem:_gridPreviousRightNavItem animated:YES];
}
// Position prior to hide animation
CGRect newPagingFrame = [self frameForPagingScrollView];
newPagingFrame = CGRectOffset(newPagingFrame, 0, (self.startOnGrid ? 1 : -1) * newPagingFrame.size.height);
_pagingScrollView.frame = newPagingFrame;
// Remember and remove controller now so things can detect a nil grid controller
MWGridViewController *tmpGridController = _gridController;
_gridController = nil;
// Update
[self updateNavigation];
[self updateVisiblePageStates];
// Animate, hide grid and show paging scroll view
[UIView animateWithDuration:0.3 animations:^{
tmpGridController.view.frame = CGRectOffset(self.view.bounds, 0, (self.startOnGrid ? -1 : 1) * self.view.bounds.size.height);
self->_pagingScrollView.frame = [self frameForPagingScrollView];
} completion:^(BOOL finished) {
[tmpGridController willMoveToParentViewController:nil];
[tmpGridController.view removeFromSuperview];
[tmpGridController removeFromParentViewController];
[self setControlsHidden:NO animated:YES permanent:NO]; // retrigger timer
}];
}
#pragma mark - Control Hiding / Showing
// If permanent then we don't set timers to hide again
// Fades all controls on iOS 5 & 6, and iOS 7 controls slide and fade
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated permanent:(BOOL)permanent {
// Force visible
if (![self numberOfPhotos] || _gridController || _alwaysShowControls)
hidden = NO;
// Cancel any timers
[self cancelControlHiding];
// Animations & positions
BOOL slideAndFade = SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7");
CGFloat animatonOffset = 20;
CGFloat animationDuration = (animated ? 0.35 : 0);
// Status bar
if (!_leaveStatusBarAlone) {
if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
// iOS 7
// Hide status bar
if (!_isVCBasedStatusBarAppearance) {
// Non-view controller based
[[UIApplication sharedApplication] setStatusBarHidden:hidden withAnimation:animated ? UIStatusBarAnimationSlide : UIStatusBarAnimationNone];
} else {
// View controller based so animate away
_statusBarShouldBeHidden = hidden;
[UIView animateWithDuration:animationDuration animations:^(void) {
[self setNeedsStatusBarAppearanceUpdate];
} completion:^(BOOL finished) {}];
}
} else {
// iOS < 7
// Status bar and nav bar positioning
BOOL fullScreen = YES;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7")) fullScreen = self.wantsFullScreenLayout;
#endif
if (fullScreen) {
// Need to get heights and set nav bar position to overcome display issues
// Get status bar height if visible
CGFloat statusBarHeight = 0;
if (![UIApplication sharedApplication].statusBarHidden) {
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
statusBarHeight = MIN(statusBarFrame.size.height, statusBarFrame.size.width);
}
// Status Bar
[[UIApplication sharedApplication] setStatusBarHidden:hidden withAnimation:animated?UIStatusBarAnimationFade:UIStatusBarAnimationNone];
// Get status bar height if visible
if (![UIApplication sharedApplication].statusBarHidden) {
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
statusBarHeight = MIN(statusBarFrame.size.height, statusBarFrame.size.width);
}
// Set navigation bar frame
CGRect navBarFrame = self.navigationController.navigationBar.frame;
navBarFrame.origin.y = statusBarHeight;
self.navigationController.navigationBar.frame = navBarFrame;
}
}
}
// Toolbar, nav bar and captions
// Pre-appear animation positions for iOS 7 sliding
if (slideAndFade && [self areControlsHidden] && !hidden && animated) {
// Toolbar
_toolbar.frame = CGRectOffset([self frameForToolbarAtOrientation:self.interfaceOrientation], 0, animatonOffset);
// Captions
for (MWZoomingScrollView *page in _visiblePages) {
if (page.captionView) {
MWCaptionView *v = page.captionView;
// Pass any index, all we're interested in is the Y
CGRect captionFrame = [self frameForCaptionView:v atIndex:0];
captionFrame.origin.x = v.frame.origin.x; // Reset X
v.frame = CGRectOffset(captionFrame, 0, animatonOffset);
}
}
}
[UIView animateWithDuration:animationDuration animations:^(void) {
CGFloat alpha = hidden ? 0 : 1;
// Nav bar slides up on it's own on iOS 7
[self.navigationController.navigationBar setAlpha:alpha];
// Toolbar
if (slideAndFade) {
self->_toolbar.frame = [self frameForToolbarAtOrientation:self.interfaceOrientation];
if (hidden) self->_toolbar.frame = CGRectOffset(self->_toolbar.frame, 0, animatonOffset);
}
self->_toolbar.alpha = alpha;
// Captions
for (MWZoomingScrollView *page in self->_visiblePages) {
if (page.captionView) {
MWCaptionView *v = page.captionView;
if (slideAndFade) {
// Pass any index, all we're interested in is the Y
CGRect captionFrame = [self frameForCaptionView:v atIndex:0];
captionFrame.origin.x = v.frame.origin.x; // Reset X
if (hidden) captionFrame = CGRectOffset(captionFrame, 0, animatonOffset);
v.frame = captionFrame;
}
v.alpha = alpha;
}
}
// Selected buttons
for (MWZoomingScrollView *page in self->_visiblePages) {
if (page.selectedButton) {
UIButton *v = page.selectedButton;
CGRect newFrame = [self frameForSelectedButton:v atIndex:0];
newFrame.origin.x = v.frame.origin.x;
v.frame = newFrame;
}
}
} completion:^(BOOL finished) {}];
// Control hiding timer
// Will cancel existing timer but only begin hiding if
// they are visible
if (!permanent) [self hideControlsAfterDelay];
}
- (BOOL)prefersStatusBarHidden {
if (!_leaveStatusBarAlone) {
return _statusBarShouldBeHidden;
} else {
return [self presentingViewControllerPrefersStatusBarHidden];
}
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return UIStatusBarAnimationSlide;
}
- (void)cancelControlHiding {
// If a timer exists then cancel and release
if (_controlVisibilityTimer) {
[_controlVisibilityTimer invalidate];
_controlVisibilityTimer = nil;
}
}
// Enable/disable control visiblity timer
- (void)hideControlsAfterDelay {
if (![self areControlsHidden]) {
[self cancelControlHiding];
_controlVisibilityTimer = [NSTimer scheduledTimerWithTimeInterval:self.delayToHideElements target:self selector:@selector(hideControls) userInfo:nil repeats:NO];
}
}
- (BOOL)areControlsHidden { return (_toolbar.alpha == 0); }
- (void)hideControls { [self setControlsHidden:YES animated:YES permanent:NO]; }
- (void)toggleControls { [self setControlsHidden:![self areControlsHidden] animated:YES permanent:NO]; }
#pragma mark - Properties
// Handle depreciated method
- (void)setInitialPageIndex:(NSUInteger)index {
[self setCurrentPhotoIndex:index];
}
- (void)setCurrentPhotoIndex:(NSUInteger)index {
// Validate
NSUInteger photoCount = [self numberOfPhotos];
if (photoCount == 0) {
index = 0;
} else {
if (index >= photoCount)
index = [self numberOfPhotos]-1;
}
_currentPageIndex = index;
if ([self isViewLoaded]) {
[self jumpToPageAtIndex:index animated:NO];
if (!_viewIsActive)
[self tilePages]; // Force tiling if view is not visible
}
}
#pragma mark - Misc
- (void)doneButtonPressed:(id)sender {
// Only if we're modal and there's a done button
if (_doneButton) {
// See if we actually just want to show/hide grid
if (self.enableGrid) {
if (self.startOnGrid && !_gridController) {
[self showGrid:YES];
return;
} else if (!self.startOnGrid && _gridController) {
[self hideGrid];
return;
}
}
// Dismiss view controller
if ([_delegate respondsToSelector:@selector(photoBrowserDidFinishModalPresentation:)]) {
// Call delegate method and let them dismiss us
[_delegate photoBrowserDidFinishModalPresentation:self];
} else {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
}
#pragma mark - Actions
- (void)actionButtonPressed:(id)sender {
if (_actionsSheet) {
// Dismiss
[_actionsSheet dismissWithClickedButtonIndex:_actionsSheet.cancelButtonIndex animated:YES];
} else {
// Only react when image has loaded
id photo = [self photoAtIndex:_currentPageIndex];
if ([self numberOfPhotos] > 0 && [photo underlyingImage]) {
// If they have defined a delegate method then just message them
if ([self.delegate respondsToSelector:@selector(photoBrowser:actionButtonPressedForPhotoAtIndex:)]) {
// Let delegate handle things
[self.delegate photoBrowser:self actionButtonPressedForPhotoAtIndex:_currentPageIndex];
} else {
// Handle default actions
if (SYSTEM_VERSION_LESS_THAN(@"6")) {
// Old handling of activities with action sheet
if ([MFMailComposeViewController canSendMail]) {
_actionsSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self
cancelButtonTitle:NSLocalizedString(@"Cancel", nil) destructiveButtonTitle:nil
otherButtonTitles:NSLocalizedString(@"Save", nil), NSLocalizedString(@"Copy", nil), NSLocalizedString(@"Email", nil), nil];
} else {
_actionsSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self
cancelButtonTitle:NSLocalizedString(@"Cancel", nil) destructiveButtonTitle:nil
otherButtonTitles:NSLocalizedString(@"Save", nil), NSLocalizedString(@"Copy", nil), nil];
}
_actionsSheet.tag = ACTION_SHEET_OLD_ACTIONS;
_actionsSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[_actionsSheet showFromBarButtonItem:sender animated:YES];
} else {
[_actionsSheet showInView:self.view];
}
} else {
// Show activity view controller
NSMutableArray *items = [NSMutableArray arrayWithObject:[photo underlyingImage]];
if (photo.caption) {
[items addObject:photo.caption];
}
self.activityViewController = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
// Show loading spinner after a couple of seconds
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if (self.activityViewController) {
[self showProgressHUDWithMessage:nil];
}
});
// Show
typeof(self) __weak weakSelf = self;
[self.activityViewController setCompletionHandler:^(NSString *activityType, BOOL completed) {
weakSelf.activityViewController = nil;
[weakSelf hideControlsAfterDelay];
[weakSelf hideProgressHUD:YES];
}];
// iOS 8 - Set the Anchor Point for the popover
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8")) {
self.activityViewController.popoverPresentationController.barButtonItem = _actionButton;
}
[self presentViewController:self.activityViewController animated:YES completion:nil];
}
}
// Keep controls hidden
[self setControlsHidden:NO animated:YES permanent:YES];
}
}
}
#pragma mark - Action Sheet Delegate
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (actionSheet.tag == ACTION_SHEET_OLD_ACTIONS) {
// Old Actions
_actionsSheet = nil;
if (buttonIndex != actionSheet.cancelButtonIndex) {
if (buttonIndex == actionSheet.firstOtherButtonIndex) {
[self savePhoto]; return;
} else if (buttonIndex == actionSheet.firstOtherButtonIndex + 1) {
[self copyPhoto]; return;
} else if (buttonIndex == actionSheet.firstOtherButtonIndex + 2) {
[self emailPhoto]; return;
}
}
}
[self hideControlsAfterDelay]; // Continue as normal...
}
#pragma mark - Action Progress
- (MBProgressHUD *)progressHUD {
if (!_progressHUD) {
_progressHUD = [[MBProgressHUD alloc] initWithView:self.view];
_progressHUD.minSize = CGSizeMake(120, 120);
_progressHUD.minShowTime = 1;
// The sample image is based on the
// work by: http://www.pixelpressicons.com
// licence: http://creativecommons.org/licenses/by/2.5/ca/
self.progressHUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MWPhotoBrowser.bundle/images/Checkmark.png"]];
[self.view addSubview:_progressHUD];
}
return _progressHUD;
}
- (void)showProgressHUDWithMessage:(NSString *)message {
self.progressHUD.labelText = message;
self.progressHUD.mode = MBProgressHUDModeIndeterminate;
[self.progressHUD show:YES];
self.navigationController.navigationBar.userInteractionEnabled = NO;
}
- (void)hideProgressHUD:(BOOL)animated {
[self.progressHUD hide:animated];
self.navigationController.navigationBar.userInteractionEnabled = YES;
}
- (void)showProgressHUDCompleteMessage:(NSString *)message {
if (message) {
if (self.progressHUD.isHidden) [self.progressHUD show:YES];
self.progressHUD.labelText = message;
self.progressHUD.mode = MBProgressHUDModeCustomView;
[self.progressHUD hide:YES afterDelay:1.5];
} else {
[self.progressHUD hide:YES];
}
self.navigationController.navigationBar.userInteractionEnabled = YES;
}
#pragma mark - Actions
- (void)savePhoto {
id photo = [self photoAtIndex:_currentPageIndex];
if ([photo underlyingImage]) {
[self showProgressHUDWithMessage:[NSString stringWithFormat:@"%@\u2026" , NSLocalizedString(@"Saving", @"Displayed with ellipsis as 'Saving...' when an item is in the process of being saved")]];
[self performSelector:@selector(actuallySavePhoto:) withObject:photo afterDelay:0];
}
}
- (void)actuallySavePhoto:(id)photo {
if ([photo underlyingImage]) {
UIImageWriteToSavedPhotosAlbum([photo underlyingImage], self,
@selector(image:didFinishSavingWithError:contextInfo:), nil);
}
}
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
[self showProgressHUDCompleteMessage: error ? NSLocalizedString(@"Failed", @"Informing the user a process has failed") : NSLocalizedString(@"Saved", @"Informing the user an item has been saved")];
[self hideControlsAfterDelay]; // Continue as normal...
}
- (void)copyPhoto {
id photo = [self photoAtIndex:_currentPageIndex];
if ([photo underlyingImage]) {
[self showProgressHUDWithMessage:[NSString stringWithFormat:@"%@\u2026" , NSLocalizedString(@"Copying", @"Displayed with ellipsis as 'Copying...' when an item is in the process of being copied")]];
[self performSelector:@selector(actuallyCopyPhoto:) withObject:photo afterDelay:0];
}
}
- (void)actuallyCopyPhoto:(id)photo {
if ([photo underlyingImage]) {
[[UIPasteboard generalPasteboard] setData:UIImagePNGRepresentation([photo underlyingImage])
forPasteboardType:@"public.png"];
[self showProgressHUDCompleteMessage:NSLocalizedString(@"Copied", @"Informing the user an item has finished copying")];
[self hideControlsAfterDelay]; // Continue as normal...
}
}
- (void)emailPhoto {
id photo = [self photoAtIndex:_currentPageIndex];
if ([photo underlyingImage]) {
[self showProgressHUDWithMessage:[NSString stringWithFormat:@"%@\u2026" , NSLocalizedString(@"Preparing", @"Displayed with ellipsis as 'Preparing...' when an item is in the process of being prepared")]];
[self performSelector:@selector(actuallyEmailPhoto:) withObject:photo afterDelay:0];
}
}
- (void)actuallyEmailPhoto:(id)photo {
if ([photo underlyingImage]) {
MFMailComposeViewController *emailer = [[MFMailComposeViewController alloc] init];
emailer.mailComposeDelegate = self;
[emailer setSubject:NSLocalizedString(@"Photo", nil)];
[emailer addAttachmentData:UIImagePNGRepresentation([photo underlyingImage]) mimeType:@"png" fileName:@"Photo.png"];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
emailer.modalPresentationStyle = UIModalPresentationPageSheet;
}
[self presentViewController:emailer animated:YES completion:nil];
[self hideProgressHUD:NO];
}
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
if (result == MFMailComposeResultFailed) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Email", nil)
message:NSLocalizedString(@"Email failed to send. Please try again.", nil)
delegate:nil cancelButtonTitle:NSLocalizedString(@"Dismiss", nil) otherButtonTitles:nil];
[alert show];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWPhotoBrowserPrivate.h.svn-base
================================================
//
// MWPhotoBrowser_Private.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 08/10/2013.
//
//
#import
#import "MBProgressHUD.h"
#import "MWGridViewController.h"
#import "MWZoomingScrollView.h"
// Declare private methods of browser
@interface MWPhotoBrowser () {
// Data
NSUInteger _photoCount;
NSMutableArray *_photos;
NSMutableArray *_thumbPhotos;
NSArray *_depreciatedPhotoData; // Depreciated
// Views
UIScrollView *_pagingScrollView;
// Paging & layout
NSMutableSet *_visiblePages, *_recycledPages;
NSUInteger _currentPageIndex;
NSUInteger _previousPageIndex;
CGRect _previousLayoutBounds;
NSUInteger _pageIndexBeforeRotation;
// Navigation & controls
UIToolbar *_toolbar;
NSTimer *_controlVisibilityTimer;
UIBarButtonItem *_previousButton, *_nextButton, *_actionButton, *_doneButton;
MBProgressHUD *_progressHUD;
UIActionSheet *_actionsSheet;
// Grid
MWGridViewController *_gridController;
UIBarButtonItem *_gridPreviousLeftNavItem;
UIBarButtonItem *_gridPreviousRightNavItem;
// Appearance
BOOL _previousNavBarHidden;
BOOL _previousNavBarTranslucent;
UIBarStyle _previousNavBarStyle;
UIStatusBarStyle _previousStatusBarStyle;
UIColor *_previousNavBarTintColor;
UIColor *_previousNavBarBarTintColor;
UIBarButtonItem *_previousViewControllerBackButton;
UIImage *_previousNavigationBarBackgroundImageDefault;
UIImage *_previousNavigationBarBackgroundImageLandscapePhone;
// Misc
BOOL _hasBelongedToViewController;
BOOL _isVCBasedStatusBarAppearance;
BOOL _statusBarShouldBeHidden;
BOOL _displayActionButton;
BOOL _leaveStatusBarAlone;
BOOL _performingLayout;
BOOL _rotating;
BOOL _viewIsActive; // active as in it's in the view heirarchy
BOOL _didSavePreviousStateOfNavBar;
BOOL _skipNextPagingScrollViewPositioning;
BOOL _viewHasAppearedInitially;
CGPoint _currentGridContentOffset;
}
// Properties
@property (nonatomic) UIActivityViewController *activityViewController;
// Layout
- (void)layoutVisiblePages;
- (void)performLayout;
- (BOOL)presentingViewControllerPrefersStatusBarHidden;
// Nav Bar Appearance
- (void)setNavBarAppearance:(BOOL)animated;
- (void)storePreviousNavBarAppearance;
- (void)restorePreviousNavBarAppearance:(BOOL)animated;
// Paging
- (void)tilePages;
- (BOOL)isDisplayingPageForIndex:(NSUInteger)index;
- (MWZoomingScrollView *)pageDisplayedAtIndex:(NSUInteger)index;
- (MWZoomingScrollView *)pageDisplayingPhoto:(id)photo;
- (MWZoomingScrollView *)dequeueRecycledPage;
- (void)configurePage:(MWZoomingScrollView *)page forIndex:(NSUInteger)index;
- (void)didStartViewingPageAtIndex:(NSUInteger)index;
// Frames
- (CGRect)frameForPagingScrollView;
- (CGRect)frameForPageAtIndex:(NSUInteger)index;
- (CGSize)contentSizeForPagingScrollView;
- (CGPoint)contentOffsetForPageAtIndex:(NSUInteger)index;
- (CGRect)frameForToolbarAtOrientation:(UIInterfaceOrientation)orientation;
- (CGRect)frameForCaptionView:(MWCaptionView *)captionView atIndex:(NSUInteger)index;
- (CGRect)frameForSelectedButton:(UIButton *)selectedButton atIndex:(NSUInteger)index;
// Navigation
- (void)updateNavigation;
- (void)jumpToPageAtIndex:(NSUInteger)index animated:(BOOL)animated;
- (void)gotoPreviousPage;
- (void)gotoNextPage;
// Grid
- (void)showGrid:(BOOL)animated;
- (void)hideGrid;
// Controls
- (void)cancelControlHiding;
- (void)hideControlsAfterDelay;
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated permanent:(BOOL)permanent;
- (void)toggleControls;
- (BOOL)areControlsHidden;
// Data
- (NSUInteger)numberOfPhotos;
- (id)photoAtIndex:(NSUInteger)index;
- (id)thumbPhotoAtIndex:(NSUInteger)index;
- (UIImage *)imageForPhoto:(id)photo;
- (BOOL)photoIsSelectedAtIndex:(NSUInteger)index;
- (void)setPhotoSelected:(BOOL)selected atIndex:(NSUInteger)index;
- (void)loadAdjacentPhotosIfNecessary:(id)photo;
- (void)releaseAllUnderlyingPhotos:(BOOL)preserveCurrent;
// Actions
- (void)savePhoto;
- (void)copyPhoto;
- (void)emailPhoto;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWPhotoProtocol.h.svn-base
================================================
//
// MWPhotoProtocol.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 02/01/2012.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import
// Notifications
#define MWPHOTO_LOADING_DID_END_NOTIFICATION @"MWPHOTO_LOADING_DID_END_NOTIFICATION"
#define MWPHOTO_PROGRESS_NOTIFICATION @"MWPHOTO_PROGRESS_NOTIFICATION"
// If you wish to use your own data models for photo then they must conform
// to this protocol. See instructions for details on each method.
// Otherwise you can use the MWPhoto object or subclass it yourself to
// store more information per photo.
//
// You can see the MWPhoto class for an example implementation of this protocol
//
@protocol MWPhoto
@required
// Return underlying UIImage to be displayed
// Return nil if the image is not immediately available (loaded into memory, preferably
// already decompressed) and needs to be loaded from a source (cache, file, web, etc)
// IMPORTANT: You should *NOT* use this method to initiate
// fetching of images from any external of source. That should be handled
// in -loadUnderlyingImageAndNotify: which may be called by the photo browser if this
// methods returns nil.
@property (nonatomic, strong) UIImage *underlyingImage;
// Called when the browser has determined the underlying images is not
// already loaded into memory but needs it.
- (void)loadUnderlyingImageAndNotify;
// Fetch the image data from a source and notify when complete.
// You must load the image asyncronously (and decompress it for better performance).
// It is recommended that you use SDWebImageDecoder to perform the decompression.
// See MWPhoto object for an example implementation.
// When the underlying UIImage is loaded (or failed to load) you should post the following
// notification:
// [[NSNotificationCenter defaultCenter] postNotificationName:MWPHOTO_LOADING_DID_END_NOTIFICATION
// object:self];
- (void)performLoadUnderlyingImageAndNotify;
// This is called when the photo browser has determined the photo data
// is no longer needed or there are low memory conditions
// You should release any underlying (possibly large and decompressed) image data
// as long as the image can be re-loaded (from cache, file, or URL)
- (void)unloadUnderlyingImage;
@optional
// Return a caption string to be displayed over the image
// Return nil to display no caption
- (NSString *)caption;
// Cancel any background loading of image data
- (void)cancelAnyLoading;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWTapDetectingImageView.h.svn-base
================================================
//
// UIImageViewTap.h
// Momento
//
// Created by Michael Waterfall on 04/11/2009.
// Copyright 2009 d3i. All rights reserved.
//
#import
@protocol MWTapDetectingImageViewDelegate;
@interface MWTapDetectingImageView : UIImageView {}
@property (nonatomic, weak) id tapDelegate;
@end
@protocol MWTapDetectingImageViewDelegate
@optional
- (void)imageView:(UIImageView *)imageView singleTapDetected:(UITouch *)touch;
- (void)imageView:(UIImageView *)imageView doubleTapDetected:(UITouch *)touch;
- (void)imageView:(UIImageView *)imageView tripleTapDetected:(UITouch *)touch;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWTapDetectingImageView.m.svn-base
================================================
//
// UIImageViewTap.m
// Momento
//
// Created by Michael Waterfall on 04/11/2009.
// Copyright 2009 d3i. All rights reserved.
//
#import "MWTapDetectingImageView.h"
@implementation MWTapDetectingImageView
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.userInteractionEnabled = YES;
}
return self;
}
- (id)initWithImage:(UIImage *)image {
if ((self = [super initWithImage:image])) {
self.userInteractionEnabled = YES;
}
return self;
}
- (id)initWithImage:(UIImage *)image highlightedImage:(UIImage *)highlightedImage {
if ((self = [super initWithImage:image highlightedImage:highlightedImage])) {
self.userInteractionEnabled = YES;
}
return self;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSUInteger tapCount = touch.tapCount;
switch (tapCount) {
case 1:
[self handleSingleTap:touch];
break;
case 2:
[self handleDoubleTap:touch];
break;
case 3:
[self handleTripleTap:touch];
break;
default:
break;
}
[[self nextResponder] touchesEnded:touches withEvent:event];
}
- (void)handleSingleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(imageView:singleTapDetected:)])
[_tapDelegate imageView:self singleTapDetected:touch];
}
- (void)handleDoubleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(imageView:doubleTapDetected:)])
[_tapDelegate imageView:self doubleTapDetected:touch];
}
- (void)handleTripleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(imageView:tripleTapDetected:)])
[_tapDelegate imageView:self tripleTapDetected:touch];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWTapDetectingView.h.svn-base
================================================
//
// UIViewTap.h
// Momento
//
// Created by Michael Waterfall on 04/11/2009.
// Copyright 2009 d3i. All rights reserved.
//
#import
@protocol MWTapDetectingViewDelegate;
@interface MWTapDetectingView : UIView {}
@property (nonatomic, weak) id tapDelegate;
@end
@protocol MWTapDetectingViewDelegate
@optional
- (void)view:(UIView *)view singleTapDetected:(UITouch *)touch;
- (void)view:(UIView *)view doubleTapDetected:(UITouch *)touch;
- (void)view:(UIView *)view tripleTapDetected:(UITouch *)touch;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWTapDetectingView.m.svn-base
================================================
//
// UIViewTap.m
// Momento
//
// Created by Michael Waterfall on 04/11/2009.
// Copyright 2009 d3i. All rights reserved.
//
#import "MWTapDetectingView.h"
@implementation MWTapDetectingView
- (id)init {
if ((self = [super init])) {
self.userInteractionEnabled = YES;
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.userInteractionEnabled = YES;
}
return self;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSUInteger tapCount = touch.tapCount;
switch (tapCount) {
case 1:
[self handleSingleTap:touch];
break;
case 2:
[self handleDoubleTap:touch];
break;
case 3:
[self handleTripleTap:touch];
break;
default:
break;
}
[[self nextResponder] touchesEnded:touches withEvent:event];
}
- (void)handleSingleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(view:singleTapDetected:)])
[_tapDelegate view:self singleTapDetected:touch];
}
- (void)handleDoubleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(view:doubleTapDetected:)])
[_tapDelegate view:self doubleTapDetected:touch];
}
- (void)handleTripleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(view:tripleTapDetected:)])
[_tapDelegate view:self tripleTapDetected:touch];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWZoomingScrollView.h.svn-base
================================================
//
// ZoomingScrollView.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 14/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import
#import "MWPhotoProtocol.h"
#import "MWTapDetectingImageView.h"
#import "MWTapDetectingView.h"
@class MWPhotoBrowser, MWPhoto, MWCaptionView;
@interface MWZoomingScrollView : UIScrollView {
}
@property () NSUInteger index;
@property (nonatomic) id photo;
@property (nonatomic, weak) MWCaptionView *captionView;
@property (nonatomic, weak) UIButton *selectedButton;
- (id)initWithPhotoBrowser:(MWPhotoBrowser *)browser;
- (void)displayImage;
- (void)displayImageFailure;
- (void)setMaxMinZoomScalesForCurrentBounds;
- (void)prepareForReuse;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/.svn/text-base/MWZoomingScrollView.m.svn-base
================================================
//
// ZoomingScrollView.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 14/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import "MWCommon.h"
#import "MWZoomingScrollView.h"
#import "MWPhotoBrowser.h"
#import "MWPhoto.h"
#import "DACircularProgressView.h"
#import "MWPhotoBrowserPrivate.h"
// Private methods and properties
@interface MWZoomingScrollView () {
MWPhotoBrowser __weak *_photoBrowser;
MWTapDetectingView *_tapView; // for background taps
MWTapDetectingImageView *_photoImageView;
DACircularProgressView *_loadingIndicator;
UIImageView *_loadingError;
}
@end
@implementation MWZoomingScrollView
- (id)initWithPhotoBrowser:(MWPhotoBrowser *)browser {
if ((self = [super init])) {
// Setup
_index = NSUIntegerMax;
_photoBrowser = browser;
// Tap view for background
_tapView = [[MWTapDetectingView alloc] initWithFrame:self.bounds];
_tapView.tapDelegate = self;
_tapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_tapView.backgroundColor = [UIColor blackColor];
[self addSubview:_tapView];
// Image view
_photoImageView = [[MWTapDetectingImageView alloc] initWithFrame:CGRectZero];
_photoImageView.tapDelegate = self;
_photoImageView.contentMode = UIViewContentModeCenter;
_photoImageView.backgroundColor = [UIColor blackColor];
[self addSubview:_photoImageView];
// Loading indicator
_loadingIndicator = [[DACircularProgressView alloc] initWithFrame:CGRectMake(140.0f, 30.0f, 40.0f, 40.0f)];
_loadingIndicator.userInteractionEnabled = NO;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7")) {
_loadingIndicator.thicknessRatio = 0.1;
_loadingIndicator.roundedCorners = NO;
} else {
_loadingIndicator.thicknessRatio = 0.2;
_loadingIndicator.roundedCorners = YES;
}
_loadingIndicator.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
[self addSubview:_loadingIndicator];
// Listen progress notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(setProgressFromNotification:)
name:MWPHOTO_PROGRESS_NOTIFICATION
object:nil];
// Setup
self.backgroundColor = [UIColor blackColor];
self.delegate = self;
self.showsHorizontalScrollIndicator = NO;
self.showsVerticalScrollIndicator = NO;
self.decelerationRate = UIScrollViewDecelerationRateFast;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)prepareForReuse {
[self hideImageFailure];
self.photo = nil;
self.captionView = nil;
self.selectedButton = nil;
_photoImageView.image = nil;
_index = NSUIntegerMax;
}
#pragma mark - Image
- (void)setPhoto:(id)photo {
// Cancel any loading on old photo
if (_photo && photo == nil) {
if ([_photo respondsToSelector:@selector(cancelAnyLoading)]) {
[_photo cancelAnyLoading];
}
}
_photo = photo;
UIImage *img = [_photoBrowser imageForPhoto:_photo];
if (img) {
[self displayImage];
} else {
// Will be loading so show loading
[self showLoadingIndicator];
}
}
// Get and display image
- (void)displayImage {
if (_photo && _photoImageView.image == nil) {
// Reset
self.maximumZoomScale = 1;
self.minimumZoomScale = 1;
self.zoomScale = 1;
self.contentSize = CGSizeMake(0, 0);
// Get image from browser as it handles ordering of fetching
UIImage *img = [_photoBrowser imageForPhoto:_photo];
if (img) {
// Hide indicator
[self hideLoadingIndicator];
// Set image
_photoImageView.image = img;
_photoImageView.hidden = NO;
// Setup photo frame
CGRect photoImageViewFrame;
photoImageViewFrame.origin = CGPointZero;
photoImageViewFrame.size = img.size;
_photoImageView.frame = photoImageViewFrame;
self.contentSize = photoImageViewFrame.size;
// Set zoom to minimum zoom
[self setMaxMinZoomScalesForCurrentBounds];
} else {
// Failed no image
[self displayImageFailure];
}
[self setNeedsLayout];
}
}
// Image failed so just show black!
- (void)displayImageFailure {
[self hideLoadingIndicator];
_photoImageView.image = nil;
if (!_loadingError) {
_loadingError = [UIImageView new];
_loadingError.image = [UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageError.png"];
_loadingError.userInteractionEnabled = NO;
_loadingError.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
[_loadingError sizeToFit];
[self addSubview:_loadingError];
}
_loadingError.frame = CGRectMake(floorf((self.bounds.size.width - _loadingError.frame.size.width) / 2.),
floorf((self.bounds.size.height - _loadingError.frame.size.height) / 2),
_loadingError.frame.size.width,
_loadingError.frame.size.height);
}
- (void)hideImageFailure {
if (_loadingError) {
[_loadingError removeFromSuperview];
_loadingError = nil;
}
}
#pragma mark - Loading Progress
- (void)setProgressFromNotification:(NSNotification *)notification {
NSDictionary *dict = [notification object];
id photoWithProgress = [dict objectForKey:@"photo"];
if (photoWithProgress == self.photo) {
float progress = [[dict valueForKey:@"progress"] floatValue];
_loadingIndicator.progress = MAX(MIN(1, progress), 0);
}
}
- (void)hideLoadingIndicator {
_loadingIndicator.hidden = YES;
}
- (void)showLoadingIndicator {
self.zoomScale = 0;
self.minimumZoomScale = 0;
self.maximumZoomScale = 0;
_loadingIndicator.progress = 0;
_loadingIndicator.hidden = NO;
[self hideImageFailure];
}
#pragma mark - Setup
- (CGFloat)initialZoomScaleWithMinScale {
CGFloat zoomScale = self.minimumZoomScale;
if (_photoImageView && _photoBrowser.zoomPhotosToFill) {
// Zoom image to fill if the aspect ratios are fairly similar
CGSize boundsSize = self.bounds.size;
CGSize imageSize = _photoImageView.image.size;
CGFloat boundsAR = boundsSize.width / boundsSize.height;
CGFloat imageAR = imageSize.width / imageSize.height;
CGFloat xScale = boundsSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
CGFloat yScale = boundsSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
// Zooms standard portrait images on a 3.5in screen but not on a 4in screen.
if (ABS(boundsAR - imageAR) < 0.17) {
zoomScale = MAX(xScale, yScale);
// Ensure we don't zoom in or out too far, just in case
zoomScale = MIN(MAX(self.minimumZoomScale, zoomScale), self.maximumZoomScale);
}
}
return zoomScale;
}
- (void)setMaxMinZoomScalesForCurrentBounds {
// Reset
self.maximumZoomScale = 1;
self.minimumZoomScale = 1;
self.zoomScale = 1;
// Bail if no image
if (_photoImageView.image == nil) return;
// Reset position
_photoImageView.frame = CGRectMake(0, 0, _photoImageView.frame.size.width, _photoImageView.frame.size.height);
// Sizes
CGSize boundsSize = self.bounds.size;
CGSize imageSize = _photoImageView.image.size;
// Calculate Min
CGFloat xScale = boundsSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
CGFloat yScale = boundsSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
CGFloat minScale = MIN(xScale, yScale); // use minimum of these to allow the image to become fully visible
// Calculate Max
CGFloat maxScale = 3;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// Let them go a bit bigger on a bigger screen!
maxScale = 4;
}
// Image is smaller than screen so no zooming!
if (xScale >= 1 && yScale >= 1) {
minScale = 1.0;
}
// Set min/max zoom
self.maximumZoomScale = maxScale;
self.minimumZoomScale = minScale;
// Initial zoom
self.zoomScale = [self initialZoomScaleWithMinScale];
// If we're zooming to fill then centralise
if (self.zoomScale != minScale) {
// Centralise
self.contentOffset = CGPointMake((imageSize.width * self.zoomScale - boundsSize.width) / 2.0,
(imageSize.height * self.zoomScale - boundsSize.height) / 2.0);
// Disable scrolling initially until the first pinch to fix issues with swiping on an initally zoomed in photo
self.scrollEnabled = NO;
}
// Layout
[self setNeedsLayout];
}
#pragma mark - Layout
- (void)layoutSubviews {
// Update tap view frame
_tapView.frame = self.bounds;
// Position indicators (centre does not seem to work!)
if (!_loadingIndicator.hidden)
_loadingIndicator.frame = CGRectMake(floorf((self.bounds.size.width - _loadingIndicator.frame.size.width) / 2.),
floorf((self.bounds.size.height - _loadingIndicator.frame.size.height) / 2),
_loadingIndicator.frame.size.width,
_loadingIndicator.frame.size.height);
if (_loadingError)
_loadingError.frame = CGRectMake(floorf((self.bounds.size.width - _loadingError.frame.size.width) / 2.),
floorf((self.bounds.size.height - _loadingError.frame.size.height) / 2),
_loadingError.frame.size.width,
_loadingError.frame.size.height);
// Super
[super layoutSubviews];
// Center the image as it becomes smaller than the size of the screen
CGSize boundsSize = self.bounds.size;
CGRect frameToCenter = _photoImageView.frame;
// Horizontally
if (frameToCenter.size.width < boundsSize.width) {
frameToCenter.origin.x = floorf((boundsSize.width - frameToCenter.size.width) / 2.0);
} else {
frameToCenter.origin.x = 0;
}
// Vertically
if (frameToCenter.size.height < boundsSize.height) {
frameToCenter.origin.y = floorf((boundsSize.height - frameToCenter.size.height) / 2.0);
} else {
frameToCenter.origin.y = 0;
}
// Center
if (!CGRectEqualToRect(_photoImageView.frame, frameToCenter))
_photoImageView.frame = frameToCenter;
}
#pragma mark - UIScrollViewDelegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _photoImageView;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[_photoBrowser cancelControlHiding];
}
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
self.scrollEnabled = YES; // reset
[_photoBrowser cancelControlHiding];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
[_photoBrowser hideControlsAfterDelay];
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
[self setNeedsLayout];
[self layoutIfNeeded];
}
#pragma mark - Tap Detection
- (void)handleSingleTap:(CGPoint)touchPoint {
[_photoBrowser performSelector:@selector(toggleControls) withObject:nil afterDelay:0.2];
}
- (void)handleDoubleTap:(CGPoint)touchPoint {
// Cancel any single tap handling
[NSObject cancelPreviousPerformRequestsWithTarget:_photoBrowser];
// Zoom
if (self.zoomScale != self.minimumZoomScale && self.zoomScale != [self initialZoomScaleWithMinScale]) {
// Zoom out
[self setZoomScale:self.minimumZoomScale animated:YES];
} else {
// Zoom in to twice the size
CGFloat newZoomScale = ((self.maximumZoomScale + self.minimumZoomScale) / 2);
CGFloat xsize = self.bounds.size.width / newZoomScale;
CGFloat ysize = self.bounds.size.height / newZoomScale;
[self zoomToRect:CGRectMake(touchPoint.x - xsize/2, touchPoint.y - ysize/2, xsize, ysize) animated:YES];
}
// Delay controls
[_photoBrowser hideControlsAfterDelay];
}
// Image View
- (void)imageView:(UIImageView *)imageView singleTapDetected:(UITouch *)touch {
[self handleSingleTap:[touch locationInView:imageView]];
}
- (void)imageView:(UIImageView *)imageView doubleTapDetected:(UITouch *)touch {
[self handleDoubleTap:[touch locationInView:imageView]];
}
// Background View
- (void)view:(UIView *)view singleTapDetected:(UITouch *)touch {
// Translate touch location to image view location
CGFloat touchX = [touch locationInView:view].x;
CGFloat touchY = [touch locationInView:view].y;
touchX *= 1/self.zoomScale;
touchY *= 1/self.zoomScale;
touchX += self.contentOffset.x;
touchY += self.contentOffset.y;
[self handleSingleTap:CGPointMake(touchX, touchY)];
}
- (void)view:(UIView *)view doubleTapDetected:(UITouch *)touch {
// Translate touch location to image view location
CGFloat touchX = [touch locationInView:view].x;
CGFloat touchY = [touch locationInView:view].y;
touchX *= 1/self.zoomScale;
touchY *= 1/self.zoomScale;
touchX += self.contentOffset.x;
touchY += self.contentOffset.y;
[self handleDoubleTap:CGPointMake(touchX, touchY)];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWCaptionView.h
================================================
//
// MWCaptionView.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 30/12/2011.
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//
#import
#import "MWPhotoProtocol.h"
@interface MWCaptionView : UIToolbar
// Init
- (id)initWithPhoto:(id)photo;
// To create your own custom caption view, subclass this view
// and override the following two methods (as well as any other
// UIView methods that you see fit):
// Override -setupCaption so setup your subviews and customise the appearance
// of your custom caption
// You can access the photo's data by accessing the _photo ivar
// If you need more data per photo then simply subclass MWPhoto and return your
// subclass to the photo browsers -photoBrowser:photoAtIndex: delegate method
- (void)setupCaption;
// Override -sizeThatFits: and return a CGSize specifying the height of your
// custom caption view. With width property is ignored and the caption is displayed
// the full width of the screen
- (CGSize)sizeThatFits:(CGSize)size;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWCaptionView.m
================================================
//
// MWCaptionView.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 30/12/2011.
// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//
#import "MWCommon.h"
#import "MWCaptionView.h"
#import "MWPhoto.h"
static const CGFloat labelPadding = 10;
// Private
@interface MWCaptionView () {
id _photo;
UILabel *_label;
}
@end
@implementation MWCaptionView
- (id)initWithPhoto:(id)photo {
self = [super initWithFrame:CGRectMake(0, 0, 320, 44)]; // Random initial frame
if (self) {
self.userInteractionEnabled = NO;
_photo = photo;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7")) {
// Use iOS 7 blurry goodness
self.barStyle = UIBarStyleBlackTranslucent;
self.tintColor = nil;
self.barTintColor = nil;
self.barStyle = UIBarStyleBlackTranslucent;
[self setBackgroundImage:nil forToolbarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault];
} else {
// Transparent black with no gloss
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [[UIColor colorWithWhite:0 alpha:0.6] CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setBackgroundImage:image forToolbarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault];
}
self.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin;
[self setupCaption];
}
return self;
}
- (CGSize)sizeThatFits:(CGSize)size {
CGFloat maxHeight = 9999;
if (_label.numberOfLines > 0) maxHeight = _label.font.leading*_label.numberOfLines;
CGSize textSize;
if ([NSString instancesRespondToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) {
textSize = [_label.text boundingRectWithSize:CGSizeMake(size.width - labelPadding*2, maxHeight)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:_label.font}
context:nil].size;
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
textSize = [_label.text sizeWithFont:_label.font
constrainedToSize:CGSizeMake(size.width - labelPadding*2, maxHeight)
lineBreakMode:_label.lineBreakMode];
#pragma clang diagnostic pop
}
return CGSizeMake(size.width, textSize.height + labelPadding * 2);
}
- (void)setupCaption {
_label = [[UILabel alloc] initWithFrame:CGRectIntegral(CGRectMake(labelPadding, 0,
self.bounds.size.width-labelPadding*2,
self.bounds.size.height))];
_label.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
_label.opaque = NO;
_label.backgroundColor = [UIColor clearColor];
if (SYSTEM_VERSION_LESS_THAN(@"6")) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
_label.textAlignment = UITextAlignmentCenter;
_label.lineBreakMode = UILineBreakModeWordWrap;
#pragma clang diagnostic pop
} else {
_label.textAlignment = NSTextAlignmentCenter;
_label.lineBreakMode = NSLineBreakByWordWrapping;
}
_label.numberOfLines = 0;
_label.textColor = [UIColor whiteColor];
if (SYSTEM_VERSION_LESS_THAN(@"7")) {
// Shadow on 6 and below
_label.shadowColor = [UIColor blackColor];
_label.shadowOffset = CGSizeMake(1, 1);
}
_label.font = [UIFont systemFontOfSize:17];
if ([_photo respondsToSelector:@selector(caption)]) {
_label.text = [_photo caption] ? [_photo caption] : @" ";
}
[self addSubview:_label];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWCommon.h
================================================
//
// MWPreprocessor.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 01/10/2013.
//
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWGridCell.h
================================================
//
// MWGridCell.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 08/10/2013.
//
//
#import
#import "MWPhoto.h"
#import "MWGridViewController.h"
#import "PSTCollectionView.h"
@interface MWGridCell : PSTCollectionViewCell {}
@property (nonatomic, weak) MWGridViewController *gridController;
@property (nonatomic) NSUInteger index;
@property (nonatomic) id photo;
@property (nonatomic) BOOL selectionMode;
@property (nonatomic) BOOL isSelected;
- (void)displayImage;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWGridCell.m
================================================
//
// MWGridCell.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 08/10/2013.
//
//
#import "MWGridCell.h"
#import "MWCommon.h"
#import "MWPhotoBrowserPrivate.h"
#import "DACircularProgressView.h"
@interface MWGridCell () {
UIImageView *_imageView;
UIImageView *_loadingError;
DACircularProgressView *_loadingIndicator;
UIButton *_selectedButton;
}
@end
@implementation MWGridCell
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Grey background
self.backgroundColor = [UIColor colorWithWhite:0.12 alpha:1];
// Image
_imageView = [UIImageView new];
_imageView.frame = self.bounds;
_imageView.contentMode = UIViewContentModeScaleAspectFill;
_imageView.clipsToBounds = YES;
_imageView.autoresizesSubviews = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self addSubview:_imageView];
// Selection button
_selectedButton = [UIButton buttonWithType:UIButtonTypeCustom];
_selectedButton.contentMode = UIViewContentModeTopRight;
_selectedButton.adjustsImageWhenHighlighted = NO;
[_selectedButton setImage:nil forState:UIControlStateNormal];
[_selectedButton setImage:[UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageSelectedSmallOff.png"] forState:UIControlStateNormal];
[_selectedButton setImage:[UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageSelectedSmallOn.png"] forState:UIControlStateSelected];
[_selectedButton addTarget:self action:@selector(selectionButtonPressed) forControlEvents:UIControlEventTouchDown];
_selectedButton.hidden = YES;
_selectedButton.frame = CGRectMake(0, 0, 44, 44);
[self addSubview:_selectedButton];
// Loading indicator
_loadingIndicator = [[DACircularProgressView alloc] initWithFrame:CGRectMake(0, 0, 40.0f, 40.0f)];
_loadingIndicator.userInteractionEnabled = NO;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7")) {
_loadingIndicator.thicknessRatio = 0.1;
_loadingIndicator.roundedCorners = NO;
} else {
_loadingIndicator.thicknessRatio = 0.2;
_loadingIndicator.roundedCorners = YES;
}
[self addSubview:_loadingIndicator];
// Listen for photo loading notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(setProgressFromNotification:)
name:MWPHOTO_PROGRESS_NOTIFICATION
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleMWPhotoLoadingDidEndNotification:)
name:MWPHOTO_LOADING_DID_END_NOTIFICATION
object:nil];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - View
- (void)layoutSubviews {
[super layoutSubviews];
_imageView.frame = self.bounds;
_loadingIndicator.frame = CGRectMake(floorf((self.bounds.size.width - _loadingIndicator.frame.size.width) / 2.),
floorf((self.bounds.size.height - _loadingIndicator.frame.size.height) / 2),
_loadingIndicator.frame.size.width,
_loadingIndicator.frame.size.height);
_selectedButton.frame = CGRectMake(self.bounds.size.width - _selectedButton.frame.size.width - 0,
0, _selectedButton.frame.size.width, _selectedButton.frame.size.height);
}
#pragma mark - Cell
- (void)prepareForReuse {
_photo = nil;
_gridController = nil;
_imageView.image = nil;
_loadingIndicator.progress = 0;
_selectedButton.hidden = YES;
[self hideImageFailure];
[super prepareForReuse];
}
#pragma mark - Image Handling
- (void)setPhoto:(id )photo {
_photo = photo;
if (_photo) {
if (![_photo underlyingImage]) {
[self showLoadingIndicator];
} else {
[self hideLoadingIndicator];
}
} else {
[self showImageFailure];
}
}
- (void)displayImage {
_imageView.image = [_photo underlyingImage];
_selectedButton.hidden = !_selectionMode;
[self hideImageFailure];
}
#pragma mark - Selection
- (void)setSelectionMode:(BOOL)selectionMode {
_selectionMode = selectionMode;
}
- (void)setIsSelected:(BOOL)isSelected {
_isSelected = isSelected;
_selectedButton.selected = isSelected;
}
- (void)selectionButtonPressed {
_selectedButton.selected = !_selectedButton.selected;
[_gridController.browser setPhotoSelected:_selectedButton.selected atIndex:_index];
}
#pragma mark - Touches
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
_imageView.alpha = 0.6;
[super touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
_imageView.alpha = 1;
[super touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
_imageView.alpha = 1;
[super touchesCancelled:touches withEvent:event];
}
#pragma mark Indicators
- (void)hideLoadingIndicator {
_loadingIndicator.hidden = YES;
}
- (void)showLoadingIndicator {
_loadingIndicator.progress = 0;
_loadingIndicator.hidden = NO;
[self hideImageFailure];
}
- (void)showImageFailure {
if (!_loadingError) {
_loadingError = [UIImageView new];
_loadingError.image = [UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageError.png"];
_loadingError.userInteractionEnabled = NO;
[_loadingError sizeToFit];
[self addSubview:_loadingError];
}
[self hideLoadingIndicator];
_imageView.image = nil;
_loadingError.frame = CGRectMake(floorf((self.bounds.size.width - _loadingError.frame.size.width) / 2.),
floorf((self.bounds.size.height - _loadingError.frame.size.height) / 2),
_loadingError.frame.size.width,
_loadingError.frame.size.height);
}
- (void)hideImageFailure {
if (_loadingError) {
[_loadingError removeFromSuperview];
_loadingError = nil;
}
}
#pragma mark - Notifications
- (void)setProgressFromNotification:(NSNotification *)notification {
NSDictionary *dict = [notification object];
id photoWithProgress = [dict objectForKey:@"photo"];
if (photoWithProgress == _photo) {
// NSLog(@"%f", [[dict valueForKey:@"progress"] floatValue]);
float progress = [[dict valueForKey:@"progress"] floatValue];
_loadingIndicator.progress = MAX(MIN(1, progress), 0);
}
}
- (void)handleMWPhotoLoadingDidEndNotification:(NSNotification *)notification {
id photo = [notification object];
if (photo == _photo) {
if ([photo underlyingImage]) {
// Successful load
[self displayImage];
} else {
// Failed to load
[self showImageFailure];
}
[self hideLoadingIndicator];
}
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWGridViewController.h
================================================
//
// MWGridViewController.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 08/10/2013.
//
//
#import
#import "MWPhotoBrowser.h"
#import "PSTCollectionView.h"
@interface MWGridViewController : PSTCollectionViewController {}
@property (nonatomic, assign) MWPhotoBrowser *browser;
@property (nonatomic) BOOL selectionMode;
@property (nonatomic) CGPoint initialContentOffset;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWGridViewController.m
================================================
//
// MWGridViewController.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 08/10/2013.
//
//
#import "MWGridViewController.h"
#import "MWGridCell.h"
#import "MWPhotoBrowserPrivate.h"
#import "MWCommon.h"
@interface MWGridViewController () {
// Store margins for current setup
CGFloat _margin, _gutter, _marginL, _gutterL, _columns, _columnsL;
}
@end
@implementation MWGridViewController
- (id)init {
if ((self = [super initWithCollectionViewLayout:[PSTCollectionViewFlowLayout new]])) {
// Defaults
_columns = 3, _columnsL = 4;
_margin = 0, _gutter = 1;
_marginL = 0, _gutterL = 1;
// For pixel perfection...
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// iPad
_columns = 6, _columnsL = 8;
_margin = 1, _gutter = 2;
_marginL = 1, _gutterL = 2;
} else if ([UIScreen mainScreen].bounds.size.height == 480) {
// iPhone 3.5 inch
_columns = 3, _columnsL = 4;
_margin = 0, _gutter = 1;
_marginL = 1, _gutterL = 2;
} else {
// iPhone 4 inch
_columns = 3, _columnsL = 5;
_margin = 0, _gutter = 1;
_marginL = 0, _gutterL = 2;
}
_initialContentOffset = CGPointMake(0, CGFLOAT_MAX);
}
return self;
}
#pragma mark - View
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView registerClass:[MWGridCell class] forCellWithReuseIdentifier:@"GridCell"];
self.collectionView.alwaysBounceVertical = YES;
self.collectionView.backgroundColor = [UIColor blackColor];
}
- (void)viewWillDisappear:(BOOL)animated {
// Cancel outstanding loading
NSArray *visibleCells = [self.collectionView visibleCells];
if (visibleCells) {
for (MWGridCell *cell in visibleCells) {
[cell.photo cancelAnyLoading];
}
}
[super viewWillDisappear:animated];
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[self performLayout];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// Move to previous content offset
if (_initialContentOffset.y != CGFLOAT_MAX) {
self.collectionView.contentOffset = _initialContentOffset;
}
CGPoint currentContentOffset = self.collectionView.contentOffset;
// Get scroll position to have the current photo on screen
if (_browser.numberOfPhotos > 0) {
NSIndexPath *currentPhotoIndexPath = [NSIndexPath indexPathForItem:_browser.currentIndex inSection:0];
[self.collectionView scrollToItemAtIndexPath:currentPhotoIndexPath atScrollPosition:PSTCollectionViewScrollPositionNone animated:NO];
}
CGPoint offsetToShowCurrent = self.collectionView.contentOffset;
// Only commit to using the scrolled position if it differs from the initial content offset
if (!CGPointEqualToPoint(offsetToShowCurrent, currentContentOffset)) {
// Use offset to show current
self.collectionView.contentOffset = offsetToShowCurrent;
} else {
// Stick with initial
self.collectionView.contentOffset = currentContentOffset;
}
}
- (void)performLayout {
UINavigationBar *navBar = self.navigationController.navigationBar;
CGFloat yAdjust = 0;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7") && !self.browser.wantsFullScreenLayout) yAdjust = -20;
#endif
self.collectionView.contentInset = UIEdgeInsetsMake(navBar.frame.origin.y + navBar.frame.size.height + [self getGutter] + yAdjust, 0, 0, 0);
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self.collectionView reloadData];
[self performLayout]; // needed for iOS 5 & 6
}
#pragma mark - Layout
- (CGFloat)getColumns {
if ((UIInterfaceOrientationIsPortrait(self.interfaceOrientation))) {
return _columns;
} else {
return _columnsL;
}
}
- (CGFloat)getMargin {
if ((UIInterfaceOrientationIsPortrait(self.interfaceOrientation))) {
return _margin;
} else {
return _marginL;
}
}
- (CGFloat)getGutter {
if ((UIInterfaceOrientationIsPortrait(self.interfaceOrientation))) {
return _gutter;
} else {
return _gutterL;
}
}
#pragma mark - Collection View
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {
return [_browser numberOfPhotos];
}
- (PSTCollectionViewCell *)collectionView:(PSTCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MWGridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"GridCell" forIndexPath:indexPath];
if (!cell) {
cell = [[MWGridCell alloc] init];
}
id photo = [_browser thumbPhotoAtIndex:indexPath.row];
cell.photo = photo;
cell.gridController = self;
cell.selectionMode = _selectionMode;
cell.isSelected = [_browser photoIsSelectedAtIndex:indexPath.row];
cell.index = indexPath.row;
UIImage *img = [_browser imageForPhoto:photo];
if (img) {
[cell displayImage];
} else {
[photo loadUnderlyingImageAndNotify];
}
return cell;
}
- (void)collectionView:(PSTCollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[_browser setCurrentPhotoIndex:indexPath.row];
[_browser hideGrid];
}
- (void)collectionView:(PSTCollectionView *)collectionView didEndDisplayingCell:(PSTCollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
[((MWGridCell *)cell).photo cancelAnyLoading];
}
- (CGSize)collectionView:(PSTCollectionView *)collectionView layout:(PSTCollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat margin = [self getMargin];
CGFloat gutter = [self getGutter];
CGFloat columns = [self getColumns];
CGFloat value = floorf(((self.view.bounds.size.width - (columns - 1) * gutter - 2 * margin) / columns));
return CGSizeMake(value, value);
}
- (CGFloat)collectionView:(PSTCollectionView *)collectionView layout:(PSTCollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
return [self getGutter];
}
- (CGFloat)collectionView:(PSTCollectionView *)collectionView layout:(PSTCollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
return [self getGutter];
}
- (UIEdgeInsets)collectionView:(PSTCollectionView *)collectionView layout:(PSTCollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
CGFloat margin = [self getMargin];
return UIEdgeInsetsMake(margin, margin, margin, margin);
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWPhoto.h
================================================
//
// MWPhoto.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 17/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import
#import "MWPhotoProtocol.h"
// This class models a photo/image and it's caption
// If you want to handle photos, caching, decompression
// yourself then you can simply ensure your custom data model
// conforms to MWPhotoProtocol
@interface MWPhoto : NSObject
@property (nonatomic, strong) NSString *caption;
@property (nonatomic, readonly) UIImage *image;
@property (nonatomic, readonly) NSURL *photoURL;
@property (nonatomic, readonly) NSString *filePath __attribute__((deprecated("Use photoURL"))); // Depreciated
+ (MWPhoto *)photoWithImage:(UIImage *)image;
+ (MWPhoto *)photoWithFilePath:(NSString *)path __attribute__((deprecated("Use photoWithURL: with a file URL"))); // Depreciated
+ (MWPhoto *)photoWithURL:(NSURL *)url;
- (id)initWithImage:(UIImage *)image;
- (id)initWithURL:(NSURL *)url;
- (id)initWithFilePath:(NSString *)path __attribute__((deprecated("Use initWithURL: with a file URL"))); // Depreciated
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWPhoto.m
================================================
//
// MWPhoto.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 17/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import "MWPhoto.h"
#import "MWPhotoBrowser.h"
#import "EMSDWebImageDecoder.h"
#import "EMSDWebImageManager.h"
#import "EMSDWebImageOperation.h"
#import
@interface MWPhoto () {
BOOL _loadingInProgress;
id _webImageOperation;
}
- (void)imageLoadingComplete;
@end
@implementation MWPhoto
@synthesize underlyingImage = _underlyingImage; // synth property from protocol
#pragma mark - Class Methods
+ (MWPhoto *)photoWithImage:(UIImage *)image {
return [[MWPhoto alloc] initWithImage:image];
}
// Deprecated
+ (MWPhoto *)photoWithFilePath:(NSString *)path {
return [MWPhoto photoWithURL:[NSURL fileURLWithPath:path]];
}
+ (MWPhoto *)photoWithURL:(NSURL *)url {
return [[MWPhoto alloc] initWithURL:url];
}
#pragma mark - Init
- (id)initWithImage:(UIImage *)image {
if ((self = [super init])) {
_image = image;
}
return self;
}
// Deprecated
- (id)initWithFilePath:(NSString *)path {
if ((self = [super init])) {
_photoURL = [NSURL fileURLWithPath:path];
}
return self;
}
- (id)initWithURL:(NSURL *)url {
if ((self = [super init])) {
_photoURL = [url copy];
}
return self;
}
#pragma mark - MWPhoto Protocol Methods
- (UIImage *)underlyingImage {
return _underlyingImage;
}
- (void)loadUnderlyingImageAndNotify {
// NSAssert([[NSThread currentThread] isMainThread], @"This method must be called on the main thread.");
if (_loadingInProgress) return;
_loadingInProgress = YES;
@try {
if (self.underlyingImage) {
[self imageLoadingComplete];
} else {
[self performLoadUnderlyingImageAndNotify];
}
}
@catch (NSException *exception) {
self.underlyingImage = nil;
_loadingInProgress = NO;
[self imageLoadingComplete];
}
@finally {
}
}
// Set the underlyingImage
- (void)performLoadUnderlyingImageAndNotify {
// Get underlying image
if (_image) {
// We have UIImage!
self.underlyingImage = _image;
[self imageLoadingComplete];
} else if (_photoURL) {
// Check what type of url it is
if ([[[_photoURL scheme] lowercaseString] isEqualToString:@"assets-library"]) {
// Load from asset library async
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@autoreleasepool {
@try {
ALAssetsLibrary *assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:self->_photoURL
resultBlock:^(ALAsset *asset){
ALAssetRepresentation *rep = [asset defaultRepresentation];
CGImageRef iref = [rep fullScreenImage];
if (iref) {
self.underlyingImage = [UIImage imageWithCGImage:iref];
}
[self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
}
failureBlock:^(NSError *error) {
self.underlyingImage = nil;
MWLog(@"Photo from asset library error: %@",error);
[self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
}];
} @catch (NSException *e) {
MWLog(@"Photo from asset library error: %@", e);
[self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
}
}
});
} else if ([_photoURL isFileReferenceURL]) {
// Load from local file async
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@autoreleasepool {
@try {
self.underlyingImage = [UIImage imageWithContentsOfFile:self->_photoURL.path];
if (!self->_underlyingImage) {
MWLog(@"Error loading photo from path: %@", _photoURL.path);
}
} @finally {
[self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
}
}
});
} else {
// Load async from web (using SDWebImage)
@try {
EMSDWebImageManager *manager = [EMSDWebImageManager sharedManager];
_webImageOperation = [manager downloadImageWithURL:_photoURL
options:0
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
if (expectedSize > 0) {
float progress = receivedSize / (float)expectedSize;
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:progress], @"progress",
self, @"photo", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:MWPHOTO_PROGRESS_NOTIFICATION object:dict];
}
}
completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (error) {
MWLog(@"SDWebImage failed to download image: %@", error);
}
self->_webImageOperation = nil;
self.underlyingImage = image;
[self imageLoadingComplete];
}];
} @catch (NSException *e) {
MWLog(@"Photo from web: %@", e);
_webImageOperation = nil;
[self imageLoadingComplete];
}
}
} else {
// Failed - no source
@throw [NSException exceptionWithName:nil reason:nil userInfo:nil];
}
}
// Release if we can get it again from path or url
- (void)unloadUnderlyingImage {
_loadingInProgress = NO;
self.underlyingImage = nil;
}
- (void)imageLoadingComplete {
// NSAssert([[NSThread currentThread] isMainThread], @"This method must be called on the main thread.");
// Complete so notify
_loadingInProgress = NO;
// Notify on next run loop
[self performSelector:@selector(postCompleteNotification) withObject:nil afterDelay:0];
}
- (void)postCompleteNotification {
[[NSNotificationCenter defaultCenter] postNotificationName:MWPHOTO_LOADING_DID_END_NOTIFICATION
object:self];
}
- (void)cancelAnyLoading {
if (_webImageOperation) {
[_webImageOperation cancel];
_loadingInProgress = NO;
}
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWPhotoBrowser.h
================================================
//
// MWPhotoBrowser.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 14/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import
#import
#import "MWPhoto.h"
#import "MWPhotoProtocol.h"
#import "MWCaptionView.h"
// Debug Logging
#if 0 // Set to 1 to enable debug logging
#define MWLog(x, ...) NSLog(x, ## __VA_ARGS__);
#else
#define MWLog(x, ...)
#endif
@class MWPhotoBrowser;
@protocol MWPhotoBrowserDelegate
- (NSUInteger)numberOfPhotosInPhotoBrowser:(MWPhotoBrowser *)photoBrowser;
- (id )photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index;
@optional
- (id )photoBrowser:(MWPhotoBrowser *)photoBrowser thumbPhotoAtIndex:(NSUInteger)index;
- (MWCaptionView *)photoBrowser:(MWPhotoBrowser *)photoBrowser captionViewForPhotoAtIndex:(NSUInteger)index;
- (NSString *)photoBrowser:(MWPhotoBrowser *)photoBrowser titleForPhotoAtIndex:(NSUInteger)index;
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser didDisplayPhotoAtIndex:(NSUInteger)index;
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser actionButtonPressedForPhotoAtIndex:(NSUInteger)index;
- (BOOL)photoBrowser:(MWPhotoBrowser *)photoBrowser isPhotoSelectedAtIndex:(NSUInteger)index;
- (void)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index selectedChanged:(BOOL)selected;
- (void)photoBrowserDidFinishModalPresentation:(MWPhotoBrowser *)photoBrowser;
@end
@interface MWPhotoBrowser : UIViewController
@property (nonatomic, weak) IBOutlet id delegate;
@property (nonatomic) BOOL zoomPhotosToFill;
@property (nonatomic) BOOL displayNavArrows;
@property (nonatomic) BOOL displayActionButton;
@property (nonatomic) BOOL displaySelectionButtons;
@property (nonatomic) BOOL alwaysShowControls;
@property (nonatomic) BOOL enableGrid;
@property (nonatomic) BOOL enableSwipeToDismiss;
@property (nonatomic) BOOL startOnGrid;
@property (nonatomic) NSUInteger delayToHideElements;
@property (nonatomic, readonly) NSUInteger currentIndex;
// Init
- (id)initWithPhotos:(NSArray *)photosArray __attribute__((deprecated("Use initWithDelegate: instead"))); // Depreciated
- (id)initWithDelegate:(id )delegate;
// Reloads the photo browser and refetches data
- (void)reloadData;
// Set page that photo browser starts on
- (void)setCurrentPhotoIndex:(NSUInteger)index;
- (void)setInitialPageIndex:(NSUInteger)index __attribute__((deprecated("Use setCurrentPhotoIndex: instead"))); // Depreciated
// Navigation
- (void)showNextPhotoAnimated:(BOOL)animated;
- (void)showPreviousPhotoAnimated:(BOOL)animated;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWPhotoBrowser.m
================================================
//
// MWPhotoBrowser.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 14/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import
#import "MWCommon.h"
#import "MWPhotoBrowser.h"
#import "MWPhotoBrowserPrivate.h"
#import "EMSDImageCache.h"
#define PADDING 10
#define ACTION_SHEET_OLD_ACTIONS 2000
@implementation MWPhotoBrowser
#pragma mark - Init
- (id)init {
if ((self = [super init])) {
[self _initialisation];
}
return self;
}
- (id)initWithDelegate:(id )delegate {
if ((self = [self init])) {
_delegate = delegate;
}
return self;
}
- (id)initWithPhotos:(NSArray *)photosArray {
if ((self = [self init])) {
_depreciatedPhotoData = photosArray;
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super initWithCoder:decoder])) {
[self _initialisation];
}
return self;
}
- (void)_initialisation {
// Defaults
NSNumber *isVCBasedStatusBarAppearanceNum = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"];
if (isVCBasedStatusBarAppearanceNum) {
_isVCBasedStatusBarAppearance = isVCBasedStatusBarAppearanceNum.boolValue;
} else {
_isVCBasedStatusBarAppearance = YES; // default
}
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7")) self.wantsFullScreenLayout = YES;
#endif
self.hidesBottomBarWhenPushed = YES;
_hasBelongedToViewController = NO;
_photoCount = NSNotFound;
_previousLayoutBounds = CGRectZero;
_currentPageIndex = 0;
_previousPageIndex = NSUIntegerMax;
_displayActionButton = YES;
_displayNavArrows = NO;
_zoomPhotosToFill = YES;
_performingLayout = NO; // Reset on view did appear
_rotating = NO;
_viewIsActive = NO;
_enableGrid = YES;
_startOnGrid = NO;
_enableSwipeToDismiss = YES;
_delayToHideElements = 5;
_visiblePages = [[NSMutableSet alloc] init];
_recycledPages = [[NSMutableSet alloc] init];
_photos = [[NSMutableArray alloc] init];
_thumbPhotos = [[NSMutableArray alloc] init];
_currentGridContentOffset = CGPointMake(0, CGFLOAT_MAX);
_didSavePreviousStateOfNavBar = NO;
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]){
self.automaticallyAdjustsScrollViewInsets = NO;
}
// Listen for MWPhoto notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleMWPhotoLoadingDidEndNotification:)
name:MWPHOTO_LOADING_DID_END_NOTIFICATION
object:nil];
}
- (void)dealloc {
_pagingScrollView.delegate = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self releaseAllUnderlyingPhotos:NO];
[[EMSDImageCache sharedImageCache] clearMemory]; // clear memory
}
- (void)releaseAllUnderlyingPhotos:(BOOL)preserveCurrent {
// Create a copy in case this array is modified while we are looping through
// Release photos
NSArray *copy = [_photos copy];
for (id p in copy) {
if (p != [NSNull null]) {
if (preserveCurrent && p == [self photoAtIndex:self.currentIndex]) {
continue; // skip current
}
[p unloadUnderlyingImage];
}
}
// Release thumbs
copy = [_thumbPhotos copy];
for (id p in copy) {
if (p != [NSNull null]) {
[p unloadUnderlyingImage];
}
}
}
- (void)didReceiveMemoryWarning {
// Release any cached data, images, etc that aren't in use.
[self releaseAllUnderlyingPhotos:YES];
[_recycledPages removeAllObjects];
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
#pragma mark - View Loading
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
// Validate grid settings
if (_startOnGrid) _enableGrid = YES;
if (_enableGrid) {
_enableGrid = [_delegate respondsToSelector:@selector(photoBrowser:thumbPhotoAtIndex:)];
}
if (!_enableGrid) _startOnGrid = NO;
// View
self.view.backgroundColor = [UIColor blackColor];
self.view.clipsToBounds = YES;
// Setup paging scrolling view
CGRect pagingScrollViewFrame = [self frameForPagingScrollView];
_pagingScrollView = [[UIScrollView alloc] initWithFrame:pagingScrollViewFrame];
_pagingScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_pagingScrollView.pagingEnabled = YES;
_pagingScrollView.delegate = self;
_pagingScrollView.showsHorizontalScrollIndicator = NO;
_pagingScrollView.showsVerticalScrollIndicator = NO;
_pagingScrollView.backgroundColor = [UIColor blackColor];
_pagingScrollView.contentSize = [self contentSizeForPagingScrollView];
[self.view addSubview:_pagingScrollView];
// Toolbar
_toolbar = [[UIToolbar alloc] initWithFrame:[self frameForToolbarAtOrientation:self.interfaceOrientation]];
_toolbar.tintColor = SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7") ? [UIColor colorWithRed:250/255.0 green:250/255.0 blue:250/255.0 alpha:1.0] : nil;
if ([_toolbar respondsToSelector:@selector(setBarTintColor:)]) {
_toolbar.barTintColor = nil;
}
if ([[UIToolbar class] respondsToSelector:@selector(appearance)]) {
[_toolbar setBackgroundImage:nil forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
[_toolbar setBackgroundImage:nil forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsLandscapePhone];
}
_toolbar.barStyle = UIBarStyleBlackTranslucent;
_toolbar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
// Toolbar Items
if (self.displayNavArrows) {
NSString *arrowPathFormat;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7")) {
arrowPathFormat = @"MWPhotoBrowser.bundle/images/UIBarButtonItemArrowOutline%@.png";
} else {
arrowPathFormat = @"MWPhotoBrowser.bundle/images/UIBarButtonItemArrow%@.png";
}
_previousButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:arrowPathFormat, @"Left"]] style:UIBarButtonItemStylePlain target:self action:@selector(gotoPreviousPage)];
_nextButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:arrowPathFormat, @"Right"]] style:UIBarButtonItemStylePlain target:self action:@selector(gotoNextPage)];
}
if (self.displayActionButton) {
_actionButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(actionButtonPressed:)];
}
// Update
[self reloadData];
// Swipe to dismiss
if (_enableSwipeToDismiss) {
UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(doneButtonPressed:)];
swipeGesture.direction = UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionUp;
[self.view addGestureRecognizer:swipeGesture];
}
// Super
[super viewDidLoad];
}
- (void)performLayout {
// Setup
_performingLayout = YES;
NSUInteger numberOfPhotos = [self numberOfPhotos];
// Setup pages
[_visiblePages removeAllObjects];
[_recycledPages removeAllObjects];
// Navigation buttons
if ([self.navigationController.viewControllers objectAtIndex:0] == self) {
// We're first on stack so show done button
_doneButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"完成", nil) style:UIBarButtonItemStylePlain target:self action:@selector(doneButtonPressed:)];
// Set appearance
if ([UIBarButtonItem respondsToSelector:@selector(appearance)]) {
[_doneButton setBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[_doneButton setBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
[_doneButton setBackgroundImage:nil forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[_doneButton setBackgroundImage:nil forState:UIControlStateHighlighted barMetrics:UIBarMetricsLandscapePhone];
[_doneButton setTitleTextAttributes:[NSDictionary dictionary] forState:UIControlStateNormal];
[_doneButton setTitleTextAttributes:[NSDictionary dictionary] forState:UIControlStateHighlighted];
}
self.navigationItem.rightBarButtonItem = _doneButton;
} else {
// We're not first so show back button
UIViewController *previousViewController = [self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2];
NSString *backButtonTitle = previousViewController.navigationItem.backBarButtonItem ? previousViewController.navigationItem.backBarButtonItem.title : previousViewController.title;
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle:backButtonTitle style:UIBarButtonItemStylePlain target:nil action:nil];
// Appearance
if ([UIBarButtonItem respondsToSelector:@selector(appearance)]) {
[newBackButton setBackButtonBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[newBackButton setBackButtonBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
[newBackButton setBackButtonBackgroundImage:nil forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[newBackButton setBackButtonBackgroundImage:nil forState:UIControlStateHighlighted barMetrics:UIBarMetricsLandscapePhone];
[newBackButton setTitleTextAttributes:[NSDictionary dictionary] forState:UIControlStateNormal];
[newBackButton setTitleTextAttributes:[NSDictionary dictionary] forState:UIControlStateHighlighted];
}
_previousViewControllerBackButton = previousViewController.navigationItem.backBarButtonItem; // remember previous
previousViewController.navigationItem.backBarButtonItem = newBackButton;
}
// Toolbar items
BOOL hasItems = NO;
UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:self action:nil];
fixedSpace.width = 32; // To balance action button
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
NSMutableArray *items = [[NSMutableArray alloc] init];
// Left button - Grid
if (_enableGrid) {
hasItems = YES;
NSString *buttonName = @"UIBarButtonItemGrid";
if (SYSTEM_VERSION_LESS_THAN(@"7")) buttonName = @"UIBarButtonItemGridiOS6";
[items addObject:[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"MWPhotoBrowser.bundle/images/%@.png", buttonName]] style:UIBarButtonItemStylePlain target:self action:@selector(showGridAnimated)]];
} else {
[items addObject:fixedSpace];
}
// Middle - Nav
if (_previousButton && _nextButton && numberOfPhotos > 1) {
hasItems = YES;
[items addObject:flexSpace];
[items addObject:_previousButton];
[items addObject:flexSpace];
[items addObject:_nextButton];
[items addObject:flexSpace];
} else {
[items addObject:flexSpace];
}
// Right - Action
if (_actionButton && !(!hasItems && !self.navigationItem.rightBarButtonItem)) {
[items addObject:_actionButton];
} else {
// We're not showing the toolbar so try and show in top right
if (_actionButton)
self.navigationItem.rightBarButtonItem = _actionButton;
[items addObject:fixedSpace];
}
// Toolbar visibility
[_toolbar setItems:items];
BOOL hideToolbar = YES;
for (UIBarButtonItem* item in _toolbar.items) {
if (item != fixedSpace && item != flexSpace) {
hideToolbar = NO;
break;
}
}
if (hideToolbar) {
[_toolbar removeFromSuperview];
} else {
[self.view addSubview:_toolbar];
}
// Update nav
[self updateNavigation];
// Content offset
_pagingScrollView.contentOffset = [self contentOffsetForPageAtIndex:_currentPageIndex];
[self tilePages];
_performingLayout = NO;
}
// Release any retained subviews of the main view.
- (void)viewDidUnload {
_currentPageIndex = 0;
_pagingScrollView = nil;
_visiblePages = nil;
_recycledPages = nil;
_toolbar = nil;
_previousButton = nil;
_nextButton = nil;
_progressHUD = nil;
[super viewDidUnload];
}
- (BOOL)presentingViewControllerPrefersStatusBarHidden {
UIViewController *presenting = self.presentingViewController;
if (presenting) {
if ([presenting isKindOfClass:[UINavigationController class]]) {
presenting = [(UINavigationController *)presenting topViewController];
}
} else {
// We're in a navigation controller so get previous one!
if (self.navigationController && self.navigationController.viewControllers.count > 1) {
presenting = [self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2];
}
}
if (presenting) {
return [presenting prefersStatusBarHidden];
} else {
return NO;
}
}
#pragma mark - Appearance
- (void)viewWillAppear:(BOOL)animated {
// Super
[super viewWillAppear:animated];
// Status bar
if ([UIViewController instancesRespondToSelector:@selector(prefersStatusBarHidden)]) {
_leaveStatusBarAlone = [self presentingViewControllerPrefersStatusBarHidden];
} else {
_leaveStatusBarAlone = [UIApplication sharedApplication].statusBarHidden;
}
if (CGRectEqualToRect([[UIApplication sharedApplication] statusBarFrame], CGRectZero)) {
// If the frame is zero then definitely leave it alone
_leaveStatusBarAlone = YES;
}
BOOL fullScreen = YES;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7")) fullScreen = self.wantsFullScreenLayout;
#endif
if (!_leaveStatusBarAlone && fullScreen && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
_previousStatusBarStyle = [[UIApplication sharedApplication] statusBarStyle];
if (SYSTEM_VERSION_LESS_THAN(@"7")) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent animated:animated];
#pragma clang diagnostic push
} else {
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:animated];
}
}
// Navigation bar appearance
if (!_viewIsActive && [self.navigationController.viewControllers objectAtIndex:0] != self) {
[self storePreviousNavBarAppearance];
}
[self setNavBarAppearance:animated];
// Update UI
[self hideControlsAfterDelay];
// Initial appearance
if (!_viewHasAppearedInitially) {
if (_startOnGrid) {
[self showGrid:NO];
}
_viewHasAppearedInitially = YES;
}
}
- (void)viewWillDisappear:(BOOL)animated {
// Check that we're being popped for good
if ([self.navigationController.viewControllers objectAtIndex:0] != self &&
![self.navigationController.viewControllers containsObject:self]) {
// State
_viewIsActive = NO;
// Bar state / appearance
[self restorePreviousNavBarAppearance:animated];
}
// Controls
[self.navigationController.navigationBar.layer removeAllAnimations]; // Stop all animations on nav bar
[NSObject cancelPreviousPerformRequestsWithTarget:self]; // Cancel any pending toggles from taps
[self setControlsHidden:NO animated:NO permanent:YES];
// Status bar
BOOL fullScreen = YES;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7")) fullScreen = self.wantsFullScreenLayout;
#endif
if (!_leaveStatusBarAlone && fullScreen && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
[[UIApplication sharedApplication] setStatusBarStyle:_previousStatusBarStyle animated:animated];
}
// Super
[super viewWillDisappear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
_viewIsActive = YES;
}
- (void)willMoveToParentViewController:(UIViewController *)parent {
if (parent && _hasBelongedToViewController) {
[NSException raise:@"MWPhotoBrowser Instance Reuse" format:@"MWPhotoBrowser instances cannot be reused."];
}
}
- (void)didMoveToParentViewController:(UIViewController *)parent {
if (!parent) _hasBelongedToViewController = YES;
}
#pragma mark - Nav Bar Appearance
- (void)setNavBarAppearance:(BOOL)animated {
[self.navigationController setNavigationBarHidden:NO animated:animated];
UINavigationBar *navBar = self.navigationController.navigationBar;
navBar.tintColor = SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7") ? [UIColor colorWithRed:250/255.0 green:250/255.0 blue:250/255.0 alpha:1.0]: nil;
if ([navBar respondsToSelector:@selector(setBarTintColor:)]) {
navBar.barTintColor = nil;
navBar.shadowImage = nil;
}
navBar.translucent = YES;
navBar.barStyle = UIBarStyleBlackTranslucent;
if ([[UINavigationBar class] respondsToSelector:@selector(appearance)]) {
[navBar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
[navBar setBackgroundImage:nil forBarMetrics:UIBarMetricsLandscapePhone];
}
}
- (void)storePreviousNavBarAppearance {
_didSavePreviousStateOfNavBar = YES;
if ([UINavigationBar instancesRespondToSelector:@selector(barTintColor)]) {
_previousNavBarBarTintColor = self.navigationController.navigationBar.barTintColor;
}
_previousNavBarTranslucent = self.navigationController.navigationBar.translucent;
_previousNavBarTintColor = self.navigationController.navigationBar.tintColor;
_previousNavBarHidden = self.navigationController.navigationBarHidden;
_previousNavBarStyle = self.navigationController.navigationBar.barStyle;
if ([[UINavigationBar class] respondsToSelector:@selector(appearance)]) {
_previousNavigationBarBackgroundImageDefault = [self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsDefault];
_previousNavigationBarBackgroundImageLandscapePhone = [self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsLandscapePhone];
}
}
- (void)restorePreviousNavBarAppearance:(BOOL)animated {
if (_didSavePreviousStateOfNavBar) {
[self.navigationController setNavigationBarHidden:_previousNavBarHidden animated:animated];
UINavigationBar *navBar = self.navigationController.navigationBar;
navBar.tintColor = _previousNavBarTintColor;
navBar.translucent = _previousNavBarTranslucent;
if ([UINavigationBar instancesRespondToSelector:@selector(barTintColor)]) {
navBar.barTintColor = _previousNavBarBarTintColor;
}
navBar.barStyle = _previousNavBarStyle;
if ([[UINavigationBar class] respondsToSelector:@selector(appearance)]) {
[navBar setBackgroundImage:_previousNavigationBarBackgroundImageDefault forBarMetrics:UIBarMetricsDefault];
[navBar setBackgroundImage:_previousNavigationBarBackgroundImageLandscapePhone forBarMetrics:UIBarMetricsLandscapePhone];
}
// Restore back button if we need to
if (_previousViewControllerBackButton) {
UIViewController *previousViewController = [self.navigationController topViewController]; // We've disappeared so previous is now top
previousViewController.navigationItem.backBarButtonItem = _previousViewControllerBackButton;
_previousViewControllerBackButton = nil;
}
}
}
#pragma mark - Layout
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[self layoutVisiblePages];
}
- (void)layoutVisiblePages {
// Flag
_performingLayout = YES;
// Toolbar
_toolbar.frame = [self frameForToolbarAtOrientation:self.interfaceOrientation];
// Remember index
NSUInteger indexPriorToLayout = _currentPageIndex;
// Get paging scroll view frame to determine if anything needs changing
CGRect pagingScrollViewFrame = [self frameForPagingScrollView];
// Frame needs changing
if (!_skipNextPagingScrollViewPositioning) {
_pagingScrollView.frame = pagingScrollViewFrame;
}
_skipNextPagingScrollViewPositioning = NO;
// Recalculate contentSize based on current orientation
_pagingScrollView.contentSize = [self contentSizeForPagingScrollView];
// Adjust frames and configuration of each visible page
for (MWZoomingScrollView *page in _visiblePages) {
NSUInteger index = page.index;
page.frame = [self frameForPageAtIndex:index];
if (page.captionView) {
page.captionView.frame = [self frameForCaptionView:page.captionView atIndex:index];
}
if (page.selectedButton) {
page.selectedButton.frame = [self frameForSelectedButton:page.selectedButton atIndex:index];
}
// Adjust scales if bounds has changed since last time
if (!CGRectEqualToRect(_previousLayoutBounds, self.view.bounds)) {
// Update zooms for new bounds
[page setMaxMinZoomScalesForCurrentBounds];
_previousLayoutBounds = self.view.bounds;
}
}
// Adjust contentOffset to preserve page location based on values collected prior to location
_pagingScrollView.contentOffset = [self contentOffsetForPageAtIndex:indexPriorToLayout];
[self didStartViewingPageAtIndex:_currentPageIndex]; // initial
// Reset
_currentPageIndex = indexPriorToLayout;
_performingLayout = NO;
}
#pragma mark - Rotation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
// Remember page index before rotation
_pageIndexBeforeRotation = _currentPageIndex;
_rotating = YES;
// In iOS 7 the nav bar gets shown after rotation, but might as well do this for everything!
if ([self areControlsHidden]) {
// Force hidden
self.navigationController.navigationBarHidden = YES;
}
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
// Perform layout
_currentPageIndex = _pageIndexBeforeRotation;
// Delay control holding
[self hideControlsAfterDelay];
// Layout
[self layoutVisiblePages];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
_rotating = NO;
// Ensure nav bar isn't re-displayed
if ([self areControlsHidden]) {
self.navigationController.navigationBarHidden = NO;
self.navigationController.navigationBar.alpha = 0;
}
}
#pragma mark - Data
- (NSUInteger)currentIndex {
return _currentPageIndex;
}
- (void)reloadData {
// Reset
_photoCount = NSNotFound;
// Get data
NSUInteger numberOfPhotos = [self numberOfPhotos];
[self releaseAllUnderlyingPhotos:YES];
[_photos removeAllObjects];
[_thumbPhotos removeAllObjects];
for (int i = 0; i < numberOfPhotos; i++) {
[_photos addObject:[NSNull null]];
[_thumbPhotos addObject:[NSNull null]];
}
// Update current page index
if (numberOfPhotos > 0) {
_currentPageIndex = MAX(0, MIN(_currentPageIndex, numberOfPhotos - 1));
} else {
_currentPageIndex = 0;
}
// Update layout
if ([self isViewLoaded]) {
while (_pagingScrollView.subviews.count) {
[[_pagingScrollView.subviews lastObject] removeFromSuperview];
}
[self performLayout];
[self.view setNeedsLayout];
}
}
- (NSUInteger)numberOfPhotos {
if (_photoCount == NSNotFound) {
if ([_delegate respondsToSelector:@selector(numberOfPhotosInPhotoBrowser:)]) {
_photoCount = [_delegate numberOfPhotosInPhotoBrowser:self];
} else if (_depreciatedPhotoData) {
_photoCount = _depreciatedPhotoData.count;
}
}
if (_photoCount == NSNotFound) _photoCount = 0;
return _photoCount;
}
- (id)photoAtIndex:(NSUInteger)index {
id photo = nil;
if (index < _photos.count) {
if ([_photos objectAtIndex:index] == [NSNull null]) {
if ([_delegate respondsToSelector:@selector(photoBrowser:photoAtIndex:)]) {
photo = [_delegate photoBrowser:self photoAtIndex:index];
} else if (_depreciatedPhotoData && index < _depreciatedPhotoData.count) {
photo = [_depreciatedPhotoData objectAtIndex:index];
}
if (photo) [_photos replaceObjectAtIndex:index withObject:photo];
} else {
photo = [_photos objectAtIndex:index];
}
}
return photo;
}
- (id)thumbPhotoAtIndex:(NSUInteger)index {
id photo = nil;
if (index < _thumbPhotos.count) {
if ([_thumbPhotos objectAtIndex:index] == [NSNull null]) {
if ([_delegate respondsToSelector:@selector(photoBrowser:thumbPhotoAtIndex:)]) {
photo = [_delegate photoBrowser:self thumbPhotoAtIndex:index];
}
if (photo) [_thumbPhotos replaceObjectAtIndex:index withObject:photo];
} else {
photo = [_thumbPhotos objectAtIndex:index];
}
}
return photo;
}
- (MWCaptionView *)captionViewForPhotoAtIndex:(NSUInteger)index {
MWCaptionView *captionView = nil;
if ([_delegate respondsToSelector:@selector(photoBrowser:captionViewForPhotoAtIndex:)]) {
captionView = [_delegate photoBrowser:self captionViewForPhotoAtIndex:index];
} else {
id photo = [self photoAtIndex:index];
if ([photo respondsToSelector:@selector(caption)]) {
if ([photo caption]) captionView = [[MWCaptionView alloc] initWithPhoto:photo];
}
}
captionView.alpha = [self areControlsHidden] ? 0 : 1; // Initial alpha
return captionView;
}
- (BOOL)photoIsSelectedAtIndex:(NSUInteger)index {
BOOL value = NO;
if (_displaySelectionButtons) {
if ([self.delegate respondsToSelector:@selector(photoBrowser:isPhotoSelectedAtIndex:)]) {
value = [self.delegate photoBrowser:self isPhotoSelectedAtIndex:index];
}
}
return value;
}
- (void)setPhotoSelected:(BOOL)selected atIndex:(NSUInteger)index {
if (_displaySelectionButtons) {
if ([self.delegate respondsToSelector:@selector(photoBrowser:photoAtIndex:selectedChanged:)]) {
[self.delegate photoBrowser:self photoAtIndex:index selectedChanged:selected];
}
}
}
- (UIImage *)imageForPhoto:(id)photo {
if (photo) {
// Get image or obtain in background
if ([photo underlyingImage]) {
return [photo underlyingImage];
} else {
[photo loadUnderlyingImageAndNotify];
}
}
return nil;
}
- (void)loadAdjacentPhotosIfNecessary:(id)photo {
MWZoomingScrollView *page = [self pageDisplayingPhoto:photo];
if (page) {
// If page is current page then initiate loading of previous and next pages
NSUInteger pageIndex = page.index;
if (_currentPageIndex == pageIndex) {
if (pageIndex > 0) {
// Preload index - 1
id photo = [self photoAtIndex:pageIndex-1];
if (![photo underlyingImage]) {
[photo loadUnderlyingImageAndNotify];
MWLog(@"Pre-loading image at index %lu", (unsigned long)pageIndex-1);
}
}
if (pageIndex < [self numberOfPhotos] - 1) {
// Preload index + 1
id photo = [self photoAtIndex:pageIndex+1];
if (![photo underlyingImage]) {
[photo loadUnderlyingImageAndNotify];
MWLog(@"Pre-loading image at index %lu", (unsigned long)pageIndex+1);
}
}
}
}
}
#pragma mark - MWPhoto Loading Notification
- (void)handleMWPhotoLoadingDidEndNotification:(NSNotification *)notification {
id photo = [notification object];
MWZoomingScrollView *page = [self pageDisplayingPhoto:photo];
if (page) {
if ([photo underlyingImage]) {
// Successful load
[page displayImage];
[self loadAdjacentPhotosIfNecessary:photo];
} else {
// Failed to load
[page displayImageFailure];
}
// Update nav
[self updateNavigation];
}
}
#pragma mark - Paging
- (void)tilePages {
// Calculate which pages should be visible
// Ignore padding as paging bounces encroach on that
// and lead to false page loads
CGRect visibleBounds = _pagingScrollView.bounds;
NSInteger iFirstIndex = (NSInteger)floorf((CGRectGetMinX(visibleBounds)+PADDING*2) / CGRectGetWidth(visibleBounds));
NSInteger iLastIndex = (NSInteger)floorf((CGRectGetMaxX(visibleBounds)-PADDING*2-1) / CGRectGetWidth(visibleBounds));
if (iFirstIndex < 0) iFirstIndex = 0;
if (iFirstIndex > [self numberOfPhotos] - 1) iFirstIndex = [self numberOfPhotos] - 1;
if (iLastIndex < 0) iLastIndex = 0;
if (iLastIndex > [self numberOfPhotos] - 1) iLastIndex = [self numberOfPhotos] - 1;
// Recycle no longer needed pages
NSInteger pageIndex;
for (MWZoomingScrollView *page in _visiblePages) {
pageIndex = page.index;
if (pageIndex < (NSUInteger)iFirstIndex || pageIndex > (NSUInteger)iLastIndex) {
[_recycledPages addObject:page];
[page.captionView removeFromSuperview];
[page.selectedButton removeFromSuperview];
[page prepareForReuse];
[page removeFromSuperview];
MWLog(@"Removed page at index %lu", (unsigned long)pageIndex);
}
}
[_visiblePages minusSet:_recycledPages];
while (_recycledPages.count > 2) // Only keep 2 recycled pages
[_recycledPages removeObject:[_recycledPages anyObject]];
// Add missing pages
for (NSUInteger index = (NSUInteger)iFirstIndex; index <= (NSUInteger)iLastIndex; index++) {
if (![self isDisplayingPageForIndex:index]) {
// Add new page
MWZoomingScrollView *page = [self dequeueRecycledPage];
if (!page) {
page = [[MWZoomingScrollView alloc] initWithPhotoBrowser:self];
}
[_visiblePages addObject:page];
[self configurePage:page forIndex:index];
[_pagingScrollView addSubview:page];
MWLog(@"Added page at index %lu", (unsigned long)index);
// Add caption
MWCaptionView *captionView = [self captionViewForPhotoAtIndex:index];
if (captionView) {
captionView.frame = [self frameForCaptionView:captionView atIndex:index];
[_pagingScrollView addSubview:captionView];
page.captionView = captionView;
}
// Add selected button
if (self.displaySelectionButtons) {
UIButton *selectedButton = [UIButton buttonWithType:UIButtonTypeCustom];
[selectedButton setImage:[UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageSelectedOff.png"] forState:UIControlStateNormal];
[selectedButton setImage:[UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageSelectedOn.png"] forState:UIControlStateSelected];
[selectedButton sizeToFit];
selectedButton.adjustsImageWhenHighlighted = NO;
[selectedButton addTarget:self action:@selector(selectedButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
selectedButton.frame = [self frameForSelectedButton:selectedButton atIndex:index];
[_pagingScrollView addSubview:selectedButton];
page.selectedButton = selectedButton;
selectedButton.selected = [self photoIsSelectedAtIndex:index];
}
}
}
}
- (void)updateVisiblePageStates {
NSSet *copy = [_visiblePages copy];
for (MWZoomingScrollView *page in copy) {
// Update selection
page.selectedButton.selected = [self photoIsSelectedAtIndex:page.index];
}
}
- (BOOL)isDisplayingPageForIndex:(NSUInteger)index {
for (MWZoomingScrollView *page in _visiblePages)
if (page.index == index) return YES;
return NO;
}
- (MWZoomingScrollView *)pageDisplayedAtIndex:(NSUInteger)index {
MWZoomingScrollView *thePage = nil;
for (MWZoomingScrollView *page in _visiblePages) {
if (page.index == index) {
thePage = page; break;
}
}
return thePage;
}
- (MWZoomingScrollView *)pageDisplayingPhoto:(id)photo {
MWZoomingScrollView *thePage = nil;
for (MWZoomingScrollView *page in _visiblePages) {
if (page.photo == photo) {
thePage = page; break;
}
}
return thePage;
}
- (void)configurePage:(MWZoomingScrollView *)page forIndex:(NSUInteger)index {
page.frame = [self frameForPageAtIndex:index];
page.index = index;
page.photo = [self photoAtIndex:index];
}
- (MWZoomingScrollView *)dequeueRecycledPage {
MWZoomingScrollView *page = [_recycledPages anyObject];
if (page) {
[_recycledPages removeObject:page];
}
return page;
}
// Handle page changes
- (void)didStartViewingPageAtIndex:(NSUInteger)index {
if (![self numberOfPhotos]) {
// Show controls
[self setControlsHidden:NO animated:YES permanent:YES];
return;
}
// Release images further away than +/-1
NSUInteger i;
if (index > 0) {
// Release anything < index - 1
for (i = 0; i < index-1; i++) {
id photo = [_photos objectAtIndex:i];
if (photo != [NSNull null]) {
[photo unloadUnderlyingImage];
[_photos replaceObjectAtIndex:i withObject:[NSNull null]];
MWLog(@"Released underlying image at index %lu", (unsigned long)i);
}
}
}
if (index < [self numberOfPhotos] - 1) {
// Release anything > index + 1
for (i = index + 2; i < _photos.count; i++) {
id photo = [_photos objectAtIndex:i];
if (photo != [NSNull null]) {
[photo unloadUnderlyingImage];
[_photos replaceObjectAtIndex:i withObject:[NSNull null]];
MWLog(@"Released underlying image at index %lu", (unsigned long)i);
}
}
}
// Load adjacent images if needed and the photo is already
// loaded. Also called after photo has been loaded in background
id currentPhoto = [self photoAtIndex:index];
if ([currentPhoto underlyingImage]) {
// photo loaded so load ajacent now
[self loadAdjacentPhotosIfNecessary:currentPhoto];
}
// Notify delegate
if (index != _previousPageIndex) {
if ([_delegate respondsToSelector:@selector(photoBrowser:didDisplayPhotoAtIndex:)])
[_delegate photoBrowser:self didDisplayPhotoAtIndex:index];
_previousPageIndex = index;
}
// Update nav
[self updateNavigation];
}
#pragma mark - Frame Calculations
- (CGRect)frameForPagingScrollView {
CGRect frame = self.view.bounds;// [[UIScreen mainScreen] bounds];
frame.origin.x -= PADDING;
frame.size.width += (2 * PADDING);
return CGRectIntegral(frame);
}
- (CGRect)frameForPageAtIndex:(NSUInteger)index {
// We have to use our paging scroll view's bounds, not frame, to calculate the page placement. When the device is in
// landscape orientation, the frame will still be in portrait because the pagingScrollView is the root view controller's
// view, so its frame is in window coordinate space, which is never rotated. Its bounds, however, will be in landscape
// because it has a rotation transform applied.
CGRect bounds = _pagingScrollView.bounds;
CGRect pageFrame = bounds;
pageFrame.size.width -= (2 * PADDING);
pageFrame.origin.x = (bounds.size.width * index) + PADDING;
return CGRectIntegral(pageFrame);
}
- (CGSize)contentSizeForPagingScrollView {
// We have to use the paging scroll view's bounds to calculate the contentSize, for the same reason outlined above.
CGRect bounds = _pagingScrollView.bounds;
return CGSizeMake(bounds.size.width * [self numberOfPhotos], bounds.size.height);
}
- (CGPoint)contentOffsetForPageAtIndex:(NSUInteger)index {
CGFloat pageWidth = _pagingScrollView.bounds.size.width;
CGFloat newOffset = index * pageWidth;
return CGPointMake(newOffset, 0);
}
- (CGRect)frameForToolbarAtOrientation:(UIInterfaceOrientation)orientation {
CGFloat height = 44;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone &&
UIInterfaceOrientationIsLandscape(orientation)) height = 32;
return CGRectIntegral(CGRectMake(0, self.view.bounds.size.height - height, self.view.bounds.size.width, height));
}
- (CGRect)frameForCaptionView:(MWCaptionView *)captionView atIndex:(NSUInteger)index {
CGRect pageFrame = [self frameForPageAtIndex:index];
CGSize captionSize = [captionView sizeThatFits:CGSizeMake(pageFrame.size.width, 0)];
CGRect captionFrame = CGRectMake(pageFrame.origin.x,
pageFrame.size.height - captionSize.height - (_toolbar.superview?_toolbar.frame.size.height:0),
pageFrame.size.width,
captionSize.height);
return CGRectIntegral(captionFrame);
}
- (CGRect)frameForSelectedButton:(UIButton *)selectedButton atIndex:(NSUInteger)index {
CGRect pageFrame = [self frameForPageAtIndex:index];
CGFloat yOffset = 0;
if (![self areControlsHidden]) {
UINavigationBar *navBar = self.navigationController.navigationBar;
yOffset = navBar.frame.origin.y + navBar.frame.size.height;
}
CGFloat statusBarOffset = [[UIApplication sharedApplication] statusBarFrame].size.height;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7") && !self.wantsFullScreenLayout) statusBarOffset = 0;
#endif
CGRect captionFrame = CGRectMake(pageFrame.origin.x + pageFrame.size.width - 20 - selectedButton.frame.size.width,
statusBarOffset + yOffset,
selectedButton.frame.size.width,
selectedButton.frame.size.height);
return CGRectIntegral(captionFrame);
}
#pragma mark - UIScrollView Delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// Checks
if (!_viewIsActive || _performingLayout || _rotating) return;
// Tile pages
[self tilePages];
// Calculate current page
CGRect visibleBounds = _pagingScrollView.bounds;
NSInteger index = (NSInteger)(floorf(CGRectGetMidX(visibleBounds) / CGRectGetWidth(visibleBounds)));
if (index < 0) index = 0;
if (index > [self numberOfPhotos] - 1) index = [self numberOfPhotos] - 1;
NSUInteger previousCurrentPage = _currentPageIndex;
_currentPageIndex = index;
if (_currentPageIndex != previousCurrentPage) {
[self didStartViewingPageAtIndex:index];
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// Hide controls when dragging begins
[self setControlsHidden:YES animated:YES permanent:NO];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// Update nav when page changes
[self updateNavigation];
}
#pragma mark - Navigation
- (void)updateNavigation {
// Title
NSUInteger numberOfPhotos = [self numberOfPhotos];
if (_gridController) {
if (_gridController.selectionMode) {
self.title = NSLocalizedString(@"Select Photos", nil);
} else {
NSString *photosText;
if (numberOfPhotos == 1) {
photosText = NSLocalizedString(@"photo", @"Used in the context: '1 photo'");
} else {
photosText = NSLocalizedString(@"photos", @"Used in the context: '3 photos'");
}
self.title = [NSString stringWithFormat:@"%lu %@", (unsigned long)numberOfPhotos, photosText];
}
} else if (numberOfPhotos > 1) {
if ([_delegate respondsToSelector:@selector(photoBrowser:titleForPhotoAtIndex:)]) {
self.title = [_delegate photoBrowser:self titleForPhotoAtIndex:_currentPageIndex];
} else {
self.title = [NSString stringWithFormat:@"%lu %@ %lu", (unsigned long)(_currentPageIndex+1), NSLocalizedString(@"of", @"Used in the context: 'Showing 1 of 3 items'"), (unsigned long)numberOfPhotos];
}
} else {
self.title = nil;
}
// Buttons
_previousButton.enabled = (_currentPageIndex > 0);
_nextButton.enabled = (_currentPageIndex < numberOfPhotos - 1);
_actionButton.enabled = [[self photoAtIndex:_currentPageIndex] underlyingImage] != nil;
}
- (void)jumpToPageAtIndex:(NSUInteger)index animated:(BOOL)animated {
// Change page
if (index < [self numberOfPhotos]) {
CGRect pageFrame = [self frameForPageAtIndex:index];
[_pagingScrollView setContentOffset:CGPointMake(pageFrame.origin.x - PADDING, 0) animated:animated];
[self updateNavigation];
}
// Update timer to give more time
[self hideControlsAfterDelay];
}
- (void)gotoPreviousPage {
[self showPreviousPhotoAnimated:NO];
}
- (void)gotoNextPage {
[self showNextPhotoAnimated:NO];
}
- (void)showPreviousPhotoAnimated:(BOOL)animated {
[self jumpToPageAtIndex:_currentPageIndex-1 animated:animated];
}
- (void)showNextPhotoAnimated:(BOOL)animated {
[self jumpToPageAtIndex:_currentPageIndex+1 animated:animated];
}
#pragma mark - Interactions
- (void)selectedButtonTapped:(id)sender {
UIButton *selectedButton = (UIButton *)sender;
selectedButton.selected = !selectedButton.selected;
NSUInteger index = NSUIntegerMax;
for (MWZoomingScrollView *page in _visiblePages) {
if (page.selectedButton == selectedButton) {
index = page.index;
break;
}
}
if (index != NSUIntegerMax) {
[self setPhotoSelected:selectedButton.selected atIndex:index];
}
}
#pragma mark - Grid
- (void)showGridAnimated {
[self showGrid:YES];
}
- (void)showGrid:(BOOL)animated {
if (_gridController) return;
// Init grid controller
_gridController = [[MWGridViewController alloc] init];
_gridController.initialContentOffset = _currentGridContentOffset;
_gridController.browser = self;
_gridController.selectionMode = _displaySelectionButtons;
_gridController.view.frame = self.view.bounds;
_gridController.view.frame = CGRectOffset(_gridController.view.frame, 0, (self.startOnGrid ? -1 : 1) * self.view.bounds.size.height);
// Stop specific layout being triggered
_skipNextPagingScrollViewPositioning = YES;
// Add as a child view controller
[self addChildViewController:_gridController];
[self.view addSubview:_gridController.view];
// Hide action button on nav bar if it exists
if (self.navigationItem.rightBarButtonItem == _actionButton) {
_gridPreviousRightNavItem = _actionButton;
[self.navigationItem setRightBarButtonItem:nil animated:YES];
} else {
_gridPreviousRightNavItem = nil;
}
// Update
[self updateNavigation];
[self setControlsHidden:NO animated:YES permanent:YES];
// Animate grid in and photo scroller out
[UIView animateWithDuration:animated ? 0.3 : 0 animations:^(void) {
self->_gridController.view.frame = self.view.bounds;
CGRect newPagingFrame = [self frameForPagingScrollView];
newPagingFrame = CGRectOffset(newPagingFrame, 0, (self.startOnGrid ? 1 : -1) * newPagingFrame.size.height);
self->_pagingScrollView.frame = newPagingFrame;
} completion:^(BOOL finished) {
[self->_gridController didMoveToParentViewController:self];
}];
}
- (void)hideGrid {
if (!_gridController) return;
// Remember previous content offset
_currentGridContentOffset = _gridController.collectionView.contentOffset;
// Restore action button if it was removed
if (_gridPreviousRightNavItem == _actionButton && _actionButton) {
[self.navigationItem setRightBarButtonItem:_gridPreviousRightNavItem animated:YES];
}
// Position prior to hide animation
CGRect newPagingFrame = [self frameForPagingScrollView];
newPagingFrame = CGRectOffset(newPagingFrame, 0, (self.startOnGrid ? 1 : -1) * newPagingFrame.size.height);
_pagingScrollView.frame = newPagingFrame;
// Remember and remove controller now so things can detect a nil grid controller
MWGridViewController *tmpGridController = _gridController;
_gridController = nil;
// Update
[self updateNavigation];
[self updateVisiblePageStates];
// Animate, hide grid and show paging scroll view
[UIView animateWithDuration:0.3 animations:^{
tmpGridController.view.frame = CGRectOffset(self.view.bounds, 0, (self.startOnGrid ? -1 : 1) * self.view.bounds.size.height);
self->_pagingScrollView.frame = [self frameForPagingScrollView];
} completion:^(BOOL finished) {
[tmpGridController willMoveToParentViewController:nil];
[tmpGridController.view removeFromSuperview];
[tmpGridController removeFromParentViewController];
[self setControlsHidden:NO animated:YES permanent:NO]; // retrigger timer
}];
}
#pragma mark - Control Hiding / Showing
// If permanent then we don't set timers to hide again
// Fades all controls on iOS 5 & 6, and iOS 7 controls slide and fade
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated permanent:(BOOL)permanent {
// Force visible
if (![self numberOfPhotos] || _gridController || _alwaysShowControls)
hidden = NO;
// Cancel any timers
[self cancelControlHiding];
// Animations & positions
BOOL slideAndFade = SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7");
CGFloat animatonOffset = 20;
CGFloat animationDuration = (animated ? 0.35 : 0);
// Status bar
if (!_leaveStatusBarAlone) {
if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
// iOS 7
// Hide status bar
if (!_isVCBasedStatusBarAppearance) {
// Non-view controller based
[[UIApplication sharedApplication] setStatusBarHidden:hidden withAnimation:animated ? UIStatusBarAnimationSlide : UIStatusBarAnimationNone];
} else {
// View controller based so animate away
_statusBarShouldBeHidden = hidden;
[UIView animateWithDuration:animationDuration animations:^(void) {
[self setNeedsStatusBarAppearanceUpdate];
} completion:^(BOOL finished) {}];
}
} else {
// iOS < 7
// Status bar and nav bar positioning
BOOL fullScreen = YES;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
if (SYSTEM_VERSION_LESS_THAN(@"7")) fullScreen = self.wantsFullScreenLayout;
#endif
if (fullScreen) {
// Need to get heights and set nav bar position to overcome display issues
// Get status bar height if visible
CGFloat statusBarHeight = 0;
if (![UIApplication sharedApplication].statusBarHidden) {
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
statusBarHeight = MIN(statusBarFrame.size.height, statusBarFrame.size.width);
}
// Status Bar
[[UIApplication sharedApplication] setStatusBarHidden:hidden withAnimation:animated?UIStatusBarAnimationFade:UIStatusBarAnimationNone];
// Get status bar height if visible
if (![UIApplication sharedApplication].statusBarHidden) {
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
statusBarHeight = MIN(statusBarFrame.size.height, statusBarFrame.size.width);
}
// Set navigation bar frame
CGRect navBarFrame = self.navigationController.navigationBar.frame;
navBarFrame.origin.y = statusBarHeight;
self.navigationController.navigationBar.frame = navBarFrame;
}
}
}
// Toolbar, nav bar and captions
// Pre-appear animation positions for iOS 7 sliding
if (slideAndFade && [self areControlsHidden] && !hidden && animated) {
// Toolbar
_toolbar.frame = CGRectOffset([self frameForToolbarAtOrientation:self.interfaceOrientation], 0, animatonOffset);
// Captions
for (MWZoomingScrollView *page in _visiblePages) {
if (page.captionView) {
MWCaptionView *v = page.captionView;
// Pass any index, all we're interested in is the Y
CGRect captionFrame = [self frameForCaptionView:v atIndex:0];
captionFrame.origin.x = v.frame.origin.x; // Reset X
v.frame = CGRectOffset(captionFrame, 0, animatonOffset);
}
}
}
[UIView animateWithDuration:animationDuration animations:^(void) {
CGFloat alpha = hidden ? 0 : 1;
// Nav bar slides up on it's own on iOS 7
[self.navigationController.navigationBar setAlpha:alpha];
// Toolbar
if (slideAndFade) {
self->_toolbar.frame = [self frameForToolbarAtOrientation:self.interfaceOrientation];
if (hidden) self->_toolbar.frame = CGRectOffset(self->_toolbar.frame, 0, animatonOffset);
}
self->_toolbar.alpha = alpha;
// Captions
for (MWZoomingScrollView *page in self->_visiblePages) {
if (page.captionView) {
MWCaptionView *v = page.captionView;
if (slideAndFade) {
// Pass any index, all we're interested in is the Y
CGRect captionFrame = [self frameForCaptionView:v atIndex:0];
captionFrame.origin.x = v.frame.origin.x; // Reset X
if (hidden) captionFrame = CGRectOffset(captionFrame, 0, animatonOffset);
v.frame = captionFrame;
}
v.alpha = alpha;
}
}
// Selected buttons
for (MWZoomingScrollView *page in self->_visiblePages) {
if (page.selectedButton) {
UIButton *v = page.selectedButton;
CGRect newFrame = [self frameForSelectedButton:v atIndex:0];
newFrame.origin.x = v.frame.origin.x;
v.frame = newFrame;
}
}
} completion:^(BOOL finished) {}];
// Control hiding timer
// Will cancel existing timer but only begin hiding if
// they are visible
if (!permanent) [self hideControlsAfterDelay];
}
- (BOOL)prefersStatusBarHidden {
if (!_leaveStatusBarAlone) {
return _statusBarShouldBeHidden;
} else {
return [self presentingViewControllerPrefersStatusBarHidden];
}
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return UIStatusBarAnimationSlide;
}
- (void)cancelControlHiding {
// If a timer exists then cancel and release
if (_controlVisibilityTimer) {
[_controlVisibilityTimer invalidate];
_controlVisibilityTimer = nil;
}
}
// Enable/disable control visiblity timer
- (void)hideControlsAfterDelay {
if (![self areControlsHidden]) {
[self cancelControlHiding];
_controlVisibilityTimer = [NSTimer scheduledTimerWithTimeInterval:self.delayToHideElements target:self selector:@selector(hideControls) userInfo:nil repeats:NO];
}
}
- (BOOL)areControlsHidden { return (_toolbar.alpha == 0); }
- (void)hideControls { [self setControlsHidden:YES animated:YES permanent:NO]; }
- (void)toggleControls { [self setControlsHidden:![self areControlsHidden] animated:YES permanent:NO]; }
#pragma mark - Properties
// Handle depreciated method
- (void)setInitialPageIndex:(NSUInteger)index {
[self setCurrentPhotoIndex:index];
}
- (void)setCurrentPhotoIndex:(NSUInteger)index {
// Validate
NSUInteger photoCount = [self numberOfPhotos];
if (photoCount == 0) {
index = 0;
} else {
if (index >= photoCount)
index = [self numberOfPhotos]-1;
}
_currentPageIndex = index;
if ([self isViewLoaded]) {
[self jumpToPageAtIndex:index animated:NO];
if (!_viewIsActive)
[self tilePages]; // Force tiling if view is not visible
}
}
#pragma mark - Misc
- (void)doneButtonPressed:(id)sender {
// Only if we're modal and there's a done button
if (_doneButton) {
// See if we actually just want to show/hide grid
if (self.enableGrid) {
if (self.startOnGrid && !_gridController) {
[self showGrid:YES];
return;
} else if (!self.startOnGrid && _gridController) {
[self hideGrid];
return;
}
}
// Dismiss view controller
if ([_delegate respondsToSelector:@selector(photoBrowserDidFinishModalPresentation:)]) {
// Call delegate method and let them dismiss us
[_delegate photoBrowserDidFinishModalPresentation:self];
} else {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
}
#pragma mark - Actions
- (void)actionButtonPressed:(id)sender {
if (_actionsSheet) {
// Dismiss
[_actionsSheet dismissWithClickedButtonIndex:_actionsSheet.cancelButtonIndex animated:YES];
} else {
// Only react when image has loaded
id photo = [self photoAtIndex:_currentPageIndex];
if ([self numberOfPhotos] > 0 && [photo underlyingImage]) {
// If they have defined a delegate method then just message them
if ([self.delegate respondsToSelector:@selector(photoBrowser:actionButtonPressedForPhotoAtIndex:)]) {
// Let delegate handle things
[self.delegate photoBrowser:self actionButtonPressedForPhotoAtIndex:_currentPageIndex];
} else {
// Handle default actions
if (SYSTEM_VERSION_LESS_THAN(@"6")) {
// Old handling of activities with action sheet
if ([MFMailComposeViewController canSendMail]) {
_actionsSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self
cancelButtonTitle:NSLocalizedString(@"Cancel", nil) destructiveButtonTitle:nil
otherButtonTitles:NSLocalizedString(@"Save", nil), NSLocalizedString(@"Copy", nil), NSLocalizedString(@"Email", nil), nil];
} else {
_actionsSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self
cancelButtonTitle:NSLocalizedString(@"Cancel", nil) destructiveButtonTitle:nil
otherButtonTitles:NSLocalizedString(@"Save", nil), NSLocalizedString(@"Copy", nil), nil];
}
_actionsSheet.tag = ACTION_SHEET_OLD_ACTIONS;
_actionsSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[_actionsSheet showFromBarButtonItem:sender animated:YES];
} else {
[_actionsSheet showInView:self.view];
}
} else {
// Show activity view controller
NSMutableArray *items = [NSMutableArray arrayWithObject:[photo underlyingImage]];
if (photo.caption) {
[items addObject:photo.caption];
}
self.activityViewController = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
// Show loading spinner after a couple of seconds
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if (self.activityViewController) {
[self showProgressHUDWithMessage:nil];
}
});
// Show
typeof(self) __weak weakSelf = self;
[self.activityViewController setCompletionHandler:^(NSString *activityType, BOOL completed) {
weakSelf.activityViewController = nil;
[weakSelf hideControlsAfterDelay];
[weakSelf hideProgressHUD:YES];
}];
// iOS 8 - Set the Anchor Point for the popover
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8")) {
self.activityViewController.popoverPresentationController.barButtonItem = _actionButton;
}
[self presentViewController:self.activityViewController animated:YES completion:nil];
}
}
// Keep controls hidden
[self setControlsHidden:NO animated:YES permanent:YES];
}
}
}
#pragma mark - Action Sheet Delegate
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (actionSheet.tag == ACTION_SHEET_OLD_ACTIONS) {
// Old Actions
_actionsSheet = nil;
if (buttonIndex != actionSheet.cancelButtonIndex) {
if (buttonIndex == actionSheet.firstOtherButtonIndex) {
[self savePhoto]; return;
} else if (buttonIndex == actionSheet.firstOtherButtonIndex + 1) {
[self copyPhoto]; return;
} else if (buttonIndex == actionSheet.firstOtherButtonIndex + 2) {
[self emailPhoto]; return;
}
}
}
[self hideControlsAfterDelay]; // Continue as normal...
}
#pragma mark - Action Progress
- (MBProgressHUD *)progressHUD {
if (!_progressHUD) {
_progressHUD = [[MBProgressHUD alloc] initWithView:self.view];
_progressHUD.minSize = CGSizeMake(120, 120);
_progressHUD.minShowTime = 1;
// The sample image is based on the
// work by: http://www.pixelpressicons.com
// licence: http://creativecommons.org/licenses/by/2.5/ca/
self.progressHUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MWPhotoBrowser.bundle/images/Checkmark.png"]];
[self.view addSubview:_progressHUD];
}
return _progressHUD;
}
- (void)showProgressHUDWithMessage:(NSString *)message {
self.progressHUD.labelText = message;
self.progressHUD.mode = MBProgressHUDModeIndeterminate;
[self.progressHUD show:YES];
self.navigationController.navigationBar.userInteractionEnabled = NO;
}
- (void)hideProgressHUD:(BOOL)animated {
[self.progressHUD hide:animated];
self.navigationController.navigationBar.userInteractionEnabled = YES;
}
- (void)showProgressHUDCompleteMessage:(NSString *)message {
if (message) {
if (self.progressHUD.isHidden) [self.progressHUD show:YES];
self.progressHUD.labelText = message;
self.progressHUD.mode = MBProgressHUDModeCustomView;
[self.progressHUD hide:YES afterDelay:1.5];
} else {
[self.progressHUD hide:YES];
}
self.navigationController.navigationBar.userInteractionEnabled = YES;
}
#pragma mark - Actions
- (void)savePhoto {
id photo = [self photoAtIndex:_currentPageIndex];
if ([photo underlyingImage]) {
[self showProgressHUDWithMessage:[NSString stringWithFormat:@"%@\u2026" , NSLocalizedString(@"Saving", @"Displayed with ellipsis as 'Saving...' when an item is in the process of being saved")]];
[self performSelector:@selector(actuallySavePhoto:) withObject:photo afterDelay:0];
}
}
- (void)actuallySavePhoto:(id)photo {
if ([photo underlyingImage]) {
UIImageWriteToSavedPhotosAlbum([photo underlyingImage], self,
@selector(image:didFinishSavingWithError:contextInfo:), nil);
}
}
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
[self showProgressHUDCompleteMessage: error ? NSLocalizedString(@"Failed", @"Informing the user a process has failed") : NSLocalizedString(@"Saved", @"Informing the user an item has been saved")];
[self hideControlsAfterDelay]; // Continue as normal...
}
- (void)copyPhoto {
id photo = [self photoAtIndex:_currentPageIndex];
if ([photo underlyingImage]) {
[self showProgressHUDWithMessage:[NSString stringWithFormat:@"%@\u2026" , NSLocalizedString(@"Copying", @"Displayed with ellipsis as 'Copying...' when an item is in the process of being copied")]];
[self performSelector:@selector(actuallyCopyPhoto:) withObject:photo afterDelay:0];
}
}
- (void)actuallyCopyPhoto:(id)photo {
if ([photo underlyingImage]) {
[[UIPasteboard generalPasteboard] setData:UIImagePNGRepresentation([photo underlyingImage])
forPasteboardType:@"public.png"];
[self showProgressHUDCompleteMessage:NSLocalizedString(@"Copied", @"Informing the user an item has finished copying")];
[self hideControlsAfterDelay]; // Continue as normal...
}
}
- (void)emailPhoto {
id photo = [self photoAtIndex:_currentPageIndex];
if ([photo underlyingImage]) {
[self showProgressHUDWithMessage:[NSString stringWithFormat:@"%@\u2026" , NSLocalizedString(@"Preparing", @"Displayed with ellipsis as 'Preparing...' when an item is in the process of being prepared")]];
[self performSelector:@selector(actuallyEmailPhoto:) withObject:photo afterDelay:0];
}
}
- (void)actuallyEmailPhoto:(id)photo {
if ([photo underlyingImage]) {
MFMailComposeViewController *emailer = [[MFMailComposeViewController alloc] init];
emailer.mailComposeDelegate = self;
[emailer setSubject:NSLocalizedString(@"Photo", nil)];
[emailer addAttachmentData:UIImagePNGRepresentation([photo underlyingImage]) mimeType:@"png" fileName:@"Photo.png"];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
emailer.modalPresentationStyle = UIModalPresentationPageSheet;
}
[self presentViewController:emailer animated:YES completion:nil];
[self hideProgressHUD:NO];
}
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
if (result == MFMailComposeResultFailed) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Email", nil)
message:NSLocalizedString(@"Email failed to send. Please try again.", nil)
delegate:nil cancelButtonTitle:NSLocalizedString(@"Dismiss", nil) otherButtonTitles:nil];
[alert show];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWPhotoBrowserPrivate.h
================================================
//
// MWPhotoBrowser_Private.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 08/10/2013.
//
//
#import
#import "MBProgressHUD.h"
#import "MWGridViewController.h"
#import "MWZoomingScrollView.h"
// Declare private methods of browser
@interface MWPhotoBrowser () {
// Data
NSUInteger _photoCount;
NSMutableArray *_photos;
NSMutableArray *_thumbPhotos;
NSArray *_depreciatedPhotoData; // Depreciated
// Views
UIScrollView *_pagingScrollView;
// Paging & layout
NSMutableSet *_visiblePages, *_recycledPages;
NSUInteger _currentPageIndex;
NSUInteger _previousPageIndex;
CGRect _previousLayoutBounds;
NSUInteger _pageIndexBeforeRotation;
// Navigation & controls
UIToolbar *_toolbar;
NSTimer *_controlVisibilityTimer;
UIBarButtonItem *_previousButton, *_nextButton, *_actionButton, *_doneButton;
MBProgressHUD *_progressHUD;
UIActionSheet *_actionsSheet;
// Grid
MWGridViewController *_gridController;
UIBarButtonItem *_gridPreviousLeftNavItem;
UIBarButtonItem *_gridPreviousRightNavItem;
// Appearance
BOOL _previousNavBarHidden;
BOOL _previousNavBarTranslucent;
UIBarStyle _previousNavBarStyle;
UIStatusBarStyle _previousStatusBarStyle;
UIColor *_previousNavBarTintColor;
UIColor *_previousNavBarBarTintColor;
UIBarButtonItem *_previousViewControllerBackButton;
UIImage *_previousNavigationBarBackgroundImageDefault;
UIImage *_previousNavigationBarBackgroundImageLandscapePhone;
// Misc
BOOL _hasBelongedToViewController;
BOOL _isVCBasedStatusBarAppearance;
BOOL _statusBarShouldBeHidden;
BOOL _displayActionButton;
BOOL _leaveStatusBarAlone;
BOOL _performingLayout;
BOOL _rotating;
BOOL _viewIsActive; // active as in it's in the view heirarchy
BOOL _didSavePreviousStateOfNavBar;
BOOL _skipNextPagingScrollViewPositioning;
BOOL _viewHasAppearedInitially;
CGPoint _currentGridContentOffset;
}
// Properties
@property (nonatomic) UIActivityViewController *activityViewController;
// Layout
- (void)layoutVisiblePages;
- (void)performLayout;
- (BOOL)presentingViewControllerPrefersStatusBarHidden;
// Nav Bar Appearance
- (void)setNavBarAppearance:(BOOL)animated;
- (void)storePreviousNavBarAppearance;
- (void)restorePreviousNavBarAppearance:(BOOL)animated;
// Paging
- (void)tilePages;
- (BOOL)isDisplayingPageForIndex:(NSUInteger)index;
- (MWZoomingScrollView *)pageDisplayedAtIndex:(NSUInteger)index;
- (MWZoomingScrollView *)pageDisplayingPhoto:(id)photo;
- (MWZoomingScrollView *)dequeueRecycledPage;
- (void)configurePage:(MWZoomingScrollView *)page forIndex:(NSUInteger)index;
- (void)didStartViewingPageAtIndex:(NSUInteger)index;
// Frames
- (CGRect)frameForPagingScrollView;
- (CGRect)frameForPageAtIndex:(NSUInteger)index;
- (CGSize)contentSizeForPagingScrollView;
- (CGPoint)contentOffsetForPageAtIndex:(NSUInteger)index;
- (CGRect)frameForToolbarAtOrientation:(UIInterfaceOrientation)orientation;
- (CGRect)frameForCaptionView:(MWCaptionView *)captionView atIndex:(NSUInteger)index;
- (CGRect)frameForSelectedButton:(UIButton *)selectedButton atIndex:(NSUInteger)index;
// Navigation
- (void)updateNavigation;
- (void)jumpToPageAtIndex:(NSUInteger)index animated:(BOOL)animated;
- (void)gotoPreviousPage;
- (void)gotoNextPage;
// Grid
- (void)showGrid:(BOOL)animated;
- (void)hideGrid;
// Controls
- (void)cancelControlHiding;
- (void)hideControlsAfterDelay;
- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated permanent:(BOOL)permanent;
- (void)toggleControls;
- (BOOL)areControlsHidden;
// Data
- (NSUInteger)numberOfPhotos;
- (id)photoAtIndex:(NSUInteger)index;
- (id)thumbPhotoAtIndex:(NSUInteger)index;
- (UIImage *)imageForPhoto:(id)photo;
- (BOOL)photoIsSelectedAtIndex:(NSUInteger)index;
- (void)setPhotoSelected:(BOOL)selected atIndex:(NSUInteger)index;
- (void)loadAdjacentPhotosIfNecessary:(id)photo;
- (void)releaseAllUnderlyingPhotos:(BOOL)preserveCurrent;
// Actions
- (void)savePhoto;
- (void)copyPhoto;
- (void)emailPhoto;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWPhotoProtocol.h
================================================
//
// MWPhotoProtocol.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 02/01/2012.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import
// Notifications
#define MWPHOTO_LOADING_DID_END_NOTIFICATION @"MWPHOTO_LOADING_DID_END_NOTIFICATION"
#define MWPHOTO_PROGRESS_NOTIFICATION @"MWPHOTO_PROGRESS_NOTIFICATION"
// If you wish to use your own data models for photo then they must conform
// to this protocol. See instructions for details on each method.
// Otherwise you can use the MWPhoto object or subclass it yourself to
// store more information per photo.
//
// You can see the MWPhoto class for an example implementation of this protocol
//
@protocol MWPhoto
@required
// Return underlying UIImage to be displayed
// Return nil if the image is not immediately available (loaded into memory, preferably
// already decompressed) and needs to be loaded from a source (cache, file, web, etc)
// IMPORTANT: You should *NOT* use this method to initiate
// fetching of images from any external of source. That should be handled
// in -loadUnderlyingImageAndNotify: which may be called by the photo browser if this
// methods returns nil.
@property (nonatomic, strong) UIImage *underlyingImage;
// Called when the browser has determined the underlying images is not
// already loaded into memory but needs it.
- (void)loadUnderlyingImageAndNotify;
// Fetch the image data from a source and notify when complete.
// You must load the image asyncronously (and decompress it for better performance).
// It is recommended that you use SDWebImageDecoder to perform the decompression.
// See MWPhoto object for an example implementation.
// When the underlying UIImage is loaded (or failed to load) you should post the following
// notification:
// [[NSNotificationCenter defaultCenter] postNotificationName:MWPHOTO_LOADING_DID_END_NOTIFICATION
// object:self];
- (void)performLoadUnderlyingImageAndNotify;
// This is called when the photo browser has determined the photo data
// is no longer needed or there are low memory conditions
// You should release any underlying (possibly large and decompressed) image data
// as long as the image can be re-loaded (from cache, file, or URL)
- (void)unloadUnderlyingImage;
@optional
// Return a caption string to be displayed over the image
// Return nil to display no caption
- (NSString *)caption;
// Cancel any background loading of image data
- (void)cancelAnyLoading;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWTapDetectingImageView.h
================================================
//
// UIImageViewTap.h
// Momento
//
// Created by Michael Waterfall on 04/11/2009.
// Copyright 2009 d3i. All rights reserved.
//
#import
@protocol MWTapDetectingImageViewDelegate;
@interface MWTapDetectingImageView : UIImageView {}
@property (nonatomic, weak) id tapDelegate;
@end
@protocol MWTapDetectingImageViewDelegate
@optional
- (void)imageView:(UIImageView *)imageView singleTapDetected:(UITouch *)touch;
- (void)imageView:(UIImageView *)imageView doubleTapDetected:(UITouch *)touch;
- (void)imageView:(UIImageView *)imageView tripleTapDetected:(UITouch *)touch;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWTapDetectingImageView.m
================================================
//
// UIImageViewTap.m
// Momento
//
// Created by Michael Waterfall on 04/11/2009.
// Copyright 2009 d3i. All rights reserved.
//
#import "MWTapDetectingImageView.h"
@implementation MWTapDetectingImageView
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.userInteractionEnabled = YES;
}
return self;
}
- (id)initWithImage:(UIImage *)image {
if ((self = [super initWithImage:image])) {
self.userInteractionEnabled = YES;
}
return self;
}
- (id)initWithImage:(UIImage *)image highlightedImage:(UIImage *)highlightedImage {
if ((self = [super initWithImage:image highlightedImage:highlightedImage])) {
self.userInteractionEnabled = YES;
}
return self;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSUInteger tapCount = touch.tapCount;
switch (tapCount) {
case 1:
[self handleSingleTap:touch];
break;
case 2:
[self handleDoubleTap:touch];
break;
case 3:
[self handleTripleTap:touch];
break;
default:
break;
}
[[self nextResponder] touchesEnded:touches withEvent:event];
}
- (void)handleSingleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(imageView:singleTapDetected:)])
[_tapDelegate imageView:self singleTapDetected:touch];
}
- (void)handleDoubleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(imageView:doubleTapDetected:)])
[_tapDelegate imageView:self doubleTapDetected:touch];
}
- (void)handleTripleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(imageView:tripleTapDetected:)])
[_tapDelegate imageView:self tripleTapDetected:touch];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWTapDetectingView.h
================================================
//
// UIViewTap.h
// Momento
//
// Created by Michael Waterfall on 04/11/2009.
// Copyright 2009 d3i. All rights reserved.
//
#import
#import
@protocol MWTapDetectingViewDelegate;
@interface MWTapDetectingView : UIView {}
@property (nonatomic, weak) id tapDelegate;
@end
@protocol MWTapDetectingViewDelegate
@optional
- (void)view:(UIView *)view singleTapDetected:(UITouch *)touch;
- (void)view:(UIView *)view doubleTapDetected:(UITouch *)touch;
- (void)view:(UIView *)view tripleTapDetected:(UITouch *)touch;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWTapDetectingView.m
================================================
//
// UIViewTap.m
// Momento
//
// Created by Michael Waterfall on 04/11/2009.
// Copyright 2009 d3i. All rights reserved.
//
#import "MWTapDetectingView.h"
@implementation MWTapDetectingView
- (id)init {
if ((self = [super init])) {
self.userInteractionEnabled = YES;
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.userInteractionEnabled = YES;
}
return self;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSUInteger tapCount = touch.tapCount;
switch (tapCount) {
case 1:
[self handleSingleTap:touch];
break;
case 2:
[self handleDoubleTap:touch];
break;
case 3:
[self handleTripleTap:touch];
break;
default:
break;
}
[[self nextResponder] touchesEnded:touches withEvent:event];
}
- (void)handleSingleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(view:singleTapDetected:)])
[_tapDelegate view:self singleTapDetected:touch];
}
- (void)handleDoubleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(view:doubleTapDetected:)])
[_tapDelegate view:self doubleTapDetected:touch];
}
- (void)handleTripleTap:(UITouch *)touch {
if ([_tapDelegate respondsToSelector:@selector(view:tripleTapDetected:)])
[_tapDelegate view:self tripleTapDetected:touch];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWZoomingScrollView.h
================================================
//
// ZoomingScrollView.h
// MWPhotoBrowser
//
// Created by Michael Waterfall on 14/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import
#import "MWPhotoProtocol.h"
#import "MWTapDetectingImageView.h"
#import "MWTapDetectingView.h"
@class MWPhotoBrowser, MWPhoto, MWCaptionView;
@interface MWZoomingScrollView : UIScrollView {
}
@property () NSUInteger index;
@property (nonatomic) id photo;
@property (nonatomic, weak) MWCaptionView *captionView;
@property (nonatomic, weak) UIButton *selectedButton;
- (id)initWithPhotoBrowser:(MWPhotoBrowser *)browser;
- (void)displayImage;
- (void)displayImageFailure;
- (void)setMaxMinZoomScalesForCurrentBounds;
- (void)prepareForReuse;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Classes/MWZoomingScrollView.m
================================================
//
// ZoomingScrollView.m
// MWPhotoBrowser
//
// Created by Michael Waterfall on 14/10/2010.
// Copyright 2010 d3i. All rights reserved.
//
#import "MWCommon.h"
#import "MWZoomingScrollView.h"
#import "MWPhotoBrowser.h"
#import "MWPhoto.h"
#import "DACircularProgressView.h"
#import "MWPhotoBrowserPrivate.h"
// Private methods and properties
@interface MWZoomingScrollView () {
MWPhotoBrowser __weak *_photoBrowser;
MWTapDetectingView *_tapView; // for background taps
MWTapDetectingImageView *_photoImageView;
DACircularProgressView *_loadingIndicator;
UIImageView *_loadingError;
}
@end
@implementation MWZoomingScrollView
- (id)initWithPhotoBrowser:(MWPhotoBrowser *)browser {
if ((self = [super init])) {
// Setup
_index = NSUIntegerMax;
_photoBrowser = browser;
// Tap view for background
_tapView = [[MWTapDetectingView alloc] initWithFrame:self.bounds];
_tapView.tapDelegate = self;
_tapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_tapView.backgroundColor = [UIColor blackColor];
[self addSubview:_tapView];
// Image view
_photoImageView = [[MWTapDetectingImageView alloc] initWithFrame:CGRectZero];
_photoImageView.tapDelegate = self;
_photoImageView.contentMode = UIViewContentModeCenter;
_photoImageView.backgroundColor = [UIColor blackColor];
[self addSubview:_photoImageView];
// Loading indicator
_loadingIndicator = [[DACircularProgressView alloc] initWithFrame:CGRectMake(140.0f, 30.0f, 40.0f, 40.0f)];
_loadingIndicator.userInteractionEnabled = NO;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7")) {
_loadingIndicator.thicknessRatio = 0.1;
_loadingIndicator.roundedCorners = NO;
} else {
_loadingIndicator.thicknessRatio = 0.2;
_loadingIndicator.roundedCorners = YES;
}
_loadingIndicator.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
[self addSubview:_loadingIndicator];
// Listen progress notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(setProgressFromNotification:)
name:MWPHOTO_PROGRESS_NOTIFICATION
object:nil];
// Setup
self.backgroundColor = [UIColor blackColor];
self.delegate = self;
self.showsHorizontalScrollIndicator = NO;
self.showsVerticalScrollIndicator = NO;
self.decelerationRate = UIScrollViewDecelerationRateFast;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)prepareForReuse {
[self hideImageFailure];
self.photo = nil;
self.captionView = nil;
self.selectedButton = nil;
_photoImageView.image = nil;
_index = NSUIntegerMax;
}
#pragma mark - Image
- (void)setPhoto:(id)photo {
// Cancel any loading on old photo
if (_photo && photo == nil) {
if ([_photo respondsToSelector:@selector(cancelAnyLoading)]) {
[_photo cancelAnyLoading];
}
}
_photo = photo;
UIImage *img = [_photoBrowser imageForPhoto:_photo];
if (img) {
[self displayImage];
} else {
// Will be loading so show loading
[self showLoadingIndicator];
}
}
// Get and display image
- (void)displayImage {
if (_photo && _photoImageView.image == nil) {
// Reset
self.maximumZoomScale = 1;
self.minimumZoomScale = 1;
self.zoomScale = 1;
self.contentSize = CGSizeMake(0, 0);
// Get image from browser as it handles ordering of fetching
UIImage *img = [_photoBrowser imageForPhoto:_photo];
if (img) {
// Hide indicator
[self hideLoadingIndicator];
// Set image
_photoImageView.image = img;
_photoImageView.hidden = NO;
// Setup photo frame
CGRect photoImageViewFrame;
photoImageViewFrame.origin = CGPointZero;
photoImageViewFrame.size = img.size;
_photoImageView.frame = photoImageViewFrame;
self.contentSize = photoImageViewFrame.size;
// Set zoom to minimum zoom
[self setMaxMinZoomScalesForCurrentBounds];
} else {
// Failed no image
[self displayImageFailure];
}
[self setNeedsLayout];
}
}
// Image failed so just show black!
- (void)displayImageFailure {
[self hideLoadingIndicator];
_photoImageView.image = nil;
if (!_loadingError) {
_loadingError = [UIImageView new];
_loadingError.image = [UIImage imageNamed:@"MWPhotoBrowser.bundle/images/ImageError.png"];
_loadingError.userInteractionEnabled = NO;
_loadingError.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
[_loadingError sizeToFit];
[self addSubview:_loadingError];
}
_loadingError.frame = CGRectMake(floorf((self.bounds.size.width - _loadingError.frame.size.width) / 2.),
floorf((self.bounds.size.height - _loadingError.frame.size.height) / 2),
_loadingError.frame.size.width,
_loadingError.frame.size.height);
}
- (void)hideImageFailure {
if (_loadingError) {
[_loadingError removeFromSuperview];
_loadingError = nil;
}
}
#pragma mark - Loading Progress
- (void)setProgressFromNotification:(NSNotification *)notification {
NSDictionary *dict = [notification object];
id photoWithProgress = [dict objectForKey:@"photo"];
if (photoWithProgress == self.photo) {
float progress = [[dict valueForKey:@"progress"] floatValue];
_loadingIndicator.progress = MAX(MIN(1, progress), 0);
}
}
- (void)hideLoadingIndicator {
_loadingIndicator.hidden = YES;
}
- (void)showLoadingIndicator {
self.zoomScale = 0;
self.minimumZoomScale = 0;
self.maximumZoomScale = 0;
_loadingIndicator.progress = 0;
_loadingIndicator.hidden = NO;
[self hideImageFailure];
}
#pragma mark - Setup
- (CGFloat)initialZoomScaleWithMinScale {
CGFloat zoomScale = self.minimumZoomScale;
if (_photoImageView && _photoBrowser.zoomPhotosToFill) {
// Zoom image to fill if the aspect ratios are fairly similar
CGSize boundsSize = self.bounds.size;
CGSize imageSize = _photoImageView.image.size;
CGFloat boundsAR = boundsSize.width / boundsSize.height;
CGFloat imageAR = imageSize.width / imageSize.height;
CGFloat xScale = boundsSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
CGFloat yScale = boundsSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
// Zooms standard portrait images on a 3.5in screen but not on a 4in screen.
if (ABS(boundsAR - imageAR) < 0.17) {
zoomScale = MAX(xScale, yScale);
// Ensure we don't zoom in or out too far, just in case
zoomScale = MIN(MAX(self.minimumZoomScale, zoomScale), self.maximumZoomScale);
}
}
return zoomScale;
}
- (void)setMaxMinZoomScalesForCurrentBounds {
// Reset
self.maximumZoomScale = 1;
self.minimumZoomScale = 1;
self.zoomScale = 1;
// Bail if no image
if (_photoImageView.image == nil) return;
// Reset position
_photoImageView.frame = CGRectMake(0, 0, _photoImageView.frame.size.width, _photoImageView.frame.size.height);
// Sizes
CGSize boundsSize = self.bounds.size;
CGSize imageSize = _photoImageView.image.size;
// Calculate Min
CGFloat xScale = boundsSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
CGFloat yScale = boundsSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
CGFloat minScale = MIN(xScale, yScale); // use minimum of these to allow the image to become fully visible
// Calculate Max
CGFloat maxScale = 3;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// Let them go a bit bigger on a bigger screen!
maxScale = 4;
}
// Image is smaller than screen so no zooming!
if (xScale >= 1 && yScale >= 1) {
minScale = 1.0;
}
// Set min/max zoom
self.maximumZoomScale = maxScale;
self.minimumZoomScale = minScale;
// Initial zoom
self.zoomScale = [self initialZoomScaleWithMinScale];
// If we're zooming to fill then centralise
if (self.zoomScale != minScale) {
// Centralise
self.contentOffset = CGPointMake((imageSize.width * self.zoomScale - boundsSize.width) / 2.0,
(imageSize.height * self.zoomScale - boundsSize.height) / 2.0);
// Disable scrolling initially until the first pinch to fix issues with swiping on an initally zoomed in photo
self.scrollEnabled = NO;
}
// Layout
[self setNeedsLayout];
}
#pragma mark - Layout
- (void)layoutSubviews {
// Update tap view frame
_tapView.frame = self.bounds;
// Position indicators (centre does not seem to work!)
if (!_loadingIndicator.hidden)
_loadingIndicator.frame = CGRectMake(floorf((self.bounds.size.width - _loadingIndicator.frame.size.width) / 2.),
floorf((self.bounds.size.height - _loadingIndicator.frame.size.height) / 2),
_loadingIndicator.frame.size.width,
_loadingIndicator.frame.size.height);
if (_loadingError)
_loadingError.frame = CGRectMake(floorf((self.bounds.size.width - _loadingError.frame.size.width) / 2.),
floorf((self.bounds.size.height - _loadingError.frame.size.height) / 2),
_loadingError.frame.size.width,
_loadingError.frame.size.height);
// Super
[super layoutSubviews];
// Center the image as it becomes smaller than the size of the screen
CGSize boundsSize = self.bounds.size;
CGRect frameToCenter = _photoImageView.frame;
// Horizontally
if (frameToCenter.size.width < boundsSize.width) {
frameToCenter.origin.x = floorf((boundsSize.width - frameToCenter.size.width) / 2.0);
} else {
frameToCenter.origin.x = 0;
}
// Vertically
if (frameToCenter.size.height < boundsSize.height) {
frameToCenter.origin.y = floorf((boundsSize.height - frameToCenter.size.height) / 2.0);
} else {
frameToCenter.origin.y = 0;
}
// Center
if (!CGRectEqualToRect(_photoImageView.frame, frameToCenter))
_photoImageView.frame = frameToCenter;
}
#pragma mark - UIScrollViewDelegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _photoImageView;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[_photoBrowser cancelControlHiding];
}
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
self.scrollEnabled = YES; // reset
[_photoBrowser cancelControlHiding];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
[_photoBrowser hideControlsAfterDelay];
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
[self setNeedsLayout];
[self layoutIfNeeded];
}
#pragma mark - Tap Detection
- (void)handleSingleTap:(CGPoint)touchPoint {
[_photoBrowser performSelector:@selector(toggleControls) withObject:nil afterDelay:0.2];
}
- (void)handleDoubleTap:(CGPoint)touchPoint {
// Cancel any single tap handling
[NSObject cancelPreviousPerformRequestsWithTarget:_photoBrowser];
// Zoom
if (self.zoomScale != self.minimumZoomScale && self.zoomScale != [self initialZoomScaleWithMinScale]) {
// Zoom out
[self setZoomScale:self.minimumZoomScale animated:YES];
} else {
// Zoom in to twice the size
CGFloat newZoomScale = ((self.maximumZoomScale + self.minimumZoomScale) / 2);
CGFloat xsize = self.bounds.size.width / newZoomScale;
CGFloat ysize = self.bounds.size.height / newZoomScale;
[self zoomToRect:CGRectMake(touchPoint.x - xsize/2, touchPoint.y - ysize/2, xsize, ysize) animated:YES];
}
// Delay controls
[_photoBrowser hideControlsAfterDelay];
}
// Image View
- (void)imageView:(UIImageView *)imageView singleTapDetected:(UITouch *)touch {
[self handleSingleTap:[touch locationInView:imageView]];
}
- (void)imageView:(UIImageView *)imageView doubleTapDetected:(UITouch *)touch {
[self handleDoubleTap:[touch locationInView:imageView]];
}
// Background View
- (void)view:(UIView *)view singleTapDetected:(UITouch *)touch {
// Translate touch location to image view location
CGFloat touchX = [touch locationInView:view].x;
CGFloat touchY = [touch locationInView:view].y;
touchX *= 1/self.zoomScale;
touchY *= 1/self.zoomScale;
touchX += self.contentOffset.x;
touchY += self.contentOffset.y;
[self handleSingleTap:CGPointMake(touchX, touchY)];
}
- (void)view:(UIView *)view doubleTapDetected:(UITouch *)touch {
// Translate touch location to image view location
CGFloat touchX = [touch locationInView:view].x;
CGFloat touchY = [touch locationInView:view].y;
touchX *= 1/self.zoomScale;
touchY *= 1/self.zoomScale;
touchX += self.contentOffset.x;
touchY += self.contentOffset.y;
[self handleDoubleTap:CGPointMake(touchX, touchY)];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/.svn/all-wcprops
================================================
K 25
svn:wc:ra_dav:version-url
V 149
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/.svn/entries
================================================
10
dir
4265
http://bishanwen@121.43.163.28:9090/zhuku/repo/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries
http://bishanwen@121.43.163.28:9090/zhuku/repo
2017-12-28T02:01:09.072372Z
4261
zhoujun
5738001b-f13b-4815-ac0f-2e2f54dd1d22
EMSDWebImage
dir
PSTCollectionView
dir
DACircularProgress
dir
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/DACircularProgress/.svn/all-wcprops
================================================
K 25
svn:wc:ra_dav:version-url
V 168
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/DACircularProgress
END
DACircularProgressView.h
K 25
svn:wc:ra_dav:version-url
V 193
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/DACircularProgress/DACircularProgressView.h
END
DACircularProgressView.m
K 25
svn:wc:ra_dav:version-url
V 193
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/DACircularProgress/DACircularProgressView.m
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/DACircularProgress/.svn/entries
================================================
10
dir
4265
http://bishanwen@121.43.163.28:9090/zhuku/repo/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/DACircularProgress
http://bishanwen@121.43.163.28:9090/zhuku/repo
2017-12-28T02:01:09.072372Z
4261
zhoujun
5738001b-f13b-4815-ac0f-2e2f54dd1d22
DACircularProgressView.h
file
2018-01-16T02:17:09.000000Z
11fde9ce062931460406eb79d8a6329d
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
883
DACircularProgressView.m
file
2018-01-16T02:17:09.000000Z
6ef3449f302133a42a4faf8d293dda3d
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
7307
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/DACircularProgress/.svn/prop-base/DACircularProgressView.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/DACircularProgress/.svn/prop-base/DACircularProgressView.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/DACircularProgress/.svn/text-base/DACircularProgressView.h.svn-base
================================================
//
// DACircularProgressView.h
// DACircularProgress
//
// Created by Daniel Amitay on 2/6/12.
// Copyright (c) 2012 Daniel Amitay. All rights reserved.
//
#import
@interface DACircularProgressView : UIView
@property(nonatomic, strong) UIColor *trackTintColor UI_APPEARANCE_SELECTOR;
@property(nonatomic, strong) UIColor *progressTintColor UI_APPEARANCE_SELECTOR;
@property(nonatomic) NSInteger roundedCorners UI_APPEARANCE_SELECTOR; // Can not use BOOL with UI_APPEARANCE_SELECTOR :-(
@property(nonatomic) CGFloat thicknessRatio UI_APPEARANCE_SELECTOR;
@property(nonatomic) CGFloat progress;
@property(nonatomic) CGFloat indeterminateDuration UI_APPEARANCE_SELECTOR;
@property(nonatomic) NSInteger indeterminate UI_APPEARANCE_SELECTOR; // Can not use BOOL with UI_APPEARANCE_SELECTOR :-(
- (void)setProgress:(CGFloat)progress animated:(BOOL)animated;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/DACircularProgress/.svn/text-base/DACircularProgressView.m.svn-base
================================================
//
// DACircularProgressView.m
// DACircularProgress
//
// Created by Daniel Amitay on 2/6/12.
// Copyright (c) 2012 Daniel Amitay. All rights reserved.
//
#import "DACircularProgressView.h"
#import
@interface DACircularProgressLayer : CALayer
@property(nonatomic, strong) UIColor *trackTintColor;
@property(nonatomic, strong) UIColor *progressTintColor;
@property(nonatomic) NSInteger roundedCorners;
@property(nonatomic) CGFloat thicknessRatio;
@property(nonatomic) CGFloat progress;
@end
@implementation DACircularProgressLayer
@dynamic trackTintColor;
@dynamic progressTintColor;
@dynamic roundedCorners;
@dynamic thicknessRatio;
@dynamic progress;
+ (BOOL)needsDisplayForKey:(NSString *)key
{
return [key isEqualToString:@"progress"] ? YES : [super needsDisplayForKey:key];
}
- (void)drawInContext:(CGContextRef)context
{
CGRect rect = self.bounds;
CGPoint centerPoint = CGPointMake(rect.size.height / 2.0f, rect.size.width / 2.0f);
CGFloat radius = MIN(rect.size.height, rect.size.width) / 2.0f;
CGFloat progress = MIN(self.progress, 1.0f - FLT_EPSILON);
CGFloat radians = (progress * 2.0f * M_PI) - M_PI_2;
CGContextSetFillColorWithColor(context, self.trackTintColor.CGColor);
CGMutablePathRef trackPath = CGPathCreateMutable();
CGPathMoveToPoint(trackPath, NULL, centerPoint.x, centerPoint.y);
CGPathAddArc(trackPath, NULL, centerPoint.x, centerPoint.y, radius, 3.0f * M_PI_2, -M_PI_2, NO);
CGPathCloseSubpath(trackPath);
CGContextAddPath(context, trackPath);
CGContextFillPath(context);
CGPathRelease(trackPath);
if (progress > 0.0f)
{
CGContextSetFillColorWithColor(context, self.progressTintColor.CGColor);
CGMutablePathRef progressPath = CGPathCreateMutable();
CGPathMoveToPoint(progressPath, NULL, centerPoint.x, centerPoint.y);
CGPathAddArc(progressPath, NULL, centerPoint.x, centerPoint.y, radius, 3.0f * M_PI_2, radians, NO);
CGPathCloseSubpath(progressPath);
CGContextAddPath(context, progressPath);
CGContextFillPath(context);
CGPathRelease(progressPath);
}
if (progress > 0.0f && self.roundedCorners)
{
CGFloat pathWidth = radius * self.thicknessRatio;
CGFloat xOffset = radius * (1.0f + ((1.0f - (self.thicknessRatio / 2.0f)) * cosf(radians)));
CGFloat yOffset = radius * (1.0f + ((1.0f - (self.thicknessRatio / 2.0f)) * sinf(radians)));
CGPoint endPoint = CGPointMake(xOffset, yOffset);
CGContextAddEllipseInRect(context, CGRectMake(centerPoint.x - pathWidth / 2.0f, 0.0f, pathWidth, pathWidth));
CGContextFillPath(context);
CGContextAddEllipseInRect(context, CGRectMake(endPoint.x - pathWidth / 2.0f, endPoint.y - pathWidth / 2.0f, pathWidth, pathWidth));
CGContextFillPath(context);
}
CGContextSetBlendMode(context, kCGBlendModeClear);
CGFloat innerRadius = radius * (1.0f - self.thicknessRatio);
CGPoint newCenterPoint = CGPointMake(centerPoint.x - innerRadius, centerPoint.y - innerRadius);
CGContextAddEllipseInRect(context, CGRectMake(newCenterPoint.x, newCenterPoint.y, innerRadius * 2.0f, innerRadius * 2.0f));
CGContextFillPath(context);
}
@end
@interface DACircularProgressView ()
@end
@implementation DACircularProgressView
+ (void) initialize
{
if (self != [DACircularProgressView class])
return;
id appearance = [self appearance];
[appearance setTrackTintColor:[[UIColor whiteColor] colorWithAlphaComponent:0.3f]];
[appearance setProgressTintColor:[UIColor whiteColor]];
[appearance setBackgroundColor:[UIColor clearColor]];
[appearance setThicknessRatio:0.3f];
[appearance setRoundedCorners:NO];
[appearance setIndeterminateDuration:2.0f];
[appearance setIndeterminate:NO];
}
+ (Class)layerClass
{
return [DACircularProgressLayer class];
}
- (DACircularProgressLayer *)circularProgressLayer
{
return (DACircularProgressLayer *)self.layer;
}
- (id)init
{
return [super initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 40.0f)];
}
- (void)didMoveToWindow
{
CGFloat windowContentsScale = self.window.screen.scale;
self.circularProgressLayer.contentsScale = windowContentsScale;
}
#pragma mark - Progress
- (CGFloat)progress
{
return self.circularProgressLayer.progress;
}
- (void)setProgress:(CGFloat)progress
{
[self setProgress:progress animated:NO];
}
- (void)setProgress:(CGFloat)progress animated:(BOOL)animated
{
[self.layer removeAnimationForKey:@"indeterminateAnimation"];
[self.circularProgressLayer removeAnimationForKey:@"progress"];
CGFloat pinnedProgress = MIN(MAX(progress, 0.0f), 1.0f);
if (animated)
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"progress"];
animation.duration = fabs(self.progress - pinnedProgress); // Same duration as UIProgressView animation
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.fromValue = [NSNumber numberWithFloat:self.progress];
animation.toValue = [NSNumber numberWithFloat:pinnedProgress];
[self.circularProgressLayer addAnimation:animation forKey:@"progress"];
}
else
{
[self.circularProgressLayer setNeedsDisplay];
}
self.circularProgressLayer.progress = pinnedProgress;
}
#pragma mark - UIAppearance methods
- (UIColor *)trackTintColor
{
return self.circularProgressLayer.trackTintColor;
}
- (void)setTrackTintColor:(UIColor *)trackTintColor
{
self.circularProgressLayer.trackTintColor = trackTintColor;
[self.circularProgressLayer setNeedsDisplay];
}
- (UIColor *)progressTintColor
{
return self.circularProgressLayer.progressTintColor;
}
- (void)setProgressTintColor:(UIColor *)progressTintColor
{
self.circularProgressLayer.progressTintColor = progressTintColor;
[self.circularProgressLayer setNeedsDisplay];
}
- (NSInteger)roundedCorners
{
return self.roundedCorners;
}
- (void)setRoundedCorners:(NSInteger)roundedCorners
{
self.circularProgressLayer.roundedCorners = roundedCorners;
[self.circularProgressLayer setNeedsDisplay];
}
- (CGFloat)thicknessRatio
{
return self.circularProgressLayer.thicknessRatio;
}
- (void)setThicknessRatio:(CGFloat)thicknessRatio
{
self.circularProgressLayer.thicknessRatio = MIN(MAX(thicknessRatio, 0.f), 1.f);
[self.circularProgressLayer setNeedsDisplay];
}
- (NSInteger)indeterminate
{
CAAnimation *spinAnimation = [self.layer animationForKey:@"indeterminateAnimation"];
return (spinAnimation == nil ? 0 : 1);
}
- (void)setIndeterminate:(NSInteger)indeterminate
{
if (indeterminate && !self.indeterminate)
{
CABasicAnimation *spinAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
spinAnimation.byValue = [NSNumber numberWithFloat:indeterminate > 0 ? 2.0f*M_PI : -2.0f*M_PI];
spinAnimation.duration = self.indeterminateDuration;
spinAnimation.repeatCount = HUGE_VALF;
[self.layer addAnimation:spinAnimation forKey:@"indeterminateAnimation"];
}
else
{
[self.layer removeAnimationForKey:@"indeterminateAnimation"];
}
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/DACircularProgress/DACircularProgressView.h
================================================
//
// DACircularProgressView.h
// DACircularProgress
//
// Created by Daniel Amitay on 2/6/12.
// Copyright (c) 2012 Daniel Amitay. All rights reserved.
//
#import
@interface DACircularProgressView : UIView
@property(nonatomic, strong) UIColor *trackTintColor UI_APPEARANCE_SELECTOR;
@property(nonatomic, strong) UIColor *progressTintColor UI_APPEARANCE_SELECTOR;
@property(nonatomic) NSInteger roundedCorners UI_APPEARANCE_SELECTOR; // Can not use BOOL with UI_APPEARANCE_SELECTOR :-(
@property(nonatomic) CGFloat thicknessRatio UI_APPEARANCE_SELECTOR;
@property(nonatomic) CGFloat progress;
@property(nonatomic) CGFloat indeterminateDuration UI_APPEARANCE_SELECTOR;
@property(nonatomic) NSInteger indeterminate UI_APPEARANCE_SELECTOR; // Can not use BOOL with UI_APPEARANCE_SELECTOR :-(
- (void)setProgress:(CGFloat)progress animated:(BOOL)animated;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/DACircularProgress/DACircularProgressView.m
================================================
//
// DACircularProgressView.m
// DACircularProgress
//
// Created by Daniel Amitay on 2/6/12.
// Copyright (c) 2012 Daniel Amitay. All rights reserved.
//
#import "DACircularProgressView.h"
#import
@interface DACircularProgressLayer : CALayer
@property(nonatomic, strong) UIColor *trackTintColor;
@property(nonatomic, strong) UIColor *progressTintColor;
@property(nonatomic) NSInteger roundedCorners;
@property(nonatomic) CGFloat thicknessRatio;
@property(nonatomic) CGFloat progress;
@end
@implementation DACircularProgressLayer
@dynamic trackTintColor;
@dynamic progressTintColor;
@dynamic roundedCorners;
@dynamic thicknessRatio;
@dynamic progress;
+ (BOOL)needsDisplayForKey:(NSString *)key
{
return [key isEqualToString:@"progress"] ? YES : [super needsDisplayForKey:key];
}
- (void)drawInContext:(CGContextRef)context
{
CGRect rect = self.bounds;
CGPoint centerPoint = CGPointMake(rect.size.height / 2.0f, rect.size.width / 2.0f);
CGFloat radius = MIN(rect.size.height, rect.size.width) / 2.0f;
CGFloat progress = MIN(self.progress, 1.0f - FLT_EPSILON);
CGFloat radians = (progress * 2.0f * M_PI) - M_PI_2;
CGContextSetFillColorWithColor(context, self.trackTintColor.CGColor);
CGMutablePathRef trackPath = CGPathCreateMutable();
CGPathMoveToPoint(trackPath, NULL, centerPoint.x, centerPoint.y);
CGPathAddArc(trackPath, NULL, centerPoint.x, centerPoint.y, radius, 3.0f * M_PI_2, -M_PI_2, NO);
CGPathCloseSubpath(trackPath);
CGContextAddPath(context, trackPath);
CGContextFillPath(context);
CGPathRelease(trackPath);
if (progress > 0.0f)
{
CGContextSetFillColorWithColor(context, self.progressTintColor.CGColor);
CGMutablePathRef progressPath = CGPathCreateMutable();
CGPathMoveToPoint(progressPath, NULL, centerPoint.x, centerPoint.y);
CGPathAddArc(progressPath, NULL, centerPoint.x, centerPoint.y, radius, 3.0f * M_PI_2, radians, NO);
CGPathCloseSubpath(progressPath);
CGContextAddPath(context, progressPath);
CGContextFillPath(context);
CGPathRelease(progressPath);
}
if (progress > 0.0f && self.roundedCorners)
{
CGFloat pathWidth = radius * self.thicknessRatio;
CGFloat xOffset = radius * (1.0f + ((1.0f - (self.thicknessRatio / 2.0f)) * cosf(radians)));
CGFloat yOffset = radius * (1.0f + ((1.0f - (self.thicknessRatio / 2.0f)) * sinf(radians)));
CGPoint endPoint = CGPointMake(xOffset, yOffset);
CGContextAddEllipseInRect(context, CGRectMake(centerPoint.x - pathWidth / 2.0f, 0.0f, pathWidth, pathWidth));
CGContextFillPath(context);
CGContextAddEllipseInRect(context, CGRectMake(endPoint.x - pathWidth / 2.0f, endPoint.y - pathWidth / 2.0f, pathWidth, pathWidth));
CGContextFillPath(context);
}
CGContextSetBlendMode(context, kCGBlendModeClear);
CGFloat innerRadius = radius * (1.0f - self.thicknessRatio);
CGPoint newCenterPoint = CGPointMake(centerPoint.x - innerRadius, centerPoint.y - innerRadius);
CGContextAddEllipseInRect(context, CGRectMake(newCenterPoint.x, newCenterPoint.y, innerRadius * 2.0f, innerRadius * 2.0f));
CGContextFillPath(context);
}
@end
@interface DACircularProgressView ()
@end
@implementation DACircularProgressView
+ (void) initialize
{
if (self != [DACircularProgressView class])
return;
id appearance = [self appearance];
[appearance setTrackTintColor:[[UIColor whiteColor] colorWithAlphaComponent:0.3f]];
[appearance setProgressTintColor:[UIColor whiteColor]];
[appearance setBackgroundColor:[UIColor clearColor]];
[appearance setThicknessRatio:0.3f];
[appearance setRoundedCorners:NO];
[appearance setIndeterminateDuration:2.0f];
[appearance setIndeterminate:NO];
}
+ (Class)layerClass
{
return [DACircularProgressLayer class];
}
- (DACircularProgressLayer *)circularProgressLayer
{
return (DACircularProgressLayer *)self.layer;
}
- (id)init
{
return [super initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 40.0f)];
}
- (void)didMoveToWindow
{
CGFloat windowContentsScale = self.window.screen.scale;
self.circularProgressLayer.contentsScale = windowContentsScale;
}
#pragma mark - Progress
- (CGFloat)progress
{
return self.circularProgressLayer.progress;
}
- (void)setProgress:(CGFloat)progress
{
[self setProgress:progress animated:NO];
}
- (void)setProgress:(CGFloat)progress animated:(BOOL)animated
{
[self.layer removeAnimationForKey:@"indeterminateAnimation"];
[self.circularProgressLayer removeAnimationForKey:@"progress"];
CGFloat pinnedProgress = MIN(MAX(progress, 0.0f), 1.0f);
if (animated)
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"progress"];
animation.duration = fabs(self.progress - pinnedProgress); // Same duration as UIProgressView animation
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.fromValue = [NSNumber numberWithFloat:self.progress];
animation.toValue = [NSNumber numberWithFloat:pinnedProgress];
[self.circularProgressLayer addAnimation:animation forKey:@"progress"];
}
else
{
[self.circularProgressLayer setNeedsDisplay];
}
self.circularProgressLayer.progress = pinnedProgress;
}
#pragma mark - UIAppearance methods
- (UIColor *)trackTintColor
{
return self.circularProgressLayer.trackTintColor;
}
- (void)setTrackTintColor:(UIColor *)trackTintColor
{
self.circularProgressLayer.trackTintColor = trackTintColor;
[self.circularProgressLayer setNeedsDisplay];
}
- (UIColor *)progressTintColor
{
return self.circularProgressLayer.progressTintColor;
}
- (void)setProgressTintColor:(UIColor *)progressTintColor
{
self.circularProgressLayer.progressTintColor = progressTintColor;
[self.circularProgressLayer setNeedsDisplay];
}
- (NSInteger)roundedCorners
{
return self.roundedCorners;
}
- (void)setRoundedCorners:(NSInteger)roundedCorners
{
self.circularProgressLayer.roundedCorners = roundedCorners;
[self.circularProgressLayer setNeedsDisplay];
}
- (CGFloat)thicknessRatio
{
return self.circularProgressLayer.thicknessRatio;
}
- (void)setThicknessRatio:(CGFloat)thicknessRatio
{
self.circularProgressLayer.thicknessRatio = MIN(MAX(thicknessRatio, 0.f), 1.f);
[self.circularProgressLayer setNeedsDisplay];
}
- (NSInteger)indeterminate
{
CAAnimation *spinAnimation = [self.layer animationForKey:@"indeterminateAnimation"];
return (spinAnimation == nil ? 0 : 1);
}
- (void)setIndeterminate:(NSInteger)indeterminate
{
if (indeterminate && !self.indeterminate)
{
CABasicAnimation *spinAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
spinAnimation.byValue = [NSNumber numberWithFloat:indeterminate > 0 ? 2.0f*M_PI : -2.0f*M_PI];
spinAnimation.duration = self.indeterminateDuration;
spinAnimation.repeatCount = HUGE_VALF;
[self.layer addAnimation:spinAnimation forKey:@"indeterminateAnimation"];
}
else
{
[self.layer removeAnimationForKey:@"indeterminateAnimation"];
}
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/all-wcprops
================================================
K 25
svn:wc:ra_dav:version-url
V 162
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage
END
EMSDWebImageManager.m
K 25
svn:wc:ra_dav:version-url
V 184
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImageManager.m
END
EMSDWebImageDownloaderOperation.h
K 25
svn:wc:ra_dav:version-url
V 196
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImageDownloaderOperation.h
END
UIImageView+EMWebCache.h
K 25
svn:wc:ra_dav:version-url
V 187
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIImageView+EMWebCache.h
END
UIImage+EMWebP.h
K 25
svn:wc:ra_dav:version-url
V 179
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIImage+EMWebP.h
END
UIButton+EMWebCache.m
K 25
svn:wc:ra_dav:version-url
V 184
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIButton+EMWebCache.m
END
EMSDWebImageDownloaderOperation.m
K 25
svn:wc:ra_dav:version-url
V 196
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImageDownloaderOperation.m
END
UIImageView+EMWebCache.m
K 25
svn:wc:ra_dav:version-url
V 187
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIImageView+EMWebCache.m
END
UIImage+EMWebP.m
K 25
svn:wc:ra_dav:version-url
V 179
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIImage+EMWebP.m
END
NSData+EMImageContentType.h
K 25
svn:wc:ra_dav:version-url
V 190
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/NSData+EMImageContentType.h
END
UIView+EMWebCacheOperation.h
K 25
svn:wc:ra_dav:version-url
V 191
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIView+EMWebCacheOperation.h
END
EMSDWebImageDownloader.h
K 25
svn:wc:ra_dav:version-url
V 187
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImageDownloader.h
END
NSData+EMImageContentType.m
K 25
svn:wc:ra_dav:version-url
V 190
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/NSData+EMImageContentType.m
END
EMSDWebImageDownloader.m
K 25
svn:wc:ra_dav:version-url
V 187
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImageDownloader.m
END
UIView+EMWebCacheOperation.m
K 25
svn:wc:ra_dav:version-url
V 191
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIView+EMWebCacheOperation.m
END
UIImageView+EMHighlightedWebCache.h
K 25
svn:wc:ra_dav:version-url
V 198
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIImageView+EMHighlightedWebCache.h
END
EMSDWebImageCompat.h
K 25
svn:wc:ra_dav:version-url
V 183
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImageCompat.h
END
UIImageView+EMHighlightedWebCache.m
K 25
svn:wc:ra_dav:version-url
V 198
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIImageView+EMHighlightedWebCache.m
END
EMSDWebImagePrefetcher.h
K 25
svn:wc:ra_dav:version-url
V 187
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImagePrefetcher.h
END
UIImage+EMMultiFormat.h
K 25
svn:wc:ra_dav:version-url
V 186
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIImage+EMMultiFormat.h
END
EMSDWebImageCompat.m
K 25
svn:wc:ra_dav:version-url
V 183
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImageCompat.m
END
UIImage+EMGIF.h
K 25
svn:wc:ra_dav:version-url
V 178
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIImage+EMGIF.h
END
EMSDWebImagePrefetcher.m
K 25
svn:wc:ra_dav:version-url
V 187
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImagePrefetcher.m
END
UIImage+EMMultiFormat.m
K 25
svn:wc:ra_dav:version-url
V 186
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIImage+EMMultiFormat.m
END
UIImage+EMGIF.m
K 25
svn:wc:ra_dav:version-url
V 178
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIImage+EMGIF.m
END
EMSDWebImageOperation.h
K 25
svn:wc:ra_dav:version-url
V 186
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImageOperation.h
END
MKAnnotationView+EMWebCache.h
K 25
svn:wc:ra_dav:version-url
V 192
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/MKAnnotationView+EMWebCache.h
END
EMSDImageCache.h
K 25
svn:wc:ra_dav:version-url
V 179
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDImageCache.h
END
EMSDWebImageDecoder.h
K 25
svn:wc:ra_dav:version-url
V 184
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImageDecoder.h
END
MKAnnotationView+EMWebCache.m
K 25
svn:wc:ra_dav:version-url
V 192
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/MKAnnotationView+EMWebCache.m
END
EMSDWebImageDecoder.m
K 25
svn:wc:ra_dav:version-url
V 184
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImageDecoder.m
END
EMSDWebImageManager.h
K 25
svn:wc:ra_dav:version-url
V 184
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDWebImageManager.h
END
EMSDImageCache.m
K 25
svn:wc:ra_dav:version-url
V 179
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/EMSDImageCache.m
END
UIButton+EMWebCache.h
K 25
svn:wc:ra_dav:version-url
V 184
/zhuku/repo/!svn/ver/4261/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage/UIButton+EMWebCache.h
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/entries
================================================
10
dir
4265
http://bishanwen@121.43.163.28:9090/zhuku/repo/zhuku-ios/trunk/zhuku-iOS/trunk/ios-zhuku2017.12.28/ChatDemo-UI3.0/ChatSDK/EaseUI/EMUIKit/3rdparty/MWPhotoBrowser/Libraries/EMSDWebImage
http://bishanwen@121.43.163.28:9090/zhuku/repo
2017-12-28T02:01:09.072372Z
4261
zhoujun
5738001b-f13b-4815-ac0f-2e2f54dd1d22
EMSDWebImageManager.m
file
2018-01-16T02:17:09.000000Z
a0c72858b79292c8d3f395596524efca
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
14560
EMSDWebImageDownloaderOperation.h
file
2018-01-16T02:17:09.000000Z
daf3d61e3d5d9d3f8bf89048aa64ce52
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
2371
UIImageView+EMWebCache.h
file
2018-01-16T02:17:09.000000Z
2b9f7ba2fa72e723c5cb6b63053df542
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
9991
UIImage+EMWebP.h
file
2018-01-16T02:17:09.000000Z
04e3a49009886898e37f25c68b305fba
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
490
UIButton+EMWebCache.m
file
2018-01-16T02:17:09.000000Z
657ee1884364ee04d8730c84df18cea8
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
11887
EMSDWebImageDownloaderOperation.m
file
2018-01-16T02:17:09.000000Z
13c4542271464ffa512c263244b02421
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
15894
UIImageView+EMWebCache.m
file
2018-01-16T02:17:09.000000Z
c6a4ed3398d6ccad1e9d2ac12c7b8cf5
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
8502
UIImage+EMWebP.m
file
2018-01-16T02:17:09.000000Z
de2c8ae3afde7e660ef4c88a7bdcf26c
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
1968
NSData+EMImageContentType.h
file
2018-01-16T02:17:09.000000Z
6073615d25953caa01033d008370b550
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
567
UIView+EMWebCacheOperation.h
file
2018-01-16T02:17:09.000000Z
1721dc0622c19d3d47b744de1ce1d0fe
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
974
EMSDWebImageDownloader.h
file
2018-01-16T02:17:09.000000Z
3661231206d752e358f9ddf0f1d25d45
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
6103
NSData+EMImageContentType.m
file
2018-01-16T02:17:09.000000Z
1c7f940f3511cc0d091b12546284c9fc
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
1178
EMSDWebImageDownloader.m
file
2018-01-16T02:17:09.000000Z
1516351ad1cc4430ece77a5f365a37fe
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
11105
UIView+EMWebCacheOperation.m
file
2018-01-16T02:17:09.000000Z
eb3b0ac4047ebec4c482391b96f2bdf6
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
1915
UIImageView+EMHighlightedWebCache.h
file
2018-01-16T02:17:09.000000Z
d842a52cc597eab6b547ab30db91cba0
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
5164
EMSDWebImageCompat.h
file
2018-01-16T02:17:09.000000Z
53f5548503954dba7e0c336d7b7f528f
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
1735
UIImageView+EMHighlightedWebCache.m
file
2018-01-16T02:17:09.000000Z
37f4c4ff6e789dbe5f38b565871b939e
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
4785
EMSDWebImagePrefetcher.h
file
2018-01-16T02:17:09.000000Z
a93f221e13bad7e87998f45b2b16602e
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
3623
UIImage+EMMultiFormat.h
file
2018-01-16T02:17:09.000000Z
046f7f9313251af5343349ca85ba9a37
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
267
EMSDWebImageCompat.m
file
2018-01-16T02:17:09.000000Z
1749d91c7e2f991b06b8d7926685b7f2
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
1482
UIImage+EMGIF.h
file
2018-01-16T02:17:09.000000Z
5be330827167af78e8af30511e07b71d
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
384
EMSDWebImagePrefetcher.m
file
2018-01-16T02:17:09.000000Z
b69783b93e503c4cf646d1dda68f0a70
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
5003
UIImage+EMMultiFormat.m
file
2018-01-16T02:17:09.000000Z
608fcbbf2bb6b8eae9cd47204ba278dc
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
3269
UIImage+EMGIF.m
file
2018-01-16T02:17:09.000000Z
86c59635d86441acfcbde5242c8ba263
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
4857
EMSDWebImageOperation.h
file
2018-01-16T02:17:09.000000Z
6b9df791f2d415c4e8b2641afdd52692
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
331
MKAnnotationView+EMWebCache.h
file
2018-01-16T02:17:09.000000Z
347f6f7fd1b99b98fca5b112a34df12a
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
5767
EMSDImageCache.h
file
2018-01-16T02:17:09.000000Z
ca57335d2f95b6ae7747dca89cb68a45
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
7557
EMSDWebImageDecoder.h
file
2018-01-16T02:17:09.000000Z
3a217b89dc9fbfdef014d9a375445bdf
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
455
MKAnnotationView+EMWebCache.m
file
2018-01-16T02:17:09.000000Z
0165e3636b9d1eee83fdf119870dbe38
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
4859
EMSDWebImageManager.h
file
2018-01-16T02:17:09.000000Z
aa2244707cf6a343d1f15e0a8678c55a
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
10641
EMSDWebImageDecoder.m
file
2018-01-16T02:17:09.000000Z
7e31e5822754ffa272bc0a018bf4be8d
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
2598
EMSDImageCache.m
file
2018-01-16T02:17:09.000000Z
b243b8a870fdff674d090ff0bf6757b2
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
19401
UIButton+EMWebCache.h
file
2018-01-16T02:17:09.000000Z
c429fc06efedca82b0d93f85a4fb238a
2017-12-28T02:01:09.072372Z
4261
zhoujun
has-props
13516
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDImageCache.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDImageCache.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImageCompat.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImageCompat.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImageDecoder.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImageDecoder.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImageDownloader.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImageDownloader.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImageDownloaderOperation.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImageDownloaderOperation.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImageManager.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImageManager.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImageOperation.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImagePrefetcher.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/EMSDWebImagePrefetcher.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/MKAnnotationView+EMWebCache.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/MKAnnotationView+EMWebCache.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/NSData+EMImageContentType.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/NSData+EMImageContentType.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIButton+EMWebCache.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIButton+EMWebCache.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIImage+EMGIF.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIImage+EMGIF.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIImage+EMMultiFormat.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIImage+EMMultiFormat.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIImage+EMWebP.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIImage+EMWebP.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIImageView+EMHighlightedWebCache.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIImageView+EMHighlightedWebCache.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIImageView+EMWebCache.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIImageView+EMWebCache.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIView+EMWebCacheOperation.h.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/prop-base/UIView+EMWebCacheOperation.m.svn-base
================================================
K 14
svn:executable
V 0
END
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDImageCache.h.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import
#import "EMSDWebImageCompat.h"
typedef NS_ENUM(NSInteger, EMSDImageCacheType) {
/**
* The image wasn't available the SDWebImage caches, but was downloaded from the web.
*/
EMSDImageCacheTypeNone,
/**
* The image was obtained from the disk cache.
*/
EMSDImageCacheTypeDisk,
/**
* The image was obtained from the memory cache.
*/
EMSDImageCacheTypeMemory
};
typedef void(^EMSDWebImageQueryCompletedBlock)(UIImage *image, EMSDImageCacheType cacheType);
typedef void(^EMSDWebImageCheckCacheCompletionBlock)(BOOL isInCache);
typedef void(^EMSDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize);
/**
* EMSDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed
* asynchronous so it doesn’t add unnecessary latency to the UI.
*/
@interface EMSDImageCache : NSObject
/**
* The maximum "total cost" of the in-memory image cache. The cost function is the number of pixels held in memory.
*/
@property (assign, nonatomic) NSUInteger maxMemoryCost;
/**
* The maximum length of time to keep an image in the cache, in seconds
*/
@property (assign, nonatomic) NSInteger maxCacheAge;
/**
* The maximum size of the cache, in bytes.
*/
@property (assign, nonatomic) NSUInteger maxCacheSize;
/**
* Returns global shared cache instance
*
* @return EMSDImageCache global instance
*/
+ (EMSDImageCache *)sharedImageCache;
/**
* Init a new cache store with a specific namespace
*
* @param ns The namespace to use for this cache store
*/
- (id)initWithNamespace:(NSString *)ns;
/**
* Add a read-only cache path to search for images pre-cached by EMSDImageCache
* Useful if you want to bundle pre-loaded images with your app
*
* @param path The path to use for this read-only cache path
*/
- (void)addReadOnlyCachePath:(NSString *)path;
/**
* Store an image into memory and disk cache at the given key.
*
* @param image The image to store
* @param key The unique image cache key, usually it's image absolute URL
*/
- (void)storeImage:(UIImage *)image forKey:(NSString *)key;
/**
* Store an image into memory and optionally disk cache at the given key.
*
* @param image The image to store
* @param key The unique image cache key, usually it's image absolute URL
* @param toDisk Store the image to disk cache if YES
*/
- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk;
/**
* Store an image into memory and optionally disk cache at the given key.
*
* @param image The image to store
* @param recalculate BOOL indicates if imageData can be used or a new data should be constructed from the UIImage
* @param imageData The image data as returned by the server, this representation will be used for disk storage
* instead of converting the given image object into a storable/compressed image format in order
* to save quality and CPU
* @param key The unique image cache key, usually it's image absolute URL
* @param toDisk Store the image to disk cache if YES
*/
- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk;
/**
* Query the disk cache asynchronously.
*
* @param key The unique key used to store the wanted image
*/
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(EMSDWebImageQueryCompletedBlock)doneBlock;
/**
* Query the memory cache synchronously.
*
* @param key The unique key used to store the wanted image
*/
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key;
/**
* Query the disk cache synchronously after checking the memory cache.
*
* @param key The unique key used to store the wanted image
*/
- (UIImage *)imageFromDiskCacheForKey:(NSString *)key;
/**
* Remove the image from memory and disk cache synchronously
*
* @param key The unique image cache key
*/
- (void)removeImageForKey:(NSString *)key;
/**
* Remove the image from memory and disk cache synchronously
*
* @param key The unique image cache key
* @param completionBlock An block that should be executed after the image has been removed (optional)
*/
- (void)removeImageForKey:(NSString *)key withCompletion:(EMSDWebImageNoParamsBlock)completion;
/**
* Remove the image from memory and optionally disk cache synchronously
*
* @param key The unique image cache key
* @param fromDisk Also remove cache entry from disk if YES
*/
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk;
/**
* Remove the image from memory and optionally disk cache synchronously
*
* @param key The unique image cache key
* @param fromDisk Also remove cache entry from disk if YES
* @param completionBlock An block that should be executed after the image has been removed (optional)
*/
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(EMSDWebImageNoParamsBlock)completion;
/**
* Clear all memory cached images
*/
- (void)clearMemory;
/**
* Clear all disk cached images. Non-blocking method - returns immediately.
* @param completionBlock An block that should be executed after cache expiration completes (optional)
*/
- (void)clearDiskOnCompletion:(EMSDWebImageNoParamsBlock)completion;
/**
* Clear all disk cached images
* @see clearDiskOnCompletion:
*/
- (void)clearDisk;
/**
* Remove all expired cached image from disk. Non-blocking method - returns immediately.
* @param completionBlock An block that should be executed after cache expiration completes (optional)
*/
- (void)cleanDiskWithCompletionBlock:(EMSDWebImageNoParamsBlock)completionBlock;
/**
* Remove all expired cached image from disk
* @see cleanDiskWithCompletionBlock:
*/
- (void)cleanDisk;
/**
* Get the size used by the disk cache
*/
- (NSUInteger)getSize;
/**
* Get the number of images in the disk cache
*/
- (NSUInteger)getDiskCount;
/**
* Asynchronously calculate the disk cache's size.
*/
- (void)calculateSizeWithCompletionBlock:(EMSDWebImageCalculateSizeBlock)completionBlock;
/**
* Async check if image exists in disk cache already (does not load the image)
*
* @param key the key describing the url
* @param completionBlock the block to be executed when the check is done.
* @note the completion block will be always executed on the main queue
*/
- (void)diskImageExistsWithKey:(NSString *)key completion:(EMSDWebImageCheckCacheCompletionBlock)completionBlock;
/**
* Check if image exists in disk cache already (does not load the image)
*
* @param key the key describing the url
*
* @return YES if an image exists for the given key
*/
- (BOOL)diskImageExistsWithKey:(NSString *)key;
/**
* Get the cache path for a certain key (needs the cache path root folder)
*
* @param key the key (can be obtained from url using cacheKeyForURL)
* @param path the cach path root folder
*
* @return the cache path
*/
- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path;
/**
* Get the default cache path for a certain key
*
* @param key the key (can be obtained from url using cacheKeyForURL)
*
* @return the default cache path
*/
- (NSString *)defaultCachePathForKey:(NSString *)key;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDImageCache.m.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "EMSDImageCache.h"
#import "EMSDWebImageDecoder.h"
#import "UIImage+EMMultiFormat.h"
#import
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
// PNG signature bytes and data (below)
static unsigned char kPNGSignatureBytes[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
static NSData *kPNGSignatureData = nil;
BOOL EMImageDataHasPNGPreffix(NSData *data);
BOOL EMImageDataHasPNGPreffix(NSData *data) {
NSUInteger pngSignatureLength = [kPNGSignatureData length];
if ([data length] >= pngSignatureLength) {
if ([[data subdataWithRange:NSMakeRange(0, pngSignatureLength)] isEqualToData:kPNGSignatureData]) {
return YES;
}
}
return NO;
}
@interface EMSDImageCache ()
@property (strong, nonatomic) NSCache *memCache;
@property (strong, nonatomic) NSString *diskCachePath;
@property (strong, nonatomic) NSMutableArray *customPaths;
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue;
@end
@implementation EMSDImageCache {
NSFileManager *_fileManager;
}
+ (EMSDImageCache *)sharedImageCache {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8];
});
return instance;
}
- (id)init {
return [self initWithNamespace:@"default"];
}
- (id)initWithNamespace:(NSString *)ns {
if ((self = [super init])) {
NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];
// Create IO serial queue
_ioQueue = dispatch_queue_create("com.hackemist.EMSDWebImageCache", DISPATCH_QUEUE_SERIAL);
// Init default values
_maxCacheAge = kDefaultCacheMaxCacheAge;
// Init the memory cache
_memCache = [[NSCache alloc] init];
_memCache.name = fullNamespace;
// Init the disk cache
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
_diskCachePath = [paths[0] stringByAppendingPathComponent:fullNamespace];
dispatch_sync(_ioQueue, ^{
self->_fileManager = [NSFileManager new];
});
#if TARGET_OS_IPHONE
// Subscribe to app events
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(clearMemory)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(cleanDisk)
name:UIApplicationWillTerminateNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundCleanDisk)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
#endif
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
SDDispatchQueueRelease(_ioQueue);
}
- (void)addReadOnlyCachePath:(NSString *)path {
if (!self.customPaths) {
self.customPaths = [NSMutableArray new];
}
if (![self.customPaths containsObject:path]) {
[self.customPaths addObject:path];
}
}
- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path {
NSString *filename = [self cachedFileNameForKey:key];
return [path stringByAppendingPathComponent:filename];
}
- (NSString *)defaultCachePathForKey:(NSString *)key {
return [self cachePathForKey:key inPath:self.diskCachePath];
}
#pragma mark EMSDImageCache (private)
- (NSString *)cachedFileNameForKey:(NSString *)key {
const char *str = [key UTF8String];
if (str == NULL) {
str = "";
}
unsigned char r[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), r);
NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]];
return filename;
}
#pragma mark ImageCache
- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk {
if (!image || !key) {
return;
}
[self.memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale];
if (toDisk) {
dispatch_async(self.ioQueue, ^{
NSData *data = imageData;
if (image && (recalculate || !data)) {
#if TARGET_OS_IPHONE
// We need to determine if the image is a PNG or a JPEG
// PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html)
// The first eight bytes of a PNG file always contain the following (decimal) values:
// 137 80 78 71 13 10 26 10
// We assume the image is PNG, in case the imageData is nil (i.e. if trying to save a UIImage directly),
// we will consider it PNG to avoid loosing the transparency
BOOL imageIsPng = YES;
// But if we have an image data, we will look at the preffix
if ([imageData length] >= [kPNGSignatureData length]) {
imageIsPng = EMImageDataHasPNGPreffix(imageData);
}
if (imageIsPng) {
data = UIImagePNGRepresentation(image);
}
else {
data = UIImageJPEGRepresentation(image, (CGFloat)1.0);
}
#else
data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil];
#endif
}
if (data) {
if (![self->_fileManager fileExistsAtPath:self->_diskCachePath]) {
[self->_fileManager createDirectoryAtPath:self->_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
}
[self->_fileManager createFileAtPath:[self defaultCachePathForKey:key] contents:data attributes:nil];
}
});
}
}
- (void)storeImage:(UIImage *)image forKey:(NSString *)key {
[self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:YES];
}
- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk {
[self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:toDisk];
}
- (BOOL)diskImageExistsWithKey:(NSString *)key {
BOOL exists = NO;
// this is an exception to access the filemanager on another queue than ioQueue, but we are using the shared instance
// from apple docs on NSFileManager: The methods of the shared NSFileManager object can be called from multiple threads safely.
exists = [[NSFileManager defaultManager] fileExistsAtPath:[self defaultCachePathForKey:key]];
return exists;
}
- (void)diskImageExistsWithKey:(NSString *)key completion:(EMSDWebImageCheckCacheCompletionBlock)completionBlock {
dispatch_async(_ioQueue, ^{
BOOL exists = [self->_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]];
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(exists);
});
}
});
}
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {
return [self.memCache objectForKey:key];
}
- (UIImage *)imageFromDiskCacheForKey:(NSString *)key {
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
return image;
}
// Second check the disk cache...
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage) {
CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale;
[self.memCache setObject:diskImage forKey:key cost:cost];
}
return diskImage;
}
- (NSData *)diskImageDataBySearchingAllPathsForKey:(NSString *)key {
NSString *defaultPath = [self defaultCachePathForKey:key];
NSData *data = [NSData dataWithContentsOfFile:defaultPath];
if (data) {
return data;
}
for (NSString *path in self.customPaths) {
NSString *filePath = [self cachePathForKey:key inPath:path];
NSData *imageData = [NSData dataWithContentsOfFile:filePath];
if (imageData) {
return imageData;
}
}
return nil;
}
- (UIImage *)diskImageForKey:(NSString *)key {
NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
if (data) {
UIImage *image = [UIImage sd_imageWithData:data];
image = [self scaledImageForKey:key image:image];
image = [UIImage decodedImageWithImage:image];
return image;
}
else {
return nil;
}
}
- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image {
return EMSDScaledImageForKey(key, image);
}
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(EMSDWebImageQueryCompletedBlock)doneBlock {
if (!doneBlock) {
return nil;
}
if (!key) {
doneBlock(nil, EMSDImageCacheTypeNone);
return nil;
}
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
doneBlock(image, EMSDImageCacheTypeMemory);
return nil;
}
NSOperation *operation = [NSOperation new];
dispatch_async(self.ioQueue, ^{
if (operation.isCancelled) {
return;
}
@autoreleasepool {
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage) {
CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale;
[self.memCache setObject:diskImage forKey:key cost:cost];
}
dispatch_async(dispatch_get_main_queue(), ^{
doneBlock(diskImage, EMSDImageCacheTypeDisk);
});
}
});
return operation;
}
- (void)removeImageForKey:(NSString *)key {
[self removeImageForKey:key withCompletion:nil];
}
- (void)removeImageForKey:(NSString *)key withCompletion:(EMSDWebImageNoParamsBlock)completion {
[self removeImageForKey:key fromDisk:YES withCompletion:completion];
}
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk {
[self removeImageForKey:key fromDisk:fromDisk withCompletion:nil];
}
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(EMSDWebImageNoParamsBlock)completion {
if (key == nil) {
return;
}
[self.memCache removeObjectForKey:key];
if (fromDisk) {
dispatch_async(self.ioQueue, ^{
[self->_fileManager removeItemAtPath:[self defaultCachePathForKey:key] error:nil];
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
});
} else if (completion){
completion();
}
}
- (void)setMaxMemoryCost:(NSUInteger)maxMemoryCost {
self.memCache.totalCostLimit = maxMemoryCost;
}
- (NSUInteger)maxMemoryCost {
return self.memCache.totalCostLimit;
}
- (void)clearMemory {
[self.memCache removeAllObjects];
}
- (void)clearDisk {
[self clearDiskOnCompletion:nil];
}
- (void)clearDiskOnCompletion:(EMSDWebImageNoParamsBlock)completion
{
dispatch_async(self.ioQueue, ^{
[self->_fileManager removeItemAtPath:self.diskCachePath error:nil];
[self->_fileManager createDirectoryAtPath:self.diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
});
}
- (void)cleanDisk {
[self cleanDiskWithCompletionBlock:nil];
}
- (void)cleanDiskWithCompletionBlock:(EMSDWebImageNoParamsBlock)completionBlock {
dispatch_async(self.ioQueue, ^{
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
// This enumerator prefetches useful properties for our cache files.
NSDirectoryEnumerator *fileEnumerator = [self->_fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
NSUInteger currentCacheSize = 0;
// Enumerate all of the files in the cache directory. This loop has two purposes:
//
// 1. Removing files that are older than the expiration date.
// 2. Storing file attributes for the size-based cleanup pass.
NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];
for (NSURL *fileURL in fileEnumerator) {
NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];
// Skip directories.
if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
continue;
}
// Remove files that are older than the expiration date;
NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
continue;
}
// Store a reference to this file and account for its total size.
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
[cacheFiles setObject:resourceValues forKey:fileURL];
}
for (NSURL *fileURL in urlsToDelete) {
[self->_fileManager removeItemAtURL:fileURL error:nil];
}
// If our remaining disk cache exceeds a configured maximum size, perform a second
// size-based cleanup pass. We delete the oldest files first.
if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) {
// Target half of our maximum cache size for this cleanup pass.
const NSUInteger desiredCacheSize = self.maxCacheSize / 2;
// Sort the remaining cache files by their last modification time (oldest first).
NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
}];
// Delete files until we fall below our desired cache size.
for (NSURL *fileURL in sortedFiles) {
if ([self->_fileManager removeItemAtURL:fileURL error:nil]) {
NSDictionary *resourceValues = cacheFiles[fileURL];
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];
if (currentCacheSize < desiredCacheSize) {
break;
}
}
}
}
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
}
- (void)backgroundCleanDisk {
UIApplication *application = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
[self cleanDiskWithCompletionBlock:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
}
- (NSUInteger)getSize {
__block NSUInteger size = 0;
dispatch_sync(self.ioQueue, ^{
NSDirectoryEnumerator *fileEnumerator = [self->_fileManager enumeratorAtPath:self.diskCachePath];
for (NSString *fileName in fileEnumerator) {
NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName];
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
size += [attrs fileSize];
}
});
return size;
}
- (NSUInteger)getDiskCount {
__block NSUInteger count = 0;
dispatch_sync(self.ioQueue, ^{
NSDirectoryEnumerator *fileEnumerator = [self->_fileManager enumeratorAtPath:self.diskCachePath];
count = [[fileEnumerator allObjects] count];
});
return count;
}
- (void)calculateSizeWithCompletionBlock:(EMSDWebImageCalculateSizeBlock)completionBlock {
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
dispatch_async(self.ioQueue, ^{
NSUInteger fileCount = 0;
NSUInteger totalSize = 0;
NSDirectoryEnumerator *fileEnumerator = [self->_fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:@[NSFileSize]
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
for (NSURL *fileURL in fileEnumerator) {
NSNumber *fileSize;
[fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL];
totalSize += [fileSize unsignedIntegerValue];
fileCount += 1;
}
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(fileCount, totalSize);
});
}
});
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImageCompat.h.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
* (c) Jamie Pinkham
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import
#ifdef __OBJC_GC__
#error SDWebImage does not support Objective-C Garbage Collection
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
#error SDWebImage doesn't support Deployement Target version < 5.0
#endif
#if !TARGET_OS_IPHONE
#import
#ifndef UIImage
#define UIImage NSImage
#endif
#ifndef UIImageView
#define UIImageView NSImageView
#endif
#else
#import
#endif
#ifndef NS_ENUM
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#endif
#ifndef NS_OPTIONS
#define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
#endif
#if OS_OBJECT_USE_OBJC
#undef SDDispatchQueueRelease
#undef SDDispatchQueueSetterSementics
#define SDDispatchQueueRelease(q)
#define SDDispatchQueueSetterSementics strong
#else
#undef SDDispatchQueueRelease
#undef SDDispatchQueueSetterSementics
#define SDDispatchQueueRelease(q) (dispatch_release(q))
#define SDDispatchQueueSetterSementics assign
#endif
extern UIImage *EMSDScaledImageForKey(NSString *key, UIImage *image);
typedef void(^EMSDWebImageNoParamsBlock)();
#define dispatch_main_sync_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_sync(dispatch_get_main_queue(), block);\
}
#define dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImageCompat.m.svn-base
================================================
//
// EMSDWebImageCompat.m
// SDWebImage
//
// Created by Olivier Poitrey on 11/12/12.
// Copyright (c) 2012 Dailymotion. All rights reserved.
//
#import "EMSDWebImageCompat.h"
#if !__has_feature(objc_arc)
#error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
#endif
inline UIImage *EMSDScaledImageForKey(NSString *key, UIImage *image) {
if (!image) {
return nil;
}
if ([image.images count] > 0) {
NSMutableArray *scaledImages = [NSMutableArray array];
for (UIImage *tempImage in image.images) {
[scaledImages addObject:EMSDScaledImageForKey(key, tempImage)];
}
return [UIImage animatedImageWithImages:scaledImages duration:image.duration];
}
else {
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
CGFloat scale = 1.0;
if (key.length >= 8) {
// Search @2x. at the end of the string, before a 3 to 4 extension length (only if key len is 8 or more @2x. + 4 len ext)
NSRange range = [key rangeOfString:@"@2x." options:0 range:NSMakeRange(key.length - 8, 5)];
if (range.location != NSNotFound) {
scale = 2.0;
}
}
UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
image = scaledImage;
}
return image;
}
}
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImageDecoder.h.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* Created by james on 9/28/11.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import
#import "EMSDWebImageCompat.h"
@interface UIImage (ForceDecode)
+ (UIImage *)decodedImageWithImage:(UIImage *)image;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImageDecoder.m.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* Created by james on 9/28/11.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "EMSDWebImageDecoder.h"
@implementation UIImage (ForceDecode)
+ (UIImage *)decodedImageWithImage:(UIImage *)image {
if (image.images) {
// Do not decode animated images
return image;
}
CGImageRef imageRef = image.CGImage;
CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
CGRect imageRect = (CGRect){.origin = CGPointZero, .size = imageSize};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask);
BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone ||
infoMask == kCGImageAlphaNoneSkipFirst ||
infoMask == kCGImageAlphaNoneSkipLast);
// CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB.
// https://developer.apple.com/library/mac/#qa/qa1037/_index.html
if (infoMask == kCGImageAlphaNone && CGColorSpaceGetNumberOfComponents(colorSpace) > 1) {
// Unset the old alpha info.
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
// Set noneSkipFirst.
bitmapInfo |= kCGImageAlphaNoneSkipFirst;
}
// Some PNGs tell us they have alpha but only 3 components. Odd.
else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3) {
// Unset the old alpha info.
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaPremultipliedFirst;
}
// It calculates the bytes-per-row based on the bitsPerComponent and width arguments.
CGContextRef context = CGBitmapContextCreate(NULL,
imageSize.width,
imageSize.height,
CGImageGetBitsPerComponent(imageRef),
0,
colorSpace,
bitmapInfo);
CGColorSpaceRelease(colorSpace);
// If failed, return undecompressed image
if (!context) return image;
CGContextDrawImage(context, imageRect, imageRef);
CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
UIImage *decompressedImage = [UIImage imageWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation];
CGImageRelease(decompressedImageRef);
return decompressedImage;
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImageDownloader.h.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import
#import "EMSDWebImageCompat.h"
#import "EMSDWebImageOperation.h"
typedef NS_OPTIONS(NSUInteger, EMSDWebImageDownloaderOptions) {
EMSDWebImageDownloaderLowPriority = 1 << 0,
EMSDWebImageDownloaderProgressiveDownload = 1 << 1,
/**
* By default, request prevent the of NSURLCache. With this flag, NSURLCache
* is used with default policies.
*/
EMSDWebImageDownloaderUseNSURLCache = 1 << 2,
/**
* Call completion block with nil image/imageData if the image was read from NSURLCache
* (to be combined with `EMSDWebImageDownloaderUseNSURLCache`).
*/
EMSDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
/**
* In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for
* extra time in background to let the request finish. If the background task expires the operation will be cancelled.
*/
EMSDWebImageDownloaderContinueInBackground = 1 << 4,
/**
* Handles cookies stored in NSHTTPCookieStore by setting
* NSMutableURLRequest.HTTPShouldHandleCookies = YES;
*/
EMSDWebImageDownloaderHandleCookies = 1 << 5,
/**
* Enable to allow untrusted SSL ceriticates.
* Useful for testing purposes. Use with caution in production.
*/
EMSDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,
/**
* Put the image in the high priority queue.
*/
EMSDWebImageDownloaderHighPriority = 1 << 7,
};
typedef NS_ENUM(NSInteger, EMSDWebImageDownloaderExecutionOrder) {
/**
* Default value. All download operations will execute in queue style (first-in-first-out).
*/
EMSDWebImageDownloaderFIFOExecutionOrder,
/**
* All download operations will execute in stack style (last-in-first-out).
*/
EMSDWebImageDownloaderLIFOExecutionOrder
};
extern NSString *const EMSDWebImageDownloadStartNotification;
extern NSString *const EMSDWebImageDownloadStopNotification;
typedef void(^EMSDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);
typedef void(^EMSDWebImageDownloaderCompletedBlock)(UIImage *image, NSData *data, NSError *error, BOOL finished);
typedef NSDictionary *(^EMSDWebImageDownloaderHeadersFilterBlock)(NSURL *url, NSDictionary *headers);
/**
* Asynchronous downloader dedicated and optimized for image loading.
*/
@interface EMSDWebImageDownloader : NSObject
@property (assign, nonatomic) NSInteger maxConcurrentDownloads;
/**
* Shows the current amount of downloads that still need to be downloaded
*/
@property (readonly, nonatomic) NSUInteger currentDownloadCount;
/**
* The timeout value (in seconds) for the download operation. Default: 15.0.
*/
@property (assign, nonatomic) NSTimeInterval downloadTimeout;
/**
* Changes download operations execution order. Default value is `EMSDWebImageDownloaderFIFOExecutionOrder`.
*/
@property (assign, nonatomic) EMSDWebImageDownloaderExecutionOrder executionOrder;
/**
* Singleton method, returns the shared instance
*
* @return global shared instance of downloader class
*/
+ (EMSDWebImageDownloader *)sharedDownloader;
/**
* Set username
*/
@property (strong, nonatomic) NSString *username;
/**
* Set password
*/
@property (strong, nonatomic) NSString *password;
/**
* Set filter to pick headers for downloading image HTTP request.
*
* This block will be invoked for each downloading image request, returned
* NSDictionary will be used as headers in corresponding HTTP request.
*/
@property (nonatomic, copy) EMSDWebImageDownloaderHeadersFilterBlock headersFilter;
/**
* Set a value for a HTTP header to be appended to each download HTTP request.
*
* @param value The value for the header field. Use `nil` value to remove the header.
* @param field The name of the header field to set.
*/
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
/**
* Returns the value of the specified HTTP header field.
*
* @return The value associated with the header field field, or `nil` if there is no corresponding header field.
*/
- (NSString *)valueForHTTPHeaderField:(NSString *)field;
/**
* Creates a EMSDWebImageDownloader async downloader instance with a given URL
*
* The delegate will be informed when the image is finish downloaded or an error has happen.
*
* @see EMSDWebImageDownloaderDelegate
*
* @param url The URL to the image to download
* @param options The options to be used for this download
* @param progressBlock A block called repeatedly while the image is downloading
* @param completedBlock A block called once the download is completed.
* If the download succeeded, the image parameter is set, in case of error,
* error parameter is set with the error. The last parameter is always YES
* if EMSDWebImageDownloaderProgressiveDownload isn't use. With the
* EMSDWebImageDownloaderProgressiveDownload option, this block is called
* repeatedly with the partial image object and the finished argument set to NO
* before to be called a last time with the full image and finished argument
* set to YES. In case of error, the finished argument is always YES.
*
* @return A cancellable EMSDWebImageOperation
*/
- (id )downloadImageWithURL:(NSURL *)url
options:(EMSDWebImageDownloaderOptions)options
progress:(EMSDWebImageDownloaderProgressBlock)progressBlock
completed:(EMSDWebImageDownloaderCompletedBlock)completedBlock;
/**
* Sets the download queue suspension state
*/
- (void)setSuspended:(BOOL)suspended;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImageDownloader.m.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "EMSDWebImageDownloader.h"
#import "EMSDWebImageDownloaderOperation.h"
#import
NSString *const EMSDWebImageDownloadStartNotification = @"EMSDWebImageDownloadStartNotification";
NSString *const EMSDWebImageDownloadStopNotification = @"EMSDWebImageDownloadStopNotification";
static NSString *const kProgressCallbackKey = @"progress";
static NSString *const kCompletedCallbackKey = @"completed";
@interface EMSDWebImageDownloader ()
@property (strong, nonatomic) NSOperationQueue *downloadQueue;
@property (weak, nonatomic) NSOperation *lastAddedOperation;
@property (strong, nonatomic) NSMutableDictionary *URLCallbacks;
@property (strong, nonatomic) NSMutableDictionary *HTTPHeaders;
// This queue is used to serialize the handling of the network responses of all the download operation in a single queue
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue;
@end
@implementation EMSDWebImageDownloader
+ (void)initialize {
// Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator )
// To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import
if (NSClassFromString(@"SDNetworkActivityIndicator")) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")];
#pragma clang diagnostic pop
// Remove observer in case it was previously added.
[[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:EMSDWebImageDownloadStartNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:EMSDWebImageDownloadStopNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:activityIndicator
selector:NSSelectorFromString(@"startActivity")
name:EMSDWebImageDownloadStartNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:activityIndicator
selector:NSSelectorFromString(@"stopActivity")
name:EMSDWebImageDownloadStopNotification object:nil];
}
}
+ (EMSDWebImageDownloader *)sharedDownloader {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
- (id)init {
if ((self = [super init])) {
_executionOrder = EMSDWebImageDownloaderFIFOExecutionOrder;
_downloadQueue = [NSOperationQueue new];
_downloadQueue.maxConcurrentOperationCount = 2;
_URLCallbacks = [NSMutableDictionary new];
_HTTPHeaders = [NSMutableDictionary dictionaryWithObject:@"image/webp,image/*;q=0.8" forKey:@"Accept"];
_barrierQueue = dispatch_queue_create("com.hackemist.EMSDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
_downloadTimeout = 15.0;
}
return self;
}
- (void)dealloc {
[self.downloadQueue cancelAllOperations];
SDDispatchQueueRelease(_barrierQueue);
}
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field {
if (value) {
self.HTTPHeaders[field] = value;
}
else {
[self.HTTPHeaders removeObjectForKey:field];
}
}
- (NSString *)valueForHTTPHeaderField:(NSString *)field {
return self.HTTPHeaders[field];
}
- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads {
_downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads;
}
- (NSUInteger)currentDownloadCount {
return _downloadQueue.operationCount;
}
- (NSInteger)maxConcurrentDownloads {
return _downloadQueue.maxConcurrentOperationCount;
}
- (id )downloadImageWithURL:(NSURL *)url options:(EMSDWebImageDownloaderOptions)options progress:(EMSDWebImageDownloaderProgressBlock)progressBlock completed:(EMSDWebImageDownloaderCompletedBlock)completedBlock {
__block EMSDWebImageDownloaderOperation *operation;
__weak EMSDWebImageDownloader *wself = self;
[self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^{
NSTimeInterval timeoutInterval = wself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
// In order to prevent from potential duplicate caching (NSURLCache + EMSDImageCache) we disable the cache for image requests if told otherwise
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & EMSDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
request.HTTPShouldHandleCookies = (options & EMSDWebImageDownloaderHandleCookies);
request.HTTPShouldUsePipelining = YES;
if (wself.headersFilter) {
request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = wself.HTTPHeaders;
}
operation = [[EMSDWebImageDownloaderOperation alloc] initWithRequest:request
options:options
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
EMSDWebImageDownloader *sself = wself;
if (!sself) return;
NSArray *callbacksForURL = [sself callbacksForURL:url];
for (NSDictionary *callbacks in callbacksForURL) {
EMSDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];
if (callback) callback(receivedSize, expectedSize);
}
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
EMSDWebImageDownloader *sself = wself;
if (!sself) return;
NSArray *callbacksForURL = [sself callbacksForURL:url];
if (finished) {
[sself removeCallbacksForURL:url];
}
for (NSDictionary *callbacks in callbacksForURL) {
EMSDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
if (callback) callback(image, data, error, finished);
}
}
cancelled:^{
EMSDWebImageDownloader *sself = wself;
if (!sself) return;
[sself removeCallbacksForURL:url];
}];
if (wself.username && wself.password) {
operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];
}
if (options & EMSDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & EMSDWebImageDownloaderLowPriority) {
operation.queuePriority = NSOperationQueuePriorityLow;
}
[wself.downloadQueue addOperation:operation];
if (wself.executionOrder == EMSDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
[wself.lastAddedOperation addDependency:operation];
wself.lastAddedOperation = operation;
}
}];
return operation;
}
- (void)addProgressCallback:(EMSDWebImageDownloaderProgressBlock)progressBlock andCompletedBlock:(EMSDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(EMSDWebImageNoParamsBlock)createCallback {
// The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
if (url == nil) {
if (completedBlock != nil) {
completedBlock(nil, nil, nil, NO);
}
return;
}
dispatch_barrier_sync(self.barrierQueue, ^{
BOOL first = NO;
if (!self.URLCallbacks[url]) {
self.URLCallbacks[url] = [NSMutableArray new];
first = YES;
}
// Handle single download of simultaneous download request for the same URL
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
NSMutableDictionary *callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
[callbacksForURL addObject:callbacks];
self.URLCallbacks[url] = callbacksForURL;
if (first) {
createCallback();
}
});
}
- (NSArray *)callbacksForURL:(NSURL *)url {
__block NSArray *callbacksForURL;
dispatch_sync(self.barrierQueue, ^{
callbacksForURL = self.URLCallbacks[url];
});
return [callbacksForURL copy];
}
- (void)removeCallbacksForURL:(NSURL *)url {
dispatch_barrier_async(self.barrierQueue, ^{
[self.URLCallbacks removeObjectForKey:url];
});
}
- (void)setSuspended:(BOOL)suspended {
[self.downloadQueue setSuspended:suspended];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImageDownloaderOperation.h.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import
#import "EMSDWebImageDownloader.h"
#import "EMSDWebImageOperation.h"
@interface EMSDWebImageDownloaderOperation : NSOperation
/**
* The request used by the operation's connection.
*/
@property (strong, nonatomic, readonly) NSURLRequest *request;
/**
* Whether the URL connection should consult the credential storage for authenticating the connection. `YES` by default.
*
* This is the value that is returned in the `NSURLConnectionDelegate` method `-connectionShouldUseCredentialStorage:`.
*/
@property (nonatomic, assign) BOOL shouldUseCredentialStorage;
/**
* The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`.
*
* This will be overridden by any shared credentials that exist for the username or password of the request URL, if present.
*/
@property (nonatomic, strong) NSURLCredential *credential;
/**
* The EMSDWebImageDownloaderOptions for the receiver.
*/
@property (assign, nonatomic, readonly) EMSDWebImageDownloaderOptions options;
/**
* Initializes a `EMSDWebImageDownloaderOperation` object
*
* @see EMSDWebImageDownloaderOperation
*
* @param request the URL request
* @param options downloader options
* @param progressBlock the block executed when a new chunk of data arrives.
* @note the progress block is executed on a background queue
* @param completedBlock the block executed when the download is done.
* @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue
* @param cancelBlock the block executed if the download (operation) is cancelled
*
* @return the initialized instance
*/
- (id)initWithRequest:(NSURLRequest *)request
options:(EMSDWebImageDownloaderOptions)options
progress:(EMSDWebImageDownloaderProgressBlock)progressBlock
completed:(EMSDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(EMSDWebImageNoParamsBlock)cancelBlock;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImageDownloaderOperation.m.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "EMSDWebImageDownloaderOperation.h"
#import "EMSDWebImageDecoder.h"
#import "UIImage+EMMultiFormat.h"
#import
#import "EMSDWebImageManager.h"
@interface EMSDWebImageDownloaderOperation ()
@property (copy, nonatomic) EMSDWebImageDownloaderProgressBlock progressBlock;
@property (copy, nonatomic) EMSDWebImageDownloaderCompletedBlock completedBlock;
@property (copy, nonatomic) EMSDWebImageNoParamsBlock cancelBlock;
@property (assign, nonatomic, getter = isExecuting) BOOL executing;
@property (assign, nonatomic, getter = isFinished) BOOL finished;
@property (assign, nonatomic) NSInteger expectedSize;
@property (strong, nonatomic) NSMutableData *imageData;
@property (strong, nonatomic) NSURLConnection *connection;
@property (strong, atomic) NSThread *thread;
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;
#endif
@end
@implementation EMSDWebImageDownloaderOperation {
size_t width, height;
UIImageOrientation orientation;
BOOL responseFromCached;
}
@synthesize executing = _executing;
@synthesize finished = _finished;
- (id)initWithRequest:(NSURLRequest *)request
options:(EMSDWebImageDownloaderOptions)options
progress:(EMSDWebImageDownloaderProgressBlock)progressBlock
completed:(EMSDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(EMSDWebImageNoParamsBlock)cancelBlock {
if ((self = [super init])) {
_request = request;
_shouldUseCredentialStorage = YES;
_options = options;
_progressBlock = [progressBlock copy];
_completedBlock = [completedBlock copy];
_cancelBlock = [cancelBlock copy];
_executing = NO;
_finished = NO;
_expectedSize = 0;
responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called
}
return self;
}
- (void)start {
@synchronized (self) {
if (self.isCancelled) {
self.finished = YES;
[self reset];
return;
}
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
if ([self shouldContinueWhenAppEntersBackground]) {
__weak __typeof__ (self) wself = self;
self.backgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
__strong __typeof (wself) sself = wself;
if (sself) {
[sself cancel];
[[UIApplication sharedApplication] endBackgroundTask:sself.backgroundTaskId];
sself.backgroundTaskId = UIBackgroundTaskInvalid;
}
}];
}
#endif
self.executing = YES;
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
self.thread = [NSThread currentThread];
}
[self.connection start];
if (self.connection) {
if (self.progressBlock) {
self.progressBlock(0, NSURLResponseUnknownLength);
}
[[NSNotificationCenter defaultCenter] postNotificationName:EMSDWebImageDownloadStartNotification object:self];
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) {
// Make sure to run the runloop in our background thread so it can process downloaded data
// Note: we use a timeout to work around an issue with NSURLConnection cancel under iOS 5
// not waking up the runloop, leading to dead threads (see https://github.com/rs/SDWebImage/issues/466)
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false);
}
else {
CFRunLoopRun();
}
if (!self.isFinished) {
[self.connection cancel];
[self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:@{NSURLErrorFailingURLErrorKey : self.request.URL}]];
}
}
else {
if (self.completedBlock) {
self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES);
}
}
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
if (self.backgroundTaskId != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskId];
self.backgroundTaskId = UIBackgroundTaskInvalid;
}
#endif
}
- (void)cancel {
@synchronized (self) {
if (self.thread) {
[self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO];
}
else {
[self cancelInternal];
}
}
}
- (void)cancelInternalAndStop {
if (self.isFinished) return;
[self cancelInternal];
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)cancelInternal {
if (self.isFinished) return;
[super cancel];
if (self.cancelBlock) self.cancelBlock();
if (self.connection) {
[self.connection cancel];
[[NSNotificationCenter defaultCenter] postNotificationName:EMSDWebImageDownloadStopNotification object:self];
// As we cancelled the connection, its callback won't be called and thus won't
// maintain the isFinished and isExecuting flags.
if (self.isExecuting) self.executing = NO;
if (!self.isFinished) self.finished = YES;
}
[self reset];
}
- (void)done {
self.finished = YES;
self.executing = NO;
[self reset];
}
- (void)reset {
self.cancelBlock = nil;
self.completedBlock = nil;
self.progressBlock = nil;
self.connection = nil;
self.imageData = nil;
self.thread = nil;
}
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
- (BOOL)isConcurrent {
return YES;
}
#pragma mark NSURLConnection (delegate)
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
if (![response respondsToSelector:@selector(statusCode)] || [((NSHTTPURLResponse *)response) statusCode] < 400) {
NSInteger expected = response.expectedContentLength > 0 ? (NSInteger)response.expectedContentLength : 0;
self.expectedSize = expected;
if (self.progressBlock) {
self.progressBlock(0, expected);
}
self.imageData = [[NSMutableData alloc] initWithCapacity:expected];
}
else {
[self.connection cancel];
[[NSNotificationCenter defaultCenter] postNotificationName:EMSDWebImageDownloadStopNotification object:nil];
if (self.completedBlock) {
self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:[((NSHTTPURLResponse *)response) statusCode] userInfo:nil], YES);
}
CFRunLoopStop(CFRunLoopGetCurrent());
[self done];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.imageData appendData:data];
if ((self.options & EMSDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0 && self.completedBlock) {
// The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/
// Thanks to the author @Nyx0uf
// Get the total bytes downloaded
const NSInteger totalSize = self.imageData.length;
// Update the data source, we must pass ALL the data, not just the new bytes
CGImageSourceRef imageSource = CGImageSourceCreateIncremental(NULL);
CGImageSourceUpdateData(imageSource, (__bridge CFDataRef)self.imageData, totalSize == self.expectedSize);
if (width + height == 0) {
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
if (properties) {
NSInteger orientationValue = -1;
CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
if (val) CFNumberGetValue(val, kCFNumberLongType, &height);
val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
if (val) CFNumberGetValue(val, kCFNumberLongType, &width);
val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);
if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue);
CFRelease(properties);
// When we draw to Core Graphics, we lose orientation information,
// which means the image below born of initWithCGIImage will be
// oriented incorrectly sometimes. (Unlike the image born of initWithData
// in connectionDidFinishLoading.) So save it here and pass it on later.
orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)];
}
}
if (width + height > 0 && totalSize < self.expectedSize) {
// Create the image
CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
#ifdef TARGET_OS_IPHONE
// Workaround for iOS anamorphic image
if (partialImageRef) {
const size_t partialHeight = CGImageGetHeight(partialImageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
if (bmContext) {
CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef);
CGImageRelease(partialImageRef);
partialImageRef = CGBitmapContextCreateImage(bmContext);
CGContextRelease(bmContext);
}
else {
CGImageRelease(partialImageRef);
partialImageRef = nil;
}
}
#endif
if (partialImageRef) {
UIImage *image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:orientation];
NSString *key = [[EMSDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
UIImage *scaledImage = [self scaledImageForKey:key image:image];
image = [UIImage decodedImageWithImage:scaledImage];
CGImageRelease(partialImageRef);
dispatch_main_sync_safe(^{
if (self.completedBlock) {
self.completedBlock(image, nil, nil, NO);
}
});
}
}
CFRelease(imageSource);
}
if (self.progressBlock) {
self.progressBlock(self.imageData.length, self.expectedSize);
}
}
+ (UIImageOrientation)orientationFromPropertyValue:(NSInteger)value {
switch (value) {
case 1:
return UIImageOrientationUp;
case 3:
return UIImageOrientationDown;
case 8:
return UIImageOrientationLeft;
case 6:
return UIImageOrientationRight;
case 2:
return UIImageOrientationUpMirrored;
case 4:
return UIImageOrientationDownMirrored;
case 5:
return UIImageOrientationLeftMirrored;
case 7:
return UIImageOrientationRightMirrored;
default:
return UIImageOrientationUp;
}
}
- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image {
return EMSDScaledImageForKey(key, image);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection {
EMSDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock;
@synchronized(self) {
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread = nil;
self.connection = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:EMSDWebImageDownloadStopNotification object:nil];
}
if (![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) {
responseFromCached = NO;
}
if (completionBlock)
{
if (self.options & EMSDWebImageDownloaderIgnoreCachedResponse && responseFromCached) {
completionBlock(nil, nil, nil, YES);
}
else {
UIImage *image = [UIImage sd_imageWithData:self.imageData];
NSString *key = [[EMSDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
image = [self scaledImageForKey:key image:image];
// Do not force decoding animated GIFs
if (!image.images) {
image = [UIImage decodedImageWithImage:image];
}
if (CGSizeEqualToSize(image.size, CGSizeZero)) {
completionBlock(nil, nil, [NSError errorWithDomain:@"SDWebImageErrorDomain" code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}], YES);
}
else {
completionBlock(image, self.imageData, nil, YES);
}
}
}
self.completionBlock = nil;
[self done];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
CFRunLoopStop(CFRunLoopGetCurrent());
[[NSNotificationCenter defaultCenter] postNotificationName:EMSDWebImageDownloadStopNotification object:nil];
if (self.completedBlock) {
self.completedBlock(nil, nil, error, YES);
}
[self done];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
responseFromCached = NO; // If this method is called, it means the response wasn't read from cache
if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) {
// Prevents caching of responses
return nil;
}
else {
return cachedResponse;
}
}
- (BOOL)shouldContinueWhenAppEntersBackground {
return self.options & EMSDWebImageDownloaderContinueInBackground;
}
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection {
return self.shouldUseCredentialStorage;
}
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else {
if ([challenge previousFailureCount] == 0) {
if (self.credential) {
[[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
}
} else {
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
}
}
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImageManager.h.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "EMSDWebImageCompat.h"
#import "EMSDWebImageOperation.h"
#import "EMSDWebImageDownloader.h"
#import "EMSDImageCache.h"
typedef NS_OPTIONS(NSUInteger, EMSDWebImageOptions) {
/**
* By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying.
* This flag disable this blacklisting.
*/
EMSDWebImageRetryFailed = 1 << 0,
/**
* By default, image downloads are started during UI interactions, this flags disable this feature,
* leading to delayed download on UIScrollView deceleration for instance.
*/
EMSDWebImageLowPriority = 1 << 1,
/**
* This flag disables on-disk caching
*/
EMSDWebImageCacheMemoryOnly = 1 << 2,
/**
* This flag enables progressive download, the image is displayed progressively during download as a browser would do.
* By default, the image is only displayed once completely downloaded.
*/
EMSDWebImageProgressiveDownload = 1 << 3,
/**
* Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed.
* The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation.
* This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.
* If a cached image is refreshed, the completion block is called once with the cached image and again with the final image.
*
* Use this flag only if you can't make your URLs static with embeded cache busting parameter.
*/
EMSDWebImageRefreshCached = 1 << 4,
/**
* In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for
* extra time in background to let the request finish. If the background task expires the operation will be cancelled.
*/
EMSDWebImageContinueInBackground = 1 << 5,
/**
* Handles cookies stored in NSHTTPCookieStore by setting
* NSMutableURLRequest.HTTPShouldHandleCookies = YES;
*/
EMSDWebImageHandleCookies = 1 << 6,
/**
* Enable to allow untrusted SSL ceriticates.
* Useful for testing purposes. Use with caution in production.
*/
EMSDWebImageAllowInvalidSSLCertificates = 1 << 7,
/**
* By default, image are loaded in the order they were queued. This flag move them to
* the front of the queue and is loaded immediately instead of waiting for the current queue to be loaded (which
* could take a while).
*/
EMSDWebImageHighPriority = 1 << 8,
/**
* By default, placeholder images are loaded while the image is loading. This flag will delay the loading
* of the placeholder image until after the image has finished loading.
*/
EMSDWebImageDelayPlaceholder = 1 << 9
};
typedef void(^EMSDWebImageCompletionBlock)(UIImage *image, NSError *error, EMSDImageCacheType cacheType, NSURL *imageURL);
typedef void(^EMSDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, EMSDImageCacheType cacheType, BOOL finished, NSURL *imageURL);
typedef NSString *(^EMSDWebImageCacheKeyFilterBlock)(NSURL *url);
@class EMSDWebImageManager;
@protocol EMSDWebImageManagerDelegate
@optional
/**
* Controls which image should be downloaded when the image is not found in the cache.
*
* @param imageManager The current `EMSDWebImageManager`
* @param imageURL The url of the image to be downloaded
*
* @return Return NO to prevent the downloading of the image on cache misses. If not implemented, YES is implied.
*/
- (BOOL)imageManager:(EMSDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL;
/**
* Allows to transform the image immediately after it has been downloaded and just before to cache it on disk and memory.
* NOTE: This method is called from a global queue in order to not to block the main thread.
*
* @param imageManager The current `EMSDWebImageManager`
* @param image The image to transform
* @param imageURL The url of the image to transform
*
* @return The transformed image object.
*/
- (UIImage *)imageManager:(EMSDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL;
@end
/**
* The EMSDWebImageManager is the class behind the UIImageView+EMWebCache category and likes.
* It ties the asynchronous downloader (EMSDWebImageDownloader) with the image cache store (EMSDImageCache).
* You can use this class directly to benefit from web image downloading with caching in another context than
* a UIView.
*
* Here is a simple example of how to use EMSDWebImageManager:
*
* @code
EMSDWebImageManager *manager = [EMSDWebImageManager sharedManager];
[manager downloadWithURL:imageURL
options:0
progress:nil
completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
// do something with image
}
}];
* @endcode
*/
@interface EMSDWebImageManager : NSObject
@property (weak, nonatomic) id delegate;
@property (strong, nonatomic, readonly) EMSDImageCache *imageCache;
@property (strong, nonatomic, readonly) EMSDWebImageDownloader *imageDownloader;
/**
* The cache filter is a block used each time EMSDWebImageManager need to convert an URL into a cache key. This can
* be used to remove dynamic part of an image URL.
*
* The following example sets a filter in the application delegate that will remove any query-string from the
* URL before to use it as a cache key:
*
* @code
[[EMSDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) {
url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
return [url absoluteString];
}];
* @endcode
*/
@property (copy) EMSDWebImageCacheKeyFilterBlock cacheKeyFilter;
/**
* Returns global EMSDWebImageManager instance.
*
* @return EMSDWebImageManager shared instance
*/
+ (EMSDWebImageManager *)sharedManager;
/**
* Downloads the image at the given URL if not present in cache or return the cached version otherwise.
*
* @param url The URL to the image
* @param options A mask to specify options to use for this request
* @param progressBlock A block called while image is downloading
* @param completedBlock A block called when operation has been completed.
*
* This parameter is required.
*
* This block has no return value and takes the requested UIImage as first parameter.
* In case of error the image parameter is nil and the second parameter may contain an NSError.
*
* The third parameter is an `EMSDImageCacheType` enum indicating if the image was retrived from the local cache
* or from the memory cache or from the network.
*
* The last parameter is set to NO when the EMSDWebImageProgressiveDownload option is used and the image is
* downloading. This block is thus called repetidly with a partial image. When image is fully downloaded, the
* block is called a last time with the full image and the last parameter set to YES.
*
* @return Returns an NSObject conforming to EMSDWebImageOperation. Should be an instance of EMSDWebImageDownloaderOperation
*/
- (id )downloadImageWithURL:(NSURL *)url
options:(EMSDWebImageOptions)options
progress:(EMSDWebImageDownloaderProgressBlock)progressBlock
completed:(EMSDWebImageCompletionWithFinishedBlock)completedBlock;
/**
* Saves image to cache for given URL
*
* @param image The image to cache
* @param url The URL to the image
*
*/
- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url;
/**
* Cancel all current opreations
*/
- (void)cancelAll;
/**
* Check one or more operations running
*/
- (BOOL)isRunning;
/**
* Check if image has already been cached
*
* @param url image url
*
* @return if the image was already cached
*/
- (BOOL)cachedImageExistsForURL:(NSURL *)url;
/**
* Check if image has already been cached on disk only
*
* @param url image url
*
* @return if the image was already cached (disk only)
*/
- (BOOL)diskImageExistsForURL:(NSURL *)url;
/**
* Async check if image has already been cached
*
* @param url image url
* @param completionBlock the block to be executed when the check is finished
*
* @note the completion block is always executed on the main queue
*/
- (void)cachedImageExistsForURL:(NSURL *)url
completion:(EMSDWebImageCheckCacheCompletionBlock)completionBlock;
/**
* Async check if image has already been cached on disk only
*
* @param url image url
* @param completionBlock the block to be executed when the check is finished
*
* @note the completion block is always executed on the main queue
*/
- (void)diskImageExistsForURL:(NSURL *)url
completion:(EMSDWebImageCheckCacheCompletionBlock)completionBlock;
/**
*Return the cache key for a given URL
*/
- (NSString *)cacheKeyForURL:(NSURL *)url;
@end
#pragma mark - Deprecated
typedef void(^EMSDWebImageCompletedBlock)(UIImage *image, NSError *error, EMSDImageCacheType cacheType) __deprecated_msg("Block type deprecated. Use `EMSDWebImageCompletionBlock`");
typedef void(^EMSDWebImageCompletedWithFinishedBlock)(UIImage *image, NSError *error, EMSDImageCacheType cacheType, BOOL finished) __deprecated_msg("Block type deprecated. Use `EMSDWebImageCompletionWithFinishedBlock`");
@interface EMSDWebImageManager (Deprecated)
/**
* Downloads the image at the given URL if not present in cache or return the cached version otherwise.
*
* @deprecated This method has been deprecated. Use `downloadImageWithURL:options:progress:completed:`
*/
- (id )downloadWithURL:(NSURL *)url
options:(EMSDWebImageOptions)options
progress:(EMSDWebImageDownloaderProgressBlock)progressBlock
completed:(EMSDWebImageCompletedWithFinishedBlock)completedBlock __deprecated_msg("Method deprecated. Use `downloadImageWithURL:options:progress:completed:`");
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImageManager.m.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "EMSDWebImageManager.h"
#import
@interface EMSDWebImageCombinedOperation : NSObject
@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
@property (copy, nonatomic) EMSDWebImageNoParamsBlock cancelBlock;
@property (strong, nonatomic) NSOperation *cacheOperation;
@end
@interface EMSDWebImageManager ()
@property (strong, nonatomic, readwrite) EMSDImageCache *imageCache;
@property (strong, nonatomic, readwrite) EMSDWebImageDownloader *imageDownloader;
@property (strong, nonatomic) NSMutableArray *failedURLs;
@property (strong, nonatomic) NSMutableArray *runningOperations;
@end
@implementation EMSDWebImageManager
+ (id)sharedManager {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
- (id)init {
if ((self = [super init])) {
_imageCache = [self createCache];
_imageDownloader = [EMSDWebImageDownloader sharedDownloader];
_failedURLs = [NSMutableArray new];
_runningOperations = [NSMutableArray new];
}
return self;
}
- (EMSDImageCache *)createCache {
return [EMSDImageCache sharedImageCache];
}
- (NSString *)cacheKeyForURL:(NSURL *)url {
if (self.cacheKeyFilter) {
return self.cacheKeyFilter(url);
}
else {
return [url absoluteString];
}
}
- (BOOL)cachedImageExistsForURL:(NSURL *)url {
NSString *key = [self cacheKeyForURL:url];
if ([self.imageCache imageFromMemoryCacheForKey:key] != nil) return YES;
return [self.imageCache diskImageExistsWithKey:key];
}
- (BOOL)diskImageExistsForURL:(NSURL *)url {
NSString *key = [self cacheKeyForURL:url];
return [self.imageCache diskImageExistsWithKey:key];
}
- (void)cachedImageExistsForURL:(NSURL *)url
completion:(EMSDWebImageCheckCacheCompletionBlock)completionBlock {
NSString *key = [self cacheKeyForURL:url];
BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil);
if (isInMemoryCache) {
// making sure we call the completion block on the main queue
dispatch_async(dispatch_get_main_queue(), ^{
if (completionBlock) {
completionBlock(YES);
}
});
return;
}
[self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
// the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch
if (completionBlock) {
completionBlock(isInDiskCache);
}
}];
}
- (void)diskImageExistsForURL:(NSURL *)url
completion:(EMSDWebImageCheckCacheCompletionBlock)completionBlock {
NSString *key = [self cacheKeyForURL:url];
[self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
// the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch
if (completionBlock) {
completionBlock(isInDiskCache);
}
}];
}
- (id )downloadImageWithURL:(NSURL *)url
options:(EMSDWebImageOptions)options
progress:(EMSDWebImageDownloaderProgressBlock)progressBlock
completed:(EMSDWebImageCompletionWithFinishedBlock)completedBlock {
// Invoking this method without a completedBlock is pointless
NSParameterAssert(completedBlock);
// Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't
// throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
// Prevents app crashing on argument type error like sending NSNull instead of NSURL
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
__block EMSDWebImageCombinedOperation *operation = [EMSDWebImageCombinedOperation new];
__weak EMSDWebImageCombinedOperation *weakOperation = operation;
BOOL isFailedUrl = NO;
@synchronized (self.failedURLs) {
isFailedUrl = [self.failedURLs containsObject:url];
}
if (!url || (!(options & EMSDWebImageRetryFailed) && isFailedUrl)) {
dispatch_main_sync_safe(^{
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
completedBlock(nil, error, EMSDImageCacheTypeNone, YES, url);
});
return operation;
}
@synchronized (self.runningOperations) {
[self.runningOperations addObject:operation];
}
NSString *key = [self cacheKeyForURL:url];
operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, EMSDImageCacheType cacheType) {
if (operation.isCancelled) {
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
return;
}
if ((!image || options & EMSDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
if (image && options & EMSDWebImageRefreshCached) {
dispatch_main_sync_safe(^{
// If image was found in the cache bug EMSDWebImageRefreshCached is provided, notify about the cached image
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
completedBlock(image, nil, cacheType, YES, url);
});
}
// download if no image or requested to refresh anyway, and download allowed by delegate
EMSDWebImageDownloaderOptions downloaderOptions = 0;
if (options & EMSDWebImageLowPriority) downloaderOptions |= EMSDWebImageDownloaderLowPriority;
if (options & EMSDWebImageProgressiveDownload) downloaderOptions |= EMSDWebImageDownloaderProgressiveDownload;
if (options & EMSDWebImageRefreshCached) downloaderOptions |= EMSDWebImageDownloaderUseNSURLCache;
if (options & EMSDWebImageContinueInBackground) downloaderOptions |= EMSDWebImageDownloaderContinueInBackground;
if (options & EMSDWebImageHandleCookies) downloaderOptions |= EMSDWebImageDownloaderHandleCookies;
if (options & EMSDWebImageAllowInvalidSSLCertificates) downloaderOptions |= EMSDWebImageDownloaderAllowInvalidSSLCertificates;
if (options & EMSDWebImageHighPriority) downloaderOptions |= EMSDWebImageDownloaderHighPriority;
if (image && options & EMSDWebImageRefreshCached) {
// force progressive off if image already cached but forced refreshing
downloaderOptions &= ~EMSDWebImageDownloaderProgressiveDownload;
// ignore image read from NSURLCache if image if cached but force refreshing
downloaderOptions |= EMSDWebImageDownloaderIgnoreCachedResponse;
}
id subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {
if (weakOperation.isCancelled) {
// Do nothing if the operation was cancelled
// See #699 for more details
// if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
}
else if (error) {
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(nil, error, EMSDImageCacheTypeNone, finished, url);
}
});
if (error.code != NSURLErrorNotConnectedToInternet && error.code != NSURLErrorCancelled && error.code != NSURLErrorTimedOut) {
@synchronized (self.failedURLs) {
[self.failedURLs addObject:url];
}
}
}
else {
BOOL cacheOnDisk = !(options & EMSDWebImageCacheMemoryOnly);
if (options & EMSDWebImageRefreshCached && image && !downloadedImage) {
// Image refresh hit the NSURLCache cache, do not call the completion block
}
// NOTE: We don't call transformDownloadedImage delegate method on animated images as most transformation code would mangle it
else if (downloadedImage && !downloadedImage.images && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
[self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:data forKey:key toDisk:cacheOnDisk];
}
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(transformedImage, nil, EMSDImageCacheTypeNone, finished, url);
}
});
});
}
else {
if (downloadedImage && finished) {
[self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
}
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(downloadedImage, nil, EMSDImageCacheTypeNone, finished, url);
}
});
}
}
if (finished) {
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
}
}];
operation.cancelBlock = ^{
[subOperation cancel];
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:weakOperation];
}
};
}
else if (image) {
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(image, nil, cacheType, YES, url);
}
});
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
}
else {
// Image not in cache and download disallowed by delegate
dispatch_main_sync_safe(^{
if (!weakOperation.isCancelled) {
completedBlock(nil, nil, EMSDImageCacheTypeNone, YES, url);
}
});
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
}
}];
return operation;
}
- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url {
if (image && url) {
NSString *key = [self cacheKeyForURL:url];
[self.imageCache storeImage:image forKey:key toDisk:YES];
}
}
- (void)cancelAll {
@synchronized (self.runningOperations) {
[self.runningOperations makeObjectsPerformSelector:@selector(cancel)];
[self.runningOperations removeAllObjects];
}
}
- (BOOL)isRunning {
return self.runningOperations.count > 0;
}
@end
@implementation EMSDWebImageCombinedOperation
- (void)setCancelBlock:(EMSDWebImageNoParamsBlock)cancelBlock {
// check if the operation is already cancelled, then we just call the cancelBlock
if (self.isCancelled) {
if (cancelBlock) {
cancelBlock();
}
_cancelBlock = nil; // don't forget to nil the cancelBlock, otherwise we will get crashes
} else {
_cancelBlock = [cancelBlock copy];
}
}
- (void)cancel {
self.cancelled = YES;
if (self.cacheOperation) {
[self.cacheOperation cancel];
self.cacheOperation = nil;
}
if (self.cancelBlock) {
self.cancelBlock();
// TODO: this is a temporary fix to #809.
// Until we can figure the exact cause of the crash, going with the ivar instead of the setter
// self.cancelBlock = nil;
_cancelBlock = nil;
}
}
@end
@implementation EMSDWebImageManager (Deprecated)
// deprecated method, uses the non deprecated method
// adapter for the completion block
- (id )downloadWithURL:(NSURL *)url options:(EMSDWebImageOptions)options progress:(EMSDWebImageDownloaderProgressBlock)progressBlock completed:(EMSDWebImageCompletedWithFinishedBlock)completedBlock {
return [self downloadImageWithURL:url
options:options
progress:progressBlock
completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (completedBlock) {
completedBlock(image, error, cacheType, finished);
}
}];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImageOperation.h.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import
@protocol EMSDWebImageOperation
- (void)cancel;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImagePrefetcher.h.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import
#import "EMSDWebImageManager.h"
@class EMSDWebImagePrefetcher;
@protocol EMSDWebImagePrefetcherDelegate
@optional
/**
* Called when an image was prefetched.
*
* @param imagePrefetcher The current image prefetcher
* @param imageURL The image url that was prefetched
* @param finishedCount The total number of images that were prefetched (successful or not)
* @param totalCount The total number of images that were to be prefetched
*/
- (void)imagePrefetcher:(EMSDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount;
/**
* Called when all images are prefetched.
* @param imagePrefetcher The current image prefetcher
* @param totalCount The total number of images that were prefetched (whether successful or not)
* @param skippedCount The total number of images that were skipped
*/
- (void)imagePrefetcher:(EMSDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount;
@end
typedef void(^EMSDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls);
typedef void(^EMSDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls);
/**
* Prefetch some URLs in the cache for future use. Images are downloaded in low priority.
*/
@interface EMSDWebImagePrefetcher : NSObject
/**
* The web image manager
*/
@property (strong, nonatomic, readonly) EMSDWebImageManager *manager;
/**
* Maximum number of URLs to prefetch at the same time. Defaults to 3.
*/
@property (nonatomic, assign) NSUInteger maxConcurrentDownloads;
/**
* EMSDWebImageOptions for prefetcher. Defaults to EMSDWebImageLowPriority.
*/
@property (nonatomic, assign) EMSDWebImageOptions options;
@property (weak, nonatomic) id delegate;
/**
* Return the global image prefetcher instance.
*/
+ (EMSDWebImagePrefetcher *)sharedImagePrefetcher;
/**
* Assign list of URLs to let EMSDWebImagePrefetcher to queue the prefetching,
* currently one image is downloaded at a time,
* and skips images for failed downloads and proceed to the next image in the list
*
* @param urls list of URLs to prefetch
*/
- (void)prefetchURLs:(NSArray *)urls;
/**
* Assign list of URLs to let EMSDWebImagePrefetcher to queue the prefetching,
* currently one image is downloaded at a time,
* and skips images for failed downloads and proceed to the next image in the list
*
* @param urls list of URLs to prefetch
* @param progressBlock block to be called when progress updates;
* first parameter is the number of completed (successful or not) requests,
* second parameter is the total number of images originally requested to be prefetched
* @param completionBlock block to be called when prefetching is completed
* first param is the number of completed (successful or not) requests,
* second parameter is the number of skipped requests
*/
- (void)prefetchURLs:(NSArray *)urls progress:(EMSDWebImagePrefetcherProgressBlock)progressBlock completed:(EMSDWebImagePrefetcherCompletionBlock)completionBlock;
/**
* Remove and cancel queued list
*/
- (void)cancelPrefetching;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/EMSDWebImagePrefetcher.m.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "EMSDWebImagePrefetcher.h"
#if !defined(DEBUG) && !defined (SD_VERBOSE)
#define NSLog(...)
#endif
@interface EMSDWebImagePrefetcher ()
@property (strong, nonatomic) EMSDWebImageManager *manager;
@property (strong, nonatomic) NSArray *prefetchURLs;
@property (assign, nonatomic) NSUInteger requestedCount;
@property (assign, nonatomic) NSUInteger skippedCount;
@property (assign, nonatomic) NSUInteger finishedCount;
@property (assign, nonatomic) NSTimeInterval startedTime;
@property (copy, nonatomic) EMSDWebImagePrefetcherCompletionBlock completionBlock;
@property (copy, nonatomic) EMSDWebImagePrefetcherProgressBlock progressBlock;
@end
@implementation EMSDWebImagePrefetcher
+ (EMSDWebImagePrefetcher *)sharedImagePrefetcher {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
- (id)init {
if ((self = [super init])) {
_manager = [EMSDWebImageManager new];
_options = EMSDWebImageLowPriority;
self.maxConcurrentDownloads = 3;
}
return self;
}
- (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads {
self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads;
}
- (NSUInteger)maxConcurrentDownloads {
return self.manager.imageDownloader.maxConcurrentDownloads;
}
- (void)startPrefetchingAtIndex:(NSUInteger)index {
if (index >= self.prefetchURLs.count) return;
self.requestedCount++;
[self.manager downloadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (!finished) return;
self.finishedCount++;
if (image) {
if (self.progressBlock) {
self.progressBlock(self.finishedCount,[self.prefetchURLs count]);
}
NSLog(@"Prefetched %@ out of %@", @(self.finishedCount), @(self.prefetchURLs.count));
}
else {
if (self.progressBlock) {
self.progressBlock(self.finishedCount,[self.prefetchURLs count]);
}
NSLog(@"Prefetched %@ out of %@ (Failed)", @(self.finishedCount), @(self.prefetchURLs.count));
// Add last failed
self.skippedCount++;
}
if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) {
[self.delegate imagePrefetcher:self
didPrefetchURL:self.prefetchURLs[index]
finishedCount:self.finishedCount
totalCount:self.prefetchURLs.count
];
}
if (self.prefetchURLs.count > self.requestedCount) {
dispatch_async(dispatch_get_main_queue(), ^{
[self startPrefetchingAtIndex:self.requestedCount];
});
}
else if (self.finishedCount == self.requestedCount) {
[self reportStatus];
if (self.completionBlock) {
self.completionBlock(self.finishedCount, self.skippedCount);
self.completionBlock = nil;
}
}
}];
}
- (void)reportStatus {
NSUInteger total = [self.prefetchURLs count];
NSLog(@"Finished prefetching (%@ successful, %@ skipped, timeElasped %.2f)", @(total - self.skippedCount), @(self.skippedCount), CFAbsoluteTimeGetCurrent() - self.startedTime);
if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) {
[self.delegate imagePrefetcher:self
didFinishWithTotalCount:(total - self.skippedCount)
skippedCount:self.skippedCount
];
}
}
- (void)prefetchURLs:(NSArray *)urls {
[self prefetchURLs:urls progress:nil completed:nil];
}
- (void)prefetchURLs:(NSArray *)urls progress:(EMSDWebImagePrefetcherProgressBlock)progressBlock completed:(EMSDWebImagePrefetcherCompletionBlock)completionBlock {
[self cancelPrefetching]; // Prevent duplicate prefetch request
self.startedTime = CFAbsoluteTimeGetCurrent();
self.prefetchURLs = urls;
self.completionBlock = completionBlock;
self.progressBlock = progressBlock;
// Starts prefetching from the very first image on the list with the max allowed concurrency
NSUInteger listCount = self.prefetchURLs.count;
for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) {
[self startPrefetchingAtIndex:i];
}
}
- (void)cancelPrefetching {
self.prefetchURLs = nil;
self.skippedCount = 0;
self.requestedCount = 0;
self.finishedCount = 0;
[self.manager cancelAll];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/MKAnnotationView+EMWebCache.h.svn-base
================================================
//
// MKAnnotationView+WebCache.h
// SDWebImage
//
// Created by Olivier Poitrey on 14/03/12.
// Copyright (c) 2012 Dailymotion. All rights reserved.
//
#import "MapKit/MapKit.h"
#import "EMSDWebImageManager.h"
/**
* Integrates SDWebImage async downloading and caching of remote images with MKAnnotationView.
*/
@interface MKAnnotationView (EMWebCache)
/**
* Get the current image URL.
*
* Note that because of the limitations of categories this property can get out of sync
* if you use sd_setImage: directly.
*/
- (NSURL *)sd_imageURL;
/**
* Set the imageView `image` with an `url`.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
*/
- (void)sd_setImageWithURL:(NSURL *)url;
/**
* Set the imageView `image` with an `url` and a placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholder The image to be set initially, until the image request finishes.
* @see sd_setImageWithURL:placeholderImage:options:
*/
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;
/**
* Set the imageView `image` with an `url`, placeholder and custom options.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see EMSDWebImageOptions for the possible values.
*/
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options;
/**
* Set the imageView `image` with an `url`.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param completedBlock A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
* The forth parameter is the original image url.
*/
- (void)sd_setImageWithURL:(NSURL *)url completed:(EMSDWebImageCompletionBlock)completedBlock;
/**
* Set the imageView `image` with an `url`, placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholder The image to be set initially, until the image request finishes.
* @param completedBlock A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
* The forth parameter is the original image url.
*/
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletionBlock)completedBlock;
/**
* Set the imageView `image` with an `url`, placeholder and custom options.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see EMSDWebImageOptions for the possible values.
* @param completedBlock A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
* The forth parameter is the original image url.
*/
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletionBlock)completedBlock;
/**
* Cancel the current download
*/
- (void)sd_cancelCurrentImageLoad;
@end
@interface MKAnnotationView (WebCacheDeprecated)
- (NSURL *)imageURL __deprecated_msg("Use `sd_imageURL`");
- (void)setImageWithURL:(NSURL *)url __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:`");
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:`");
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options:`");
- (void)setImageWithURL:(NSURL *)url completed:(EMSDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:completed:`");
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:completed:`");
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:placeholderImage:options:completed:`");
- (void)cancelCurrentImageLoad __deprecated_msg("Use `sd_cancelCurrentImageLoad`");
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/MKAnnotationView+EMWebCache.m.svn-base
================================================
//
// MKAnnotationView+WebCache.m
// SDWebImage
//
// Created by Olivier Poitrey on 14/03/12.
// Copyright (c) 2012 Dailymotion. All rights reserved.
//
#import "MKAnnotationView+EMWebCache.h"
#import "objc/runtime.h"
#import "UIView+EMWebCacheOperation.h"
static char imageURLKey;
@implementation MKAnnotationView (EMWebCache)
- (NSURL *)sd_imageURL {
return objc_getAssociatedObject(self, &imageURLKey);
}
- (void)sd_setImageWithURL:(NSURL *)url {
[self sd_setImageWithURL:url placeholderImage:nil options:0 completed:nil];
}
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
[self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:nil];
}
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options {
[self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:nil];
}
- (void)sd_setImageWithURL:(NSURL *)url completed:(EMSDWebImageCompletionBlock)completedBlock {
[self sd_setImageWithURL:url placeholderImage:nil options:0 completed:completedBlock];
}
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletionBlock)completedBlock {
[self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:completedBlock];
}
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletionBlock)completedBlock {
[self sd_cancelCurrentImageLoad];
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.image = placeholder;
if (url) {
__weak MKAnnotationView *wself = self;
id operation = [EMSDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (!wself) return;
dispatch_main_sync_safe(^{
__strong MKAnnotationView *sself = wself;
if (!sself) return;
if (image) {
sself.image = image;
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setImageLoadOperation:operation forKey:@"MKAnnotationViewImage"];
} else {
dispatch_main_async_safe(^{
NSError *error = [NSError errorWithDomain:@"SDWebImageErrorDomain" code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
if (completedBlock) {
completedBlock(nil, error, EMSDImageCacheTypeNone, url);
}
});
}
}
- (void)sd_cancelCurrentImageLoad {
[self sd_cancelImageLoadOperationWithKey:@"MKAnnotationViewImage"];
}
@end
@implementation MKAnnotationView (WebCacheDeprecated)
- (NSURL *)imageURL {
return [self sd_imageURL];
}
- (void)setImageWithURL:(NSURL *)url {
[self sd_setImageWithURL:url placeholderImage:nil options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
[self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options {
[self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:nil];
}
- (void)setImageWithURL:(NSURL *)url completed:(EMSDWebImageCompletedBlock)completedBlock {
[self sd_setImageWithURL:url placeholderImage:nil options:0 completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, NSURL *imageURL) {
if (completedBlock) {
completedBlock(image, error, cacheType);
}
}];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletedBlock)completedBlock {
[self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, NSURL *imageURL) {
if (completedBlock) {
completedBlock(image, error, cacheType);
}
}];
}
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletedBlock)completedBlock {
[self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, NSURL *imageURL) {
if (completedBlock) {
completedBlock(image, error, cacheType);
}
}];
}
- (void)cancelCurrentImageLoad {
[self sd_cancelCurrentImageLoad];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/NSData+EMImageContentType.h.svn-base
================================================
//
// Created by Fabrice Aneche on 06/01/14.
// Copyright (c) 2014 Dailymotion. All rights reserved.
//
#import
@interface NSData (EMImageContentType)
/**
* Compute the content type for an image data
*
* @param data the input data
*
* @return the content type as string (i.e. image/jpeg, image/gif)
*/
+ (NSString *)sd_contentTypeForImageData:(NSData *)data;
@end
@interface NSData (ImageContentTypeDeprecated)
+ (NSString *)contentTypeForImageData:(NSData *)data __deprecated_msg("Use `sd_contentTypeForImageData:`");
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/NSData+EMImageContentType.m.svn-base
================================================
//
// Created by Fabrice Aneche on 06/01/14.
// Copyright (c) 2014 Dailymotion. All rights reserved.
//
#import "NSData+EMImageContentType.h"
@implementation NSData (EMImageContentType)
+ (NSString *)sd_contentTypeForImageData:(NSData *)data {
uint8_t c;
[data getBytes:&c length:1];
switch (c) {
case 0xFF:
return @"image/jpeg";
case 0x89:
return @"image/png";
case 0x47:
return @"image/gif";
case 0x49:
case 0x4D:
return @"image/tiff";
case 0x52:
// R as RIFF for WEBP
if ([data length] < 12) {
return nil;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
return @"image/webp";
}
return nil;
}
return nil;
}
@end
@implementation NSData (ImageContentTypeDeprecated)
+ (NSString *)contentTypeForImageData:(NSData *)data {
return [self sd_contentTypeForImageData:data];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/UIButton+EMWebCache.h.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "EMSDWebImageCompat.h"
#import "EMSDWebImageManager.h"
/**
* Integrates SDWebImage async downloading and caching of remote images with UIButtonView.
*/
@interface UIButton (EMWebCache)
/**
* Get the current image URL.
*/
- (NSURL *)sd_currentImageURL;
/**
* Get the image URL for a control state.
*
* @param state Which state you want to know the URL for. The values are described in UIControlState.
*/
- (NSURL *)sd_imageURLForState:(UIControlState)state;
/**
* Set the imageView `image` with an `url`.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
*/
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state;
/**
* Set the imageView `image` with an `url` and a placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @see sd_setImageWithURL:placeholderImage:options:
*/
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder;
/**
* Set the imageView `image` with an `url`, placeholder and custom options.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see EMSDWebImageOptions for the possible values.
*/
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options;
/**
* Set the imageView `image` with an `url`.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param completedBlock A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
* The forth parameter is the original image url.
*/
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(EMSDWebImageCompletionBlock)completedBlock;
/**
* Set the imageView `image` with an `url`, placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @param completedBlock A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
* The forth parameter is the original image url.
*/
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletionBlock)completedBlock;
/**
* Set the imageView `image` with an `url`, placeholder and custom options.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see EMSDWebImageOptions for the possible values.
* @param completedBlock A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
* The forth parameter is the original image url.
*/
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletionBlock)completedBlock;
/**
* Set the backgroundImageView `image` with an `url`.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
*/
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state;
/**
* Set the backgroundImageView `image` with an `url` and a placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @see sd_setImageWithURL:placeholderImage:options:
*/
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder;
/**
* Set the backgroundImageView `image` with an `url`, placeholder and custom options.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see EMSDWebImageOptions for the possible values.
*/
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options;
/**
* Set the backgroundImageView `image` with an `url`.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param completedBlock A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
* The forth parameter is the original image url.
*/
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(EMSDWebImageCompletionBlock)completedBlock;
/**
* Set the backgroundImageView `image` with an `url`, placeholder.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param state The state that uses the specified title. The values are described in UIControlState.
* @param placeholder The image to be set initially, until the image request finishes.
* @param completedBlock A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
* The forth parameter is the original image url.
*/
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletionBlock)completedBlock;
/**
* Set the backgroundImageView `image` with an `url`, placeholder and custom options.
*
* The download is asynchronous and cached.
*
* @param url The url for the image.
* @param placeholder The image to be set initially, until the image request finishes.
* @param options The options to use when downloading the image. @see EMSDWebImageOptions for the possible values.
* @param completedBlock A block called when operation has been completed. This block has no return value
* and takes the requested UIImage as first parameter. In case of error the image parameter
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
* indicating if the image was retrived from the local cache of from the network.
* The forth parameter is the original image url.
*/
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletionBlock)completedBlock;
/**
* Cancel the current image download
*/
- (void)sd_cancelImageLoadForState:(UIControlState)state;
/**
* Cancel the current backgroundImage download
*/
- (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state;
@end
@interface UIButton (WebCacheDeprecated)
- (NSURL *)currentImageURL __deprecated_msg("Use `sd_currentImageURL`");
- (NSURL *)imageURLForState:(UIControlState)state __deprecated_msg("Use `sd_imageURLForState:`");
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:`");
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:`");
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:options:`");
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(EMSDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:completed:`");
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:completed:`");
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setImageWithURL:forState:placeholderImage:options:completed:`");
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:`");
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:`");
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:options:`");
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(EMSDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:completed:`");
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:completed:`");
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletedBlock)completedBlock __deprecated_msg("Method deprecated. Use `sd_setBackgroundImageWithURL:forState:placeholderImage:options:completed:`");
- (void)cancelCurrentImageLoad __deprecated_msg("Use `sd_cancelImageLoadForState:`");
- (void)cancelBackgroundImageLoadForState:(UIControlState)state __deprecated_msg("Use `sd_cancelBackgroundImageLoadForState:`");
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/UIButton+EMWebCache.m.svn-base
================================================
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "UIButton+EMWebCache.h"
#import "objc/runtime.h"
#import "UIView+EMWebCacheOperation.h"
static char imageURLStorageKey;
@implementation UIButton (EMWebCache)
- (NSURL *)sd_currentImageURL {
NSURL *url = self.imageURLStorage[@(self.state)];
if (!url) {
url = self.imageURLStorage[@(UIControlStateNormal)];
}
return url;
}
- (NSURL *)sd_imageURLForState:(UIControlState)state {
return self.imageURLStorage[@(state)];
}
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state {
[self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
}
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder {
[self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
}
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options {
[self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil];
}
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(EMSDWebImageCompletionBlock)completedBlock {
[self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock];
}
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletionBlock)completedBlock {
[self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock];
}
- (void)sd_setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletionBlock)completedBlock {
[self setImage:placeholder forState:state];
[self sd_cancelImageLoadForState:state];
if (!url) {
[self.imageURLStorage removeObjectForKey:@(state)];
dispatch_main_async_safe(^{
NSError *error = [NSError errorWithDomain:@"SDWebImageErrorDomain" code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
if (completedBlock) {
completedBlock(nil, error, EMSDImageCacheTypeNone, url);
}
});
return;
}
self.imageURLStorage[@(state)] = url;
__weak UIButton *wself = self;
id operation = [EMSDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (!wself) return;
dispatch_main_sync_safe(^{
__strong UIButton *sself = wself;
if (!sself) return;
if (image) {
[sself setImage:image forState:state];
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setImageLoadOperation:operation forState:state];
}
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
}
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
}
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil];
}
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(EMSDWebImageCompletionBlock)completedBlock {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock];
}
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletionBlock)completedBlock {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock];
}
- (void)sd_setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletionBlock)completedBlock {
[self sd_cancelImageLoadForState:state];
[self setBackgroundImage:placeholder forState:state];
if (url) {
__weak UIButton *wself = self;
id operation = [EMSDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (!wself) return;
dispatch_main_sync_safe(^{
__strong UIButton *sself = wself;
if (!sself) return;
if (image) {
[sself setBackgroundImage:image forState:state];
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
[self sd_setBackgroundImageLoadOperation:operation forState:state];
} else {
dispatch_main_async_safe(^{
NSError *error = [NSError errorWithDomain:@"SDWebImageErrorDomain" code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
if (completedBlock) {
completedBlock(nil, error, EMSDImageCacheTypeNone, url);
}
});
}
}
- (void)sd_setImageLoadOperation:(id)operation forState:(UIControlState)state {
[self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]];
}
- (void)sd_cancelImageLoadForState:(UIControlState)state {
[self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]];
}
- (void)sd_setBackgroundImageLoadOperation:(id)operation forState:(UIControlState)state {
[self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]];
}
- (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state {
[self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]];
}
- (NSMutableDictionary *)imageURLStorage {
NSMutableDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey);
if (!storage)
{
storage = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return storage;
}
@end
@implementation UIButton (WebCacheDeprecated)
- (NSURL *)currentImageURL {
return [self sd_currentImageURL];
}
- (NSURL *)imageURLForState:(UIControlState)state {
return [self sd_imageURLForState:state];
}
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state {
[self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder {
[self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
}
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options {
[self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil];
}
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(EMSDWebImageCompletedBlock)completedBlock {
[self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, NSURL *imageURL) {
if (completedBlock) {
completedBlock(image, error, cacheType);
}
}];
}
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletedBlock)completedBlock {
[self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, NSURL *imageURL) {
if (completedBlock) {
completedBlock(image, error, cacheType);
}
}];
}
- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletedBlock)completedBlock {
[self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, NSURL *imageURL) {
if (completedBlock) {
completedBlock(image, error, cacheType);
}
}];
}
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
}
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
}
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil];
}
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(EMSDWebImageCompletedBlock)completedBlock {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, NSURL *imageURL) {
if (completedBlock) {
completedBlock(image, error, cacheType);
}
}];
}
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(EMSDWebImageCompletedBlock)completedBlock {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, NSURL *imageURL) {
if (completedBlock) {
completedBlock(image, error, cacheType);
}
}];
}
- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(EMSDWebImageOptions)options completed:(EMSDWebImageCompletedBlock)completedBlock {
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, NSURL *imageURL) {
if (completedBlock) {
completedBlock(image, error, cacheType);
}
}];
}
- (void)cancelCurrentImageLoad {
// in a backwards compatible manner, cancel for current state
[self sd_cancelImageLoadForState:self.state];
}
- (void)cancelBackgroundImageLoadForState:(UIControlState)state {
[self sd_cancelBackgroundImageLoadForState:state];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/UIImage+EMGIF.h.svn-base
================================================
//
// UIImage+GIF.h
// LBGIFImage
//
// Created by Laurin Brandner on 06.01.12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import
@interface UIImage (EMGIF)
+ (UIImage *)sd_animatedGIFNamed:(NSString *)name;
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data;
- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/UIImage+EMGIF.m.svn-base
================================================
//
// UIImage+GIF.m
// LBGIFImage
//
// Created by Laurin Brandner on 06.01.12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "UIImage+EMGIF.h"
#import
@implementation UIImage (EMGIF)
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data {
if (!data) {
return nil;
}
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
size_t count = CGImageSourceGetCount(source);
UIImage *animatedImage;
if (count <= 1) {
animatedImage = [[UIImage alloc] initWithData:data];
}
else {
NSMutableArray *images = [NSMutableArray array];
NSTimeInterval duration = 0.0f;
for (size_t i = 0; i < count; i++) {
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
duration += [self sd_frameDurationAtIndex:i source:source];
[images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
CGImageRelease(image);
}
if (!duration) {
duration = (1.0f / 10.0f) * count;
}
animatedImage = [UIImage animatedImageWithImages:images duration:duration];
}
CFRelease(source);
return animatedImage;
}
+ (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
float frameDuration = 0.1f;
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];
NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
if (delayTimeUnclampedProp) {
frameDuration = [delayTimeUnclampedProp floatValue];
}
else {
NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
if (delayTimeProp) {
frameDuration = [delayTimeProp floatValue];
}
}
// Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
// We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
// a duration of <= 10 ms. See and
// for more information.
if (frameDuration < 0.011f) {
frameDuration = 0.100f;
}
CFRelease(cfFrameProperties);
return frameDuration;
}
+ (UIImage *)sd_animatedGIFNamed:(NSString *)name {
CGFloat scale = [UIScreen mainScreen].scale;
if (scale > 1.0f) {
NSString *retinaPath = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"gif"];
NSData *data = [NSData dataWithContentsOfFile:retinaPath];
if (data) {
return [UIImage sd_animatedGIFWithData:data];
}
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"];
data = [NSData dataWithContentsOfFile:path];
if (data) {
return [UIImage sd_animatedGIFWithData:data];
}
return [UIImage imageNamed:name];
}
else {
NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"];
NSData *data = [NSData dataWithContentsOfFile:path];
if (data) {
return [UIImage sd_animatedGIFWithData:data];
}
return [UIImage imageNamed:name];
}
}
- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size {
if (CGSizeEqualToSize(self.size, size) || CGSizeEqualToSize(size, CGSizeZero)) {
return self;
}
CGSize scaledSize = size;
CGPoint thumbnailPoint = CGPointZero;
CGFloat widthFactor = size.width / self.size.width;
CGFloat heightFactor = size.height / self.size.height;
CGFloat scaleFactor = (widthFactor > heightFactor) ? widthFactor : heightFactor;
scaledSize.width = self.size.width * scaleFactor;
scaledSize.height = self.size.height * scaleFactor;
if (widthFactor > heightFactor) {
thumbnailPoint.y = (size.height - scaledSize.height) * 0.5;
}
else if (widthFactor < heightFactor) {
thumbnailPoint.x = (size.width - scaledSize.width) * 0.5;
}
NSMutableArray *scaledImages = [NSMutableArray array];
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
for (UIImage *image in self.images) {
[image drawInRect:CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledSize.width, scaledSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
[scaledImages addObject:newImage];
}
UIGraphicsEndImageContext();
return [UIImage animatedImageWithImages:scaledImages duration:self.duration];
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/UIImage+EMMultiFormat.h.svn-base
================================================
//
// UIImage+EMMultiFormat.h
// SDWebImage
//
// Created by Olivier Poitrey on 07/06/13.
// Copyright (c) 2013 Dailymotion. All rights reserved.
//
#import
@interface UIImage (EMMultiFormat)
+ (UIImage *)sd_imageWithData:(NSData *)data;
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/UIImage+EMMultiFormat.m.svn-base
================================================
//
// UIImage+EMMultiFormat.m
// SDWebImage
//
// Created by Olivier Poitrey on 07/06/13.
// Copyright (c) 2013 Dailymotion. All rights reserved.
//
#import "UIImage+EMMultiFormat.h"
#import "UIImage+EMGIF.h"
#import "NSData+EMImageContentType.h"
#import
#ifdef SD_WEBP
#import "UIImage+WebP.h"
#endif
@implementation UIImage (EMMultiFormat)
+ (UIImage *)sd_imageWithData:(NSData *)data {
UIImage *image;
NSString *imageContentType = [NSData sd_contentTypeForImageData:data];
if ([imageContentType isEqualToString:@"image/gif"]) {
image = [UIImage sd_animatedGIFWithData:data];
}
#ifdef SD_WEBP
else if ([imageContentType isEqualToString:@"image/webp"])
{
image = [UIImage sd_imageWithWebPData:data];
}
#endif
else {
image = [[UIImage alloc] initWithData:data];
UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data];
if (orientation != UIImageOrientationUp) {
image = [UIImage imageWithCGImage:image.CGImage
scale:image.scale
orientation:orientation];
}
}
return image;
}
+(UIImageOrientation)sd_imageOrientationFromImageData:(NSData *)imageData {
UIImageOrientation result = UIImageOrientationUp;
CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
if (imageSource) {
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
if (properties) {
CFTypeRef val;
int exifOrientation;
val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);
if (val) {
CFNumberGetValue(val, kCFNumberIntType, &exifOrientation);
result = [self sd_exifOrientationToiOSOrientation:exifOrientation];
} // else - if it's not set it remains at up
CFRelease((CFTypeRef) properties);
} else {
//NSLog(@"NO PROPERTIES, FAIL");
}
CFRelease(imageSource);
}
return result;
}
#pragma mark EXIF orientation tag converter
// Convert an EXIF image orientation to an iOS one.
// reference see here: http://sylvana.net/jpegcrop/exif_orientation.html
+ (UIImageOrientation) sd_exifOrientationToiOSOrientation:(int)exifOrientation {
UIImageOrientation orientation = UIImageOrientationUp;
switch (exifOrientation) {
case 1:
orientation = UIImageOrientationUp;
break;
case 3:
orientation = UIImageOrientationDown;
break;
case 8:
orientation = UIImageOrientationLeft;
break;
case 6:
orientation = UIImageOrientationRight;
break;
case 2:
orientation = UIImageOrientationUpMirrored;
break;
case 4:
orientation = UIImageOrientationDownMirrored;
break;
case 5:
orientation = UIImageOrientationLeftMirrored;
break;
case 7:
orientation = UIImageOrientationRightMirrored;
break;
default:
break;
}
return orientation;
}
@end
================================================
FILE: SWFormExample/SWFormExample/Vendors/MWPhotoBrowser/Libraries/EMSDWebImage/.svn/text-base/UIImage+EMWebP.h.svn-base
================================================
//
// UIImage+WebP.h
// SDWebImage
//
// Created by Olivier Poitrey on 07/06/13.
// Copyright (c) 2013 Dailymotion. All rights reserved.
//
#ifdef SD_WEBP
#import