From 25b86d99a9d82af78479a93e5748c3dd2d4378e4 Mon Sep 17 00:00:00 2001 From: John Kuan Date: Sat, 2 Dec 2017 00:18:47 +0800 Subject: [PATCH 01/13] added in files from Burpple project --- Fusuma.xcodeproj/project.pbxproj | 16 ++ Sources/ArrowImageView.swift | 72 +++++ Sources/ArrowableTitleView.swift | 103 +++++++ .../ic_arrow.imageset/Contents.json | 21 ++ .../ic_arrow.imageset/expandArrowCopy.pdf | Bin 0 -> 4024 bytes Sources/FSAlbumSelectionTableViewCell.swift | 99 +++++++ Sources/FSAlbumSelectionViewController.swift | 264 ++++++++++++++++++ 7 files changed, 575 insertions(+) create mode 100644 Sources/ArrowImageView.swift create mode 100644 Sources/ArrowableTitleView.swift create mode 100644 Sources/Assets.xcassets/ic_arrow.imageset/Contents.json create mode 100644 Sources/Assets.xcassets/ic_arrow.imageset/expandArrowCopy.pdf create mode 100644 Sources/FSAlbumSelectionTableViewCell.swift create mode 100644 Sources/FSAlbumSelectionViewController.swift diff --git a/Fusuma.xcodeproj/project.pbxproj b/Fusuma.xcodeproj/project.pbxproj index 793943e5..84e15dd3 100644 --- a/Fusuma.xcodeproj/project.pbxproj +++ b/Fusuma.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 4786CC771FD1B56900B6A97C /* FSAlbumSelectionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC741FD1B56700B6A97C /* FSAlbumSelectionTableViewCell.swift */; }; + 4786CC781FD1B56900B6A97C /* ArrowableTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC751FD1B56800B6A97C /* ArrowableTitleView.swift */; }; + 4786CC791FD1B56900B6A97C /* FSAlbumSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC761FD1B56800B6A97C /* FSAlbumSelectionViewController.swift */; }; + 4786CC7B1FD1B68800B6A97C /* ArrowImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC7A1FD1B68800B6A97C /* ArrowImageView.swift */; }; 52412A971CA6114A0073C4BE /* Fusuma.h in Headers */ = {isa = PBXBuildFile; fileRef = 52412A961CA6114A0073C4BE /* Fusuma.h */; settings = {ATTRIBUTES = (Public, ); }; }; EDDC0B3E1D04F53E009135DB /* FSVideoCameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDC0B3C1D04F53E009135DB /* FSVideoCameraView.swift */; }; EDDC0B3F1D04F53E009135DB /* FSVideoCameraView.xib in Resources */ = {isa = PBXBuildFile; fileRef = EDDC0B3D1D04F53E009135DB /* FSVideoCameraView.xib */; }; @@ -24,6 +28,10 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 4786CC741FD1B56700B6A97C /* FSAlbumSelectionTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FSAlbumSelectionTableViewCell.swift; sourceTree = ""; }; + 4786CC751FD1B56800B6A97C /* ArrowableTitleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrowableTitleView.swift; sourceTree = ""; }; + 4786CC761FD1B56800B6A97C /* FSAlbumSelectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FSAlbumSelectionViewController.swift; sourceTree = ""; }; + 4786CC7A1FD1B68800B6A97C /* ArrowImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrowImageView.swift; sourceTree = ""; }; 52412A931CA6114A0073C4BE /* Fusuma.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Fusuma.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 52412A961CA6114A0073C4BE /* Fusuma.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Fusuma.h; sourceTree = ""; }; 52412A981CA6114A0073C4BE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -87,6 +95,10 @@ EDDC0B5C1D051245009135DB /* Assets.xcassets */, EDDC0B481D0511E7009135DB /* FusumaViewController.swift */, EDDC0B491D0511E7009135DB /* FusumaViewController.xib */, + 4786CC751FD1B56800B6A97C /* ArrowableTitleView.swift */, + 4786CC7A1FD1B68800B6A97C /* ArrowImageView.swift */, + 4786CC741FD1B56700B6A97C /* FSAlbumSelectionTableViewCell.swift */, + 4786CC761FD1B56800B6A97C /* FSAlbumSelectionViewController.swift */, EDDC0B4C1D0511F2009135DB /* FSAlbumView.swift */, EDDC0B4D1D0511F2009135DB /* FSAlbumView.xib */, EDDC0B4E1D0511F2009135DB /* FSAlbumViewCell.swift */, @@ -188,7 +200,11 @@ buildActionMask = 2147483647; files = ( EDDC0B3E1D04F53E009135DB /* FSVideoCameraView.swift in Sources */, + 4786CC771FD1B56900B6A97C /* FSAlbumSelectionTableViewCell.swift in Sources */, + 4786CC791FD1B56900B6A97C /* FSAlbumSelectionViewController.swift in Sources */, EDDC0B581D0511F2009135DB /* FSCameraView.swift in Sources */, + 4786CC781FD1B56900B6A97C /* ArrowableTitleView.swift in Sources */, + 4786CC7B1FD1B68800B6A97C /* ArrowImageView.swift in Sources */, EDDC0B4A1D0511E7009135DB /* FusumaViewController.swift in Sources */, EDDC0B541D0511F2009135DB /* FSAlbumView.swift in Sources */, EDDC0B5A1D0511F2009135DB /* FSConstants.swift in Sources */, diff --git a/Sources/ArrowImageView.swift b/Sources/ArrowImageView.swift new file mode 100644 index 00000000..3bad60fb --- /dev/null +++ b/Sources/ArrowImageView.swift @@ -0,0 +1,72 @@ +// +// ArrowImageView.swift +// Burpple2 +// +// Created by Valent Richie on 25/8/16. +// Copyright © 2016 Burpple Pte Ltd. All rights reserved. +// + +import UIKit + +@objc class ArrowImageView: UIImageView, Rotatable { + + var duration: Double = 0.3 + + func setup() { + translatesAutoresizingMaskIntoConstraints = false + setContentCompressionResistancePriority(UILayoutPriority.required, for: .horizontal) + setContentHuggingPriority(UILayoutPriority.required, for: .horizontal) + setContentCompressionResistancePriority(UILayoutPriority.required, for: .vertical) + setContentHuggingPriority(UILayoutPriority.required, for: .vertical) + transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2)) + } + + init() { + // ic_arrow is right facing + super.init(image: UIImage(named: "ic_arrow")) + setup() + } + + init(template: Bool) { + let image = UIImage(named: "ic_arrow") + if template { + image?.withRenderingMode(.alwaysTemplate) + } + super.init(image: image) + setup() + } + + required init?(coder _: NSCoder) { + super.init(image: UIImage(named: "ic_arrow")) + setup() + } + + func upsideDown(_ animation: Bool) { + let angle = CGFloat(3 * Double.pi / 2) // Upside down + if animation == false { + transform = CGAffineTransform(rotationAngle: angle) + } else { + UIView.animate(withDuration: duration, animations: { + self.transform = CGAffineTransform(rotationAngle: angle) + }) + } + } + + func normal(_ animation: Bool) { + let angle: CGFloat = CGFloat(Double.pi / 2) // Normal direction + + // The rotation transform will rotate in the direction of shortest angle change + // If the angle is the same, the default direction is clockwise (which is undesired in this case since the rotation upside down is clockwise) + // Transform the initial angle to be slightly less than M_PI to make the rotation direction counter clockwise + let startAngle = CGFloat(3 * Double.pi / 2 - 0.001) + transform = CGAffineTransform(rotationAngle: startAngle) + + if animation == false { + transform = CGAffineTransform(rotationAngle: angle) + } else { + UIView.animate(withDuration: duration, animations: { + self.transform = CGAffineTransform(rotationAngle: angle) + }) + } + } +} diff --git a/Sources/ArrowableTitleView.swift b/Sources/ArrowableTitleView.swift new file mode 100644 index 00000000..4269ace6 --- /dev/null +++ b/Sources/ArrowableTitleView.swift @@ -0,0 +1,103 @@ +// +// ArrowableTitleView.swift +// Burpple2 +// +// Created by John Kuan on 14/8/17. +// Copyright © 2017 Burpple Pte Ltd. All rights reserved. +// + +import UIKit + +protocol ArrowableTitleViewDelegate: NSObjectProtocol { + func viewDidTapped(_ view: ArrowableTitleView, state: Bool) +} + +class ArrowableTitleView: UIView { + + var delegate: ArrowableTitleViewDelegate! + var selectedState: Bool = false + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = UIFont.BPMediumFont(17.0) + label.textAlignment = .center + label.translatesAutoresizingMaskIntoConstraints = false + label.textColor = .white + return label + }() + + lazy var chevronView: ArrowImageView = { + let imageV = ArrowImageView(template: true) + imageV.setContentHuggingPriority(UILayoutPriority.required, for: .horizontal) + imageV.setContentCompressionResistancePriority(UILayoutPriority.required, for: .horizontal) + imageV.tintColor = .white + imageV.translatesAutoresizingMaskIntoConstraints = false + imageV.isHidden = true + return imageV + }() + + lazy var stack: UIStackView = { + let stack = UIStackView(arrangedSubviews: [titleLabel, chevronView]) + stack.axis = .horizontal + stack.spacing = 8.0 + stack.distribution = .fill + stack.translatesAutoresizingMaskIntoConstraints = false + self.addSubview(stack) + return stack + }() + + // public function + init(frame: CGRect, delegate: ArrowableTitleViewDelegate) { + super.init(frame: frame) + setupViews() + self.delegate = delegate + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setupViews() + } + + func setupViews() { + translatesAutoresizingMaskIntoConstraints = false + heightAnchor.constraint(equalToConstant: 40.0).isActive = true + + stack.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + stack.rightAnchor.constraint(equalTo: rightAnchor).isActive = true + stack.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + + let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(selected)) + addGestureRecognizer(tap) + translatesAutoresizingMaskIntoConstraints = true + sizeToFit() + } + + func toggle() { + DispatchQueue.main.async { + self.selectedState = !self.selectedState + if self.selectedState { + self.chevronView.upsideDown(true) + } else { + self.chevronView.normal(true) + } + } + } + + func setTitle(text: String, hideArrow: Bool) { + translatesAutoresizingMaskIntoConstraints = false + titleLabel.text = text + titleLabel.sizeToFit() + chevronView.isHidden = hideArrow + sizeToFit() + if #available(iOS 10, *) { + // skip + } else { + translatesAutoresizingMaskIntoConstraints = true + } + } + + @objc func selected() { + toggle() + delegate.viewDidTapped(self, state: selectedState) + } +} diff --git a/Sources/Assets.xcassets/ic_arrow.imageset/Contents.json b/Sources/Assets.xcassets/ic_arrow.imageset/Contents.json new file mode 100644 index 00000000..f6d000ec --- /dev/null +++ b/Sources/Assets.xcassets/ic_arrow.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "expandArrowCopy.pdf", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Sources/Assets.xcassets/ic_arrow.imageset/expandArrowCopy.pdf b/Sources/Assets.xcassets/ic_arrow.imageset/expandArrowCopy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..41e4ea539e180821475e48404cb4f5a00e953d61 GIT binary patch literal 4024 zcmai%c|6nqAHW?ejHGfzs!w84Vl$(a`&y!0DYrJ}wuw1wIdXhm$(5s!BllHP$r`zd zuO(M1Bt}Bc-16I0zxw|AJ|4f%WBYvGdwpKlUa!|5&zG3KhSo`#6dWwpK%1vc<}KWR z)zAc%1)u;P;{;Yv0Hk%XID3KvfS`*E0ckBq7XsFu{&YbTuo_q!ye$?`QUZGr+_7j^ zun#lQK-H-M%JqDl&qJ!=v82u;s&8#vy%ONkUx!2ARj;u|X7=;eL3kA6V-Z4%Zgs4% z9FuTTP>`W^r<=E-^GAmw%O=+M(|BrwN{jmcU#@5UD)#Z@w4Sq2-0k6ny5 zJjufm3kRxS#ha|>aLK8jD;`+hVZTKimLv%?;hiOQKzxS;P7dK@Er3-jq28hjZ zF|Q@bXWW-vj|*Qlf6Q67VlwYHVK_@#J{=Xto$;+X)0JryPndw;MJ(c+idsZ=Rn-~P zhqNa5IuN<^&MWAW4DS`GMiVo)f)S#}A(rn2X!q5S;^Rx_-{xn>_^s`D-+*@lV{x`W zDnUOn3dpD$<0*qcFkWsSf2-|9UpFiut&g_fzPn>_1OWcM&W2bIyr;Vj)&r3HE>Oqg z2=w5(+F082nx z9q)p7H*!PUU;##l)jeQ<{I*vmB}OxU_&D{Oj~_#T+}4u5K+jTIl`#l7`V;`t8dxt! z8>}Hp?f=h5+8ytUM*aL@REj+1)m{-mY*EOM19*MrEFZ`qTsuIYB{Ue~rLjYccaOv+ z+lw!lQDGuBn!KDhgD`y@^E~=ZwFVlRw1FsRj+qj~!eHuVv*}R(_|v|A`x##k1$w!dC?OLHli|tjAja%b_hO~O^+h$52lI@XGeiDEt17P*k@m7!%{A4r!vu&k9Bpe+|y-( z@}^j~k3%wD!eumh1;2#PvoNtIG$5b5b4RWs9nzV6a_vVWK;hb$+&z8Cj83+2Q^nF0ex@Z+v%3dL)rrPT6|Or~Xv4*NGXT^44yKC1dkcp`U9937 zK5~h3Movw+cra%;OP%;6zn&3m639vn-A( zphv1oZW~%EeE*>QaRk2&%T)WTq z3Xt8o$xIC1lg!eE-t~s*NkD)38DVx*UE2o@AHI8R0x#YTpR43o;(l5uFnnN^NixK` zE}mVbg`Kri%Y^;FoQB$M6H(?pN1S6ta##v9Q*U$U9C#LeHumZ$>&~!Dbn<@wCc%*~ z)2Lz0C3G+59JD9dCi#-Cz?hsJ4?J)+=mDnZVXldIv9cG3+@8D9CmSwZiMe84p7fCQ z4SMQ~Fn99{!H>djH!AmA$lwsApr^b0qR0W0^}B{4b3V_H5Ax0g?XLBh#aus94fYrI zKQt6`xT!|`HisF=IcWEhJAkN*=tUkO_Hp*j;5~J+27&o{1d&i4-v$ee#KT)}RG(`L zLoUSgYKMvn7>Y`yR0Lksb`r6KC_*Sl;#*JKqTKhJs#xijie>=TV!Pv%^%IUkc=gT+ zYpV5MK%RT1;;Z7T{=jfoKCauOFu5wtOpl^LJV%s9#lw>pl15rf;)`OP6DO0diRmTu zC3z=VOJ#^#AMro;Eq9}m#h_ND)+jK${8FaYa7CSBh_+^GRwCvvOe!WEbMqsj(Ln5Z zTwvVOy?O;I6XmbP9vl%;>w4h(y0YSY#p$=$x6p$q`JDBx_=(2F!=EUN99S`|Iqeut zm?lIcy)@ie2feZaHxzsj6(2>8idm2ydqy3hzLP6+wAy+uk+^yscl>&yNTOXLb7D-P zt}(ucwI88@JM}xQ_ed2*Q}d|a>C`l9*{#uyu+QaH;eM5CPG9RkD}Ig_ zN)@UW5)?8PN|J3t2&UpvTT^MNL1r|w@yAxs#FQ`X7wyDIk;u%F(&@aD zok}?aMT#Xh`6Ltj>AtLf=Tn@C%i8O&rV8Dx}X7@! zcm=$@5~+H(Ha{~Wvj&}x&K|ZoS{Pf@c|6-J`%Kd*n=y2 zbPryJ^(AK_a)T8*6$w7yuUt5b>bn~;QZK!*NC~zRSEcw8^~v4T3)xHBBS|r_`S7SQH`2CsPmfFG?O%Sl1wB` zNuyd(W>G+{BLCq7eAh(1+zXap59oxAOnn58e361%+#yZ9b$?7NXrq?iwyrh3di>CV z!v}|Ww!-(-70`4`7paICGbT^p;w@dv?LwvbJjYYxQJ|E8u#zf3{7lEgE}Z2p2KfZE)Y9 zy!wg*X=bcR?U|avFy&;2Ik!ur3!lsMfE}sC>0OeDRp(bs+sBBHdSdV6CWlOkmsbbc zmx8A$50)ORdw=j^_kKSxQMF)_f-$w~e^vUyvV_(?pEduKe0i$+659OgmEDdpwlNhl z`Hv{)%A8O=d7pDW2MWccj!`~edpe!=w)tIO&5)~q7jragBb(A8Ug78dEzA1OC91BC z==a0L_0-9%&s8<{{?`5)(`V;msi+XtgXX#z&z>{gee(J8337GOaZL|Lt~TR0KYUfc zwy$VHAD;be1$62Na_(dE%Hf%GE`@&I2x8%c_-4k^lUHUDW|_V3d&8gP$T?oqy*4xv z(3N(u6qn1)qNiXensIeV<_wFJyyl>2_HHFMYh6{w9zD!W6su+EpR$5*< z*cO;nW;OTBz2F^XeZx4Z$i5V5d4`xqq)dlyM)w{fr{A%jy5TtSc*d17;YQlLGg5M^ zdIxcQ&2n9OaH)z&yS0^efHiXG+83$nYg<(ZH<(|ITd#!{wGv3D0G{oh7Awbj+t&>mP@fB~?EfaOmW23h}P;$PtH@vkV&U}R4^jl%$G9YEN2A7fl(-r}zPsKlN;WCIn^x*JQf8+v} zh5w5T`P=^>xT76iu@aY8%jD5eBobjOi-DuzO5p#mLf_TsE%6}GM`ODoa5< AssetCollectionTuple { + switch self { + case .cameraRoll: + return (PHAssetCollectionType.smartAlbum, PHAssetCollectionSubtype.smartAlbumUserLibrary) + case .video: + return (PHAssetCollectionType.smartAlbum, PHAssetCollectionSubtype.smartAlbumVideos) + case .allUserAlbum: + return (PHAssetCollectionType.album, PHAssetCollectionSubtype.any) + case .allUserSmartAlbum: + return (PHAssetCollectionType.smartAlbum, PHAssetCollectionSubtype.any) + case .favorites: + return (PHAssetCollectionType.smartAlbum, PHAssetCollectionSubtype.smartAlbumFavorites) + } + } +} + +@objc class AlbumModel: NSObject { + let collection: PHAssetCollection + var asset: PHAsset + var image: UIImage? + init(collection: PHAssetCollection, asset: PHAsset) { + self.collection = collection + self.asset = asset + } + + func updateImage(image: UIImage?) { + self.image = image + } +} + +@objc protocol FSAlbumSelectionViewControllerDelegate: NSObjectProtocol { + func didSelectAlbum(sender: FSAlbumSelectionViewController, albumSelected: AlbumModel) + @objc optional func didSelectCancel(sender: FSAlbumSelectionViewController) +} + +class FSAlbumSelectionViewController: UIViewController { + + lazy var tableView: UITableView = { + let tableView = BPTableView(frame: .zero, style: .plain) + tableView.separatorStyle = .none + tableView.backgroundColor = .white + tableView.dataSource = self + tableView.delegate = self + tableView.register(FSAlbumSelectionTableViewCell.self, forCellReuseIdentifier: "albumlist") + tableView.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(tableView) + return tableView + }() + + var album: [AlbumModel] = [AlbumModel]() + fileprivate var imageManager: PHCachingImageManager? + fileprivate var delegate: FSAlbumSelectionViewControllerDelegate? + var albumTypeArray: [AlbumCollectionType] = [.cameraRoll, .favorites, .allUserAlbum] + fileprivate lazy var assetFetchOptions: PHFetchOptions = { + let fetchOptions = PHFetchOptions() + fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] + fetchOptions.fetchLimit = 1 + return fetchOptions + }() + + public var assetType: FSAlbumAssetType = .both + + override func viewDidLoad() { + super.viewDidLoad() + prepareViews() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + if album.isEmpty { + listAlbums() + } + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + public override var prefersStatusBarHidden: Bool { + return true + } + + init(delegate: FSAlbumSelectionViewControllerDelegate) { + super.init(nibName: nil, bundle: nil) + self.delegate = delegate + view.backgroundColor = .clear + modalPresentationStyle = .overCurrentContext + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension FSAlbumSelectionViewController { + func prepareViews() { + + if #available(iOS 11.0, *) { + tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true + } else { + tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true + } + tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true + tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true + imageManager = PHCachingImageManager() + + // let closeButton = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(closeViewController)) + let image = UIImage(named: "icon_delete_sign") + let closeButton = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(closeViewController)) + navigationItem.setLeftBarButton(closeButton, animated: false) + navigationItem.title = NSLocalizedString("Choose Album", comment: "Navigation title to choose album") + } + + @objc func closeViewController() { + delegate?.didSelectCancel?(sender: self) + } +} + +extension FSAlbumSelectionViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let selectedAlbum = album[indexPath.row] + delegate?.didSelectAlbum(sender: self, albumSelected: selectedAlbum) + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return FSAlbumSelectionTableViewCell.rowHeight + } +} + +extension FSAlbumSelectionViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return album.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "albumlist", for: indexPath) as! FSAlbumSelectionTableViewCell + let item = album[indexPath.row] + + if let previewImage = item.image { + cell.setup(albumDetails: item, image: previewImage) + } else { + cell.setup(albumDetails: item, image: nil) + + let scale = UIScreen.main.scale + let imageSize = CGSize(width: FSAlbumSelectionTableViewCell.imageSize * scale, height: FSAlbumSelectionTableViewCell.imageSize * scale) + let options = PHImageRequestOptions() + options.isSynchronous = true + + imageManager?.requestImage(for: item.asset, + targetSize: imageSize, + contentMode: .aspectFill, + options: options) { + result, info in + DispatchQueue.main.async { + if let found = tableView.indexPathsForVisibleRows?.contains(indexPath), found == true { + cell.updateImage(image: result) + } + item.updateImage(image: result) + } + } + } + + return cell + } +} + +extension FSAlbumSelectionViewController { + // find all user created albums + func listAlbums() { + DispatchQueue.main.async { + var album: [AlbumModel] = [AlbumModel]() + var assetArray: [PHAsset] = [PHAsset]() + + let options = PHFetchOptions() + options.includeAssetSourceTypes = .typeUserLibrary + + for item in self.albumTypeArray { + let collectionTuple = item.getPHAssetCollectionType() + let albumCollection = PHAssetCollection.fetchAssetCollections(with: collectionTuple.collection, subtype: collectionTuple.subType, options: options) + albumCollection.enumerateObjects(_:) { object, count, stop in + let fetchResult = PHAsset.fetchAssets(in: object, options: self.assetFetchOptions) + guard let asset = fetchResult.firstObject else { + return + } + + let newAlbum = AlbumModel(collection: object, asset: asset) + album.append(newAlbum) + assetArray.append(asset) + } + } + + self.album = album + + self.tableView.beginUpdates() + let section = IndexSet(integer: 0) + self.tableView.reloadSections(section, with: .fade) + self.tableView.endUpdates() + + self.cacheAllFirstImages(assetArray: assetArray) + } + } + + func cacheAllFirstImages(assetArray: [PHAsset]) { + let imageSize = CGSize(width: FSAlbumSelectionTableViewCell.imageSize, height: FSAlbumSelectionTableViewCell.imageSize) + DispatchQueue.main.async(execute: { + self.imageManager?.startCachingImages(for: assetArray, targetSize: imageSize, contentMode: .aspectFill, options: PHImageRequestOptions()) + }) + } + + func createAssetFetchOptions() -> PHFetchOptions? { + let createImagePredicate = { () -> NSPredicate in + NSPredicate(format: "mediaType == %d", PHAssetMediaType.image.rawValue) + } + + let createVideoPredicate = { () -> NSPredicate in + NSPredicate(format: "mediaType == %d", PHAssetMediaType.video.rawValue) + } + + var predicate: NSPredicate? + switch assetType { + case .both: + predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [createImagePredicate(), createVideoPredicate()]) + case .photos: + predicate = createImagePredicate() + case .videos: + predicate = createVideoPredicate() + } + + assetFetchOptions.predicate = predicate + + return assetFetchOptions + } +} + +extension PHAssetCollection { + var photosCount: Int { + let fetchOptions = PHFetchOptions() + fetchOptions.predicate = NSPredicate(format: "mediaType == %d", PHAssetMediaType.image.rawValue) + let result = PHAsset.fetchAssets(in: self, options: fetchOptions) + return result.count + } +} From d48fe14531bec38fa30e06f89b2fb8888e400066 Mon Sep 17 00:00:00 2001 From: John Kuan Date: Sat, 2 Dec 2017 01:11:49 +0800 Subject: [PATCH 02/13] add Rotatable.swift --- Fusuma.xcodeproj/project.pbxproj | 14 +++++++++ Sources/Rotatable.swift | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 Sources/Rotatable.swift diff --git a/Fusuma.xcodeproj/project.pbxproj b/Fusuma.xcodeproj/project.pbxproj index 84e15dd3..b39975ca 100644 --- a/Fusuma.xcodeproj/project.pbxproj +++ b/Fusuma.xcodeproj/project.pbxproj @@ -11,6 +11,12 @@ 4786CC781FD1B56900B6A97C /* ArrowableTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC751FD1B56800B6A97C /* ArrowableTitleView.swift */; }; 4786CC791FD1B56900B6A97C /* FSAlbumSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC761FD1B56800B6A97C /* FSAlbumSelectionViewController.swift */; }; 4786CC7B1FD1B68800B6A97C /* ArrowImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC7A1FD1B68800B6A97C /* ArrowImageView.swift */; }; + 4786CC7C1FD1C15800B6A97C /* ArrowableTitleView.swift in Resources */ = {isa = PBXBuildFile; fileRef = 4786CC751FD1B56800B6A97C /* ArrowableTitleView.swift */; }; + 4786CC7D1FD1C15800B6A97C /* ArrowImageView.swift in Resources */ = {isa = PBXBuildFile; fileRef = 4786CC7A1FD1B68800B6A97C /* ArrowImageView.swift */; }; + 4786CC7E1FD1C15800B6A97C /* FSAlbumSelectionTableViewCell.swift in Resources */ = {isa = PBXBuildFile; fileRef = 4786CC741FD1B56700B6A97C /* FSAlbumSelectionTableViewCell.swift */; }; + 4786CC7F1FD1C15800B6A97C /* FSAlbumSelectionViewController.swift in Resources */ = {isa = PBXBuildFile; fileRef = 4786CC761FD1B56800B6A97C /* FSAlbumSelectionViewController.swift */; }; + 4786CC811FD1C22000B6A97C /* Rotatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC801FD1C22000B6A97C /* Rotatable.swift */; }; + 4786CC821FD1C37400B6A97C /* Rotatable.swift in Resources */ = {isa = PBXBuildFile; fileRef = 4786CC801FD1C22000B6A97C /* Rotatable.swift */; }; 52412A971CA6114A0073C4BE /* Fusuma.h in Headers */ = {isa = PBXBuildFile; fileRef = 52412A961CA6114A0073C4BE /* Fusuma.h */; settings = {ATTRIBUTES = (Public, ); }; }; EDDC0B3E1D04F53E009135DB /* FSVideoCameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDC0B3C1D04F53E009135DB /* FSVideoCameraView.swift */; }; EDDC0B3F1D04F53E009135DB /* FSVideoCameraView.xib in Resources */ = {isa = PBXBuildFile; fileRef = EDDC0B3D1D04F53E009135DB /* FSVideoCameraView.xib */; }; @@ -32,6 +38,7 @@ 4786CC751FD1B56800B6A97C /* ArrowableTitleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrowableTitleView.swift; sourceTree = ""; }; 4786CC761FD1B56800B6A97C /* FSAlbumSelectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FSAlbumSelectionViewController.swift; sourceTree = ""; }; 4786CC7A1FD1B68800B6A97C /* ArrowImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrowImageView.swift; sourceTree = ""; }; + 4786CC801FD1C22000B6A97C /* Rotatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rotatable.swift; sourceTree = ""; }; 52412A931CA6114A0073C4BE /* Fusuma.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Fusuma.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 52412A961CA6114A0073C4BE /* Fusuma.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Fusuma.h; sourceTree = ""; }; 52412A981CA6114A0073C4BE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -97,6 +104,7 @@ EDDC0B491D0511E7009135DB /* FusumaViewController.xib */, 4786CC751FD1B56800B6A97C /* ArrowableTitleView.swift */, 4786CC7A1FD1B68800B6A97C /* ArrowImageView.swift */, + 4786CC801FD1C22000B6A97C /* Rotatable.swift */, 4786CC741FD1B56700B6A97C /* FSAlbumSelectionTableViewCell.swift */, 4786CC761FD1B56800B6A97C /* FSAlbumSelectionViewController.swift */, EDDC0B4C1D0511F2009135DB /* FSAlbumView.swift */, @@ -183,6 +191,11 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4786CC821FD1C37400B6A97C /* Rotatable.swift in Resources */, + 4786CC7C1FD1C15800B6A97C /* ArrowableTitleView.swift in Resources */, + 4786CC7D1FD1C15800B6A97C /* ArrowImageView.swift in Resources */, + 4786CC7E1FD1C15800B6A97C /* FSAlbumSelectionTableViewCell.swift in Resources */, + 4786CC7F1FD1C15800B6A97C /* FSAlbumSelectionViewController.swift in Resources */, EDDC0B3F1D04F53E009135DB /* FSVideoCameraView.xib in Resources */, EDDC0B571D0511F2009135DB /* FSAlbumViewCell.xib in Resources */, EDDC0B591D0511F2009135DB /* FSCameraView.xib in Resources */, @@ -203,6 +216,7 @@ 4786CC771FD1B56900B6A97C /* FSAlbumSelectionTableViewCell.swift in Sources */, 4786CC791FD1B56900B6A97C /* FSAlbumSelectionViewController.swift in Sources */, EDDC0B581D0511F2009135DB /* FSCameraView.swift in Sources */, + 4786CC811FD1C22000B6A97C /* Rotatable.swift in Sources */, 4786CC781FD1B56900B6A97C /* ArrowableTitleView.swift in Sources */, 4786CC7B1FD1B68800B6A97C /* ArrowImageView.swift in Sources */, EDDC0B4A1D0511E7009135DB /* FusumaViewController.swift in Sources */, diff --git a/Sources/Rotatable.swift b/Sources/Rotatable.swift new file mode 100644 index 00000000..73315702 --- /dev/null +++ b/Sources/Rotatable.swift @@ -0,0 +1,49 @@ +// +// Rotatable.swift +// Burpple2 +// +// Created by Valent Richie on 25/8/16. +// Copyright © 2016 Burpple Pte Ltd. All rights reserved. +// + +import UIKit + +@objc public protocol Rotatable { + var duration: Double { get set } + + func upsideDown(_ animation: Bool) + func normal(_ animation: Bool) +} + +public extension Rotatable where Self: UIView { + + // Default upside down implementation + func upsideDown(_ animation: Bool) { + let angle = CGFloat(Double.pi) + if animation == false { + transform = CGAffineTransform(rotationAngle: angle) + } else { + UIView.animate(withDuration: duration, animations: { + self.transform = CGAffineTransform(rotationAngle: angle) + }) + } + } + + func normal(_ animation: Bool) { + let angle: CGFloat = 0 // Normal direction + + // The rotation transform will rotate in the direction of shortest angle change + // If the angle is the same, the default direction is clockwise (which is undesired in this case since the rotation upside down is clockwise) + // Transform the initial angle to be slightly less than M_PI to make the rotation direction counter clockwise + let startAngle = CGFloat(Double.pi - 0.001) + transform = CGAffineTransform(rotationAngle: startAngle) + + if animation == false { + transform = CGAffineTransform(rotationAngle: angle) + } else { + UIView.animate(withDuration: duration, animations: { + self.transform = CGAffineTransform(rotationAngle: angle) + }) + } + } +} From 1934d46c0c5daf41690501913ad1b750722680ab Mon Sep 17 00:00:00 2001 From: John Kuan Date: Sun, 3 Dec 2017 19:27:26 +0800 Subject: [PATCH 03/13] updated to support pre iOS 9 --- Sources/ArrowImageView.swift | 15 ++++++------ Sources/ArrowableTitleView.swift | 39 +++++++++++++++++++++++++------- Sources/FSConstants.swift | 4 ++++ 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/Sources/ArrowImageView.swift b/Sources/ArrowImageView.swift index 3bad60fb..30261484 100644 --- a/Sources/ArrowImageView.swift +++ b/Sources/ArrowImageView.swift @@ -8,16 +8,17 @@ import UIKit -@objc class ArrowImageView: UIImageView, Rotatable { +@objc final class ArrowImageView: UIImageView, Rotatable { var duration: Double = 0.3 func setup() { translatesAutoresizingMaskIntoConstraints = false - setContentCompressionResistancePriority(UILayoutPriority.required, for: .horizontal) - setContentHuggingPriority(UILayoutPriority.required, for: .horizontal) - setContentCompressionResistancePriority(UILayoutPriority.required, for: .vertical) - setContentHuggingPriority(UILayoutPriority.required, for: .vertical) + setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .horizontal) + setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .horizontal) + setContentHuggingPriority(UILayoutPriorityRequired, for: .horizontal) + setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .vertical) + setContentHuggingPriority(UILayoutPriorityRequired, for: .vertical) transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2)) } @@ -41,7 +42,7 @@ import UIKit setup() } - func upsideDown(_ animation: Bool) { + public func upsideDown(_ animation: Bool) { let angle = CGFloat(3 * Double.pi / 2) // Upside down if animation == false { transform = CGAffineTransform(rotationAngle: angle) @@ -52,7 +53,7 @@ import UIKit } } - func normal(_ animation: Bool) { + public func normal(_ animation: Bool) { let angle: CGFloat = CGFloat(Double.pi / 2) // Normal direction // The rotation transform will rotate in the direction of shortest angle change diff --git a/Sources/ArrowableTitleView.swift b/Sources/ArrowableTitleView.swift index 4269ace6..35a2bf39 100644 --- a/Sources/ArrowableTitleView.swift +++ b/Sources/ArrowableTitleView.swift @@ -8,18 +8,22 @@ import UIKit -protocol ArrowableTitleViewDelegate: NSObjectProtocol { +@objc public protocol ArrowableTitleViewDelegate: NSObjectProtocol { func viewDidTapped(_ view: ArrowableTitleView, state: Bool) } -class ArrowableTitleView: UIView { +final public class ArrowableTitleView: UIView { var delegate: ArrowableTitleViewDelegate! var selectedState: Bool = false lazy var titleLabel: UILabel = { let label = UILabel() - label.font = UIFont.BPMediumFont(17.0) + if #available(iOS 8.2, *) { + label.font = UIFont.systemFont(ofSize:17, weight: UIFontWeightMedium) + } else { + label.font = UIFont(name: "HelveticaNeue-Medium", size: 17) + } label.textAlignment = .center label.translatesAutoresizingMaskIntoConstraints = false label.textColor = .white @@ -28,16 +32,19 @@ class ArrowableTitleView: UIView { lazy var chevronView: ArrowImageView = { let imageV = ArrowImageView(template: true) - imageV.setContentHuggingPriority(UILayoutPriority.required, for: .horizontal) - imageV.setContentCompressionResistancePriority(UILayoutPriority.required, for: .horizontal) + + + imageV.setContentHuggingPriority(UILayoutPriorityRequired, for: .horizontal) + imageV.setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .horizontal) imageV.tintColor = .white imageV.translatesAutoresizingMaskIntoConstraints = false imageV.isHidden = true return imageV }() + @available(iOS 9, *) lazy var stack: UIStackView = { - let stack = UIStackView(arrangedSubviews: [titleLabel, chevronView]) + let stack = UIStackView(arrangedSubviews: [self.titleLabel, self.chevronView]) stack.axis = .horizontal stack.spacing = 8.0 stack.distribution = .fill @@ -53,21 +60,37 @@ class ArrowableTitleView: UIView { self.delegate = delegate } - required init?(coder aDecoder: NSCoder) { + required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setupViews() } func setupViews() { translatesAutoresizingMaskIntoConstraints = false + + + if #available(iOS 9, *) { heightAnchor.constraint(equalToConstant: 40.0).isActive = true - stack.leftAnchor.constraint(equalTo: leftAnchor).isActive = true stack.rightAnchor.constraint(equalTo: rightAnchor).isActive = true stack.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + } else { + titleLabel.addConstraints([ + NSLayoutConstraint(item: titleLabel, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0), + NSLayoutConstraint(item: titleLabel, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0), + NSLayoutConstraint(item: titleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 40.0) + ]) + chevronView.addConstraints([ + NSLayoutConstraint(item: chevronView, attribute: .left, relatedBy: .equal, toItem: titleLabel, attribute: .right, multiplier: 1.0, constant: 8.0), + NSLayoutConstraint(item: chevronView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0), + NSLayoutConstraint(item: titleLabel, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0), + NSLayoutConstraint(item: titleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 40.0) + ]) + } let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(selected)) addGestureRecognizer(tap) + // there is some resizing issue if do not set this back to true translatesAutoresizingMaskIntoConstraints = true sizeToFit() } diff --git a/Sources/FSConstants.swift b/Sources/FSConstants.swift index a1698868..38827edf 100644 --- a/Sources/FSConstants.swift +++ b/Sources/FSConstants.swift @@ -39,3 +39,7 @@ extension UIView { } } + +extension Notification.Name { + static let photoLibraryReloaded = Notification.Name("photoLibraryReloaded") +} From d33bfe1e9472e2b3904d71049eccd0b09f5941e5 Mon Sep 17 00:00:00 2001 From: John Kuan Date: Sun, 3 Dec 2017 19:27:56 +0800 Subject: [PATCH 04/13] updated reference to FusumaExample --- .../FusumaExample.xcodeproj/project.pbxproj | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Example/FusumaExample.xcodeproj/project.pbxproj b/Example/FusumaExample.xcodeproj/project.pbxproj index 739267dd..9535bbc9 100644 --- a/Example/FusumaExample.xcodeproj/project.pbxproj +++ b/Example/FusumaExample.xcodeproj/project.pbxproj @@ -7,6 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 4786CC921FD413FC00B6A97C /* FSAlbumSelectionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC8D1FD413FB00B6A97C /* FSAlbumSelectionTableViewCell.swift */; }; + 4786CC931FD413FC00B6A97C /* ArrowableTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC8E1FD413FB00B6A97C /* ArrowableTitleView.swift */; }; + 4786CC941FD413FC00B6A97C /* FSAlbumSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC8F1FD413FC00B6A97C /* FSAlbumSelectionViewController.swift */; }; + 4786CC951FD413FC00B6A97C /* ArrowImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC901FD413FC00B6A97C /* ArrowImageView.swift */; }; + 4786CC961FD413FC00B6A97C /* Rotatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786CC911FD413FC00B6A97C /* Rotatable.swift */; }; 52412AC41CA61C9F0073C4BE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52412AC31CA61C9F0073C4BE /* AppDelegate.swift */; }; 52412AC61CA61C9F0073C4BE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52412AC51CA61C9F0073C4BE /* ViewController.swift */; }; 52412AC91CA61C9F0073C4BE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 52412AC71CA61C9F0073C4BE /* Main.storyboard */; }; @@ -28,6 +33,11 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 4786CC8D1FD413FB00B6A97C /* FSAlbumSelectionTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FSAlbumSelectionTableViewCell.swift; sourceTree = ""; }; + 4786CC8E1FD413FB00B6A97C /* ArrowableTitleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrowableTitleView.swift; sourceTree = ""; }; + 4786CC8F1FD413FC00B6A97C /* FSAlbumSelectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FSAlbumSelectionViewController.swift; sourceTree = ""; }; + 4786CC901FD413FC00B6A97C /* ArrowImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrowImageView.swift; sourceTree = ""; }; + 4786CC911FD413FC00B6A97C /* Rotatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rotatable.swift; sourceTree = ""; }; 52412AC01CA61C9F0073C4BE /* FusumaExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FusumaExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 52412AC31CA61C9F0073C4BE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 52412AC51CA61C9F0073C4BE /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -104,7 +114,12 @@ D77CA2951CBBDEB300C4BAC4 /* FSConstants.swift */, D77CA2961CBBDEB300C4BAC4 /* FSImageCropView.swift */, D77CA2971CBBDEB300C4BAC4 /* FusumaViewController.swift */, + 4786CC8E1FD413FB00B6A97C /* ArrowableTitleView.swift */, D77CA2981CBBDEB300C4BAC4 /* FusumaViewController.xib */, + 4786CC901FD413FC00B6A97C /* ArrowImageView.swift */, + 4786CC911FD413FC00B6A97C /* Rotatable.swift */, + 4786CC8D1FD413FB00B6A97C /* FSAlbumSelectionTableViewCell.swift */, + 4786CC8F1FD413FC00B6A97C /* FSAlbumSelectionViewController.swift */, EDDC0B401D04FA14009135DB /* FSVideoCameraView.swift */, EDDC0B411D04FA14009135DB /* FSVideoCameraView.xib */, ); @@ -191,10 +206,15 @@ files = ( 52412AC61CA61C9F0073C4BE /* ViewController.swift in Sources */, EDDC0B421D04FA14009135DB /* FSVideoCameraView.swift in Sources */, + 4786CC951FD413FC00B6A97C /* ArrowImageView.swift in Sources */, + 4786CC941FD413FC00B6A97C /* FSAlbumSelectionViewController.swift in Sources */, D77CA29E1CBBDEB300C4BAC4 /* FSCameraView.swift in Sources */, + 4786CC931FD413FC00B6A97C /* ArrowableTitleView.swift in Sources */, D77CA29C1CBBDEB300C4BAC4 /* FSAlbumViewCell.swift in Sources */, D77CA29A1CBBDEB300C4BAC4 /* FSAlbumView.swift in Sources */, + 4786CC921FD413FC00B6A97C /* FSAlbumSelectionTableViewCell.swift in Sources */, D77CA2A01CBBDEB300C4BAC4 /* FSConstants.swift in Sources */, + 4786CC961FD413FC00B6A97C /* Rotatable.swift in Sources */, D77CA2A21CBBDEB300C4BAC4 /* FusumaViewController.swift in Sources */, 52412AC41CA61C9F0073C4BE /* AppDelegate.swift in Sources */, D77CA2A11CBBDEB300C4BAC4 /* FSImageCropView.swift in Sources */, From d35a02d1d4e9523a91f9673c3a52d701e3da2801 Mon Sep 17 00:00:00 2001 From: John Kuan Date: Sun, 3 Dec 2017 19:29:04 +0800 Subject: [PATCH 05/13] updated to support pre-iOS 9 as well for AlbumSelection --- Sources/FSAlbumSelectionTableViewCell.swift | 66 +++++++++++++++----- Sources/FSAlbumSelectionViewController.swift | 41 +++++++++--- 2 files changed, 80 insertions(+), 27 deletions(-) diff --git a/Sources/FSAlbumSelectionTableViewCell.swift b/Sources/FSAlbumSelectionTableViewCell.swift index 0797ab9f..0896393a 100644 --- a/Sources/FSAlbumSelectionTableViewCell.swift +++ b/Sources/FSAlbumSelectionTableViewCell.swift @@ -17,8 +17,8 @@ final class FSAlbumSelectionTableViewCell: UITableViewCell { fileprivate lazy var mainLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false - label.font = UIFont.BPFont(16.0) - label.textColor = UIColor.BPGreyishBrown() + label.font = UIFont.systemFont(ofSize: 16.0) + label.textColor = UIColor(red: 85.0 / 255.0, green: 85.0 / 255.0, blue: 85.0 / 255.0, alpha: 1.0) label.numberOfLines = 1 self.contentView.addSubview(label) return label @@ -27,17 +27,27 @@ final class FSAlbumSelectionTableViewCell: UITableViewCell { fileprivate lazy var subLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false - label.font = UIFont.BPFont(13.0) - label.textColor = UIColor.BPGreyishBrown() + label.font = UIFont.systemFont(ofSize: 13.0) + label.textColor = UIColor(red: 85.0 / 255.0, green: 85.0 / 255.0, blue: 85.0 / 255.0, alpha: 1.0) label.numberOfLines = 1 self.contentView.addSubview(label) return label }() + lazy var mainimageView: UIImageView = { let imageV = UIImageView(frame: .zero) - imageV.heightAnchor.constraint(equalToConstant: FSAlbumSelectionTableViewCell.imageSize).isActive = true - imageV.widthAnchor.constraint(equalTo: imageV.heightAnchor).isActive = true + if #available(iOS 9.0, *) { + imageV.heightAnchor.constraint(equalToConstant: FSAlbumSelectionTableViewCell.imageSize).isActive = true + imageV.widthAnchor.constraint(equalTo: imageV.heightAnchor).isActive = true + } else { + let constraints = [ + NSLayoutConstraint(item: imageV, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: FSAlbumSelectionTableViewCell.imageSize), + NSLayoutConstraint(item: imageV, attribute: .width, relatedBy: .equal, toItem: imageV, attribute: .height, multiplier: 1.0, constant: 0), + ] + imageV.addConstraints(constraints) + } + imageV.translatesAutoresizingMaskIntoConstraints = false imageV.contentMode = .scaleAspectFit self.contentView.addSubview(imageV) @@ -62,20 +72,42 @@ final class FSAlbumSelectionTableViewCell: UITableViewCell { fileprivate func setupViews() { selectedBackgroundView = { let view = UIView() - view.backgroundColor = UIColor(netHex: 0xEEEEEE) + view.backgroundColor = UIColor(red: 238.0/255.0, green: 238.0/255.0, blue: 238.0/255.0, alpha: 1.0) return view }() - mainimageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 12.0).isActive = true - mainimageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true - - mainLabel.leftAnchor.constraint(equalTo: mainimageView.rightAnchor, constant: 15.0).isActive = true - mainLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -5.0).isActive = true - mainLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor, constant: -10.0).isActive = true - - subLabel.leftAnchor.constraint(equalTo: mainLabel.leftAnchor).isActive = true - subLabel.rightAnchor.constraint(equalTo: mainLabel.rightAnchor).isActive = true - subLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor, constant: 10.0).isActive = true + if #available(iOS 9.0, *) { + mainimageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 12.0).isActive = true + mainimageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true + + mainLabel.leftAnchor.constraint(equalTo: mainimageView.rightAnchor, constant: 15.0).isActive = true + mainLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -5.0).isActive = true + mainLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor, constant: -10.0).isActive = true + + subLabel.leftAnchor.constraint(equalTo: mainLabel.leftAnchor).isActive = true + subLabel.rightAnchor.constraint(equalTo: mainLabel.rightAnchor).isActive = true + subLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor, constant: 10.0).isActive = true + } else { + let mainImageViewConstraints = [ + NSLayoutConstraint(item: mainimageView, attribute: .left, relatedBy: .equal, toItem: contentView, attribute: .left, multiplier: 1.0, constant: 12.0), + NSLayoutConstraint(item: mainimageView, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1.0, constant: 0), + ] + mainimageView.addConstraints(mainImageViewConstraints) + + let mainLabelConstraints = [ + NSLayoutConstraint(item: mainLabel, attribute: .left, relatedBy: .equal, toItem: mainLabel, attribute: .right, multiplier: 1.0, constant: 15.0), + NSLayoutConstraint(item: mainLabel, attribute: .right, relatedBy: .equal, toItem: contentView, attribute: .right, multiplier: 1.0, constant: -5.0), + NSLayoutConstraint(item: mainLabel, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1.0, constant: -10.0), + ] + mainLabel.addConstraints(mainLabelConstraints) + + let subLabelConstraints = [ + NSLayoutConstraint(item: subLabel, attribute: .left, relatedBy: .equal, toItem: mainLabel, attribute: .left, multiplier: 1.0, constant: 0.0), + NSLayoutConstraint(item: subLabel, attribute: .right, relatedBy: .equal, toItem: mainLabel, attribute: .right, multiplier: 1.0, constant: 0.0), + NSLayoutConstraint(item: subLabel, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1.0, constant: 10.0), + ] + subLabel.addConstraints(subLabelConstraints) + } } public func setup(albumDetails: AlbumModel, image: UIImage?) { diff --git a/Sources/FSAlbumSelectionViewController.swift b/Sources/FSAlbumSelectionViewController.swift index 12057f1a..49b41c79 100644 --- a/Sources/FSAlbumSelectionViewController.swift +++ b/Sources/FSAlbumSelectionViewController.swift @@ -34,6 +34,10 @@ typealias AssetCollectionTuple = (collection: PHAssetCollectionType, subType: PH } } +@objc enum FSAlbumAssetType: Int { + case photos, videos, both +} + @objc class AlbumModel: NSObject { let collection: PHAssetCollection var asset: PHAsset @@ -56,7 +60,7 @@ typealias AssetCollectionTuple = (collection: PHAssetCollectionType, subType: PH class FSAlbumSelectionViewController: UIViewController { lazy var tableView: UITableView = { - let tableView = BPTableView(frame: .zero, style: .plain) + let tableView = UITableView(frame: .zero, style: .plain) tableView.separatorStyle = .none tableView.backgroundColor = .white tableView.dataSource = self @@ -74,10 +78,12 @@ class FSAlbumSelectionViewController: UIViewController { fileprivate lazy var assetFetchOptions: PHFetchOptions = { let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] - fetchOptions.fetchLimit = 1 + if #available(iOS 9.0, *) { + fetchOptions.fetchLimit = 1 + } return fetchOptions }() - + public var assetType: FSAlbumAssetType = .both override func viewDidLoad() { @@ -116,14 +122,24 @@ class FSAlbumSelectionViewController: UIViewController { extension FSAlbumSelectionViewController { func prepareViews() { - if #available(iOS 11.0, *) { - tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true + if #available(iOS 9.0, *) { + if #available(iOS 11.0, *) { + tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true + } else { + tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true + } + tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true + tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true } else { - tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true + tableView.addConstraints([ + NSLayoutConstraint(item: tableView, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 0), + NSLayoutConstraint(item: tableView, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1.0, constant: 0), + NSLayoutConstraint(item: tableView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: 0), + NSLayoutConstraint(item: tableView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0), + ]) } - tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true - tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true - tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true + imageManager = PHCachingImageManager() // let closeButton = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(closeViewController)) @@ -194,7 +210,12 @@ extension FSAlbumSelectionViewController { var assetArray: [PHAsset] = [PHAsset]() let options = PHFetchOptions() - options.includeAssetSourceTypes = .typeUserLibrary + if #available(iOS 9.0, *) { + options.includeAssetSourceTypes = .typeUserLibrary + } else { + // Fallback on earlier versions + + } for item in self.albumTypeArray { let collectionTuple = item.getPHAssetCollectionType() From 1e09a5acd4ff6c8915b161067065e9f6aa559fb5 Mon Sep 17 00:00:00 2001 From: John Kuan Date: Sun, 3 Dec 2017 20:10:25 +0800 Subject: [PATCH 06/13] added functions to help update album pictures after selection of album --- Sources/FSAlbumView.swift | 94 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/Sources/FSAlbumView.swift b/Sources/FSAlbumView.swift index 6f4cc64a..2d753eb3 100644 --- a/Sources/FSAlbumView.swift +++ b/Sources/FSAlbumView.swift @@ -31,9 +31,43 @@ final class FSAlbumView: UIView, UICollectionViewDataSource, UICollectionViewDel fileprivate var images: PHFetchResult! fileprivate var imageManager: PHCachingImageManager? + fileprivate var selectedAssetCollection: PHAssetCollection? fileprivate var previousPreheatRect: CGRect = .zero fileprivate let cellSize = CGSize(width: 100, height: 100) + // For now add no image view indicator for iOS 9 and above + @available(iOS 9, *) + fileprivate lazy var noImageLabelsView: UIStackView = { + let noImageTitleLabel: UILabel = { + let label = UILabel() + label.text = NSLocalizedString("No Photos Found", comment: "No Photos Found") + label.font = UIFont.systemFont(ofSize:22, weight: UIFontWeightBold) + label.textColor = UIColor(red: 153.0 / 255.0, green: 153.0 / 255.0, blue: 153.0 / 255.0, alpha: 1.0) + label.alpha = 0.6 + label.textAlignment = .center + return label + }() + + let noImageDescriptionLabel: UILabel = { + let label = UILabel() + label.text = NSLocalizedString("Use camera to take a photo or choose a different album to write a review", comment: "Description text when no photo found in current album") + label.font = UIFont.systemFont(ofSize:15, weight: UIFontWeightRegular) + label.textColor = UIColor.white + label.textAlignment = .center + label.preferredMaxLayoutWidth = 280 + label.numberOfLines = 0 + label.alpha = 0.5 + return label + }() + + let subviews = [noImageTitleLabel, noImageDescriptionLabel] + let stackView = UIStackView(arrangedSubviews: subviews) + stackView.axis = UILayoutConstraintAxis.vertical + stackView.spacing = 20 + return stackView + }() + + var phAsset: PHAsset! var selectedImages: [UIImage] = [] @@ -366,6 +400,7 @@ final class FSAlbumView: UIView, UICollectionViewDataSource, UICollectionViewDel collectionChanges.hasMoves { collectionView.reloadData() + NotificationCenter.default.post(name: NSNotification.Name.photoLibraryReloaded, object: nil) } else { @@ -390,11 +425,70 @@ final class FSAlbumView: UIView, UICollectionViewDataSource, UICollectionViewDel } }, completion: nil) + + NotificationCenter.default.post(name: NSNotification.Name.photoLibraryReloaded, object: nil) } self.resetCachedAssets() } } + + // MARK: - Update PHAssetCollection + internal func updateAssetCollection(assetCollection: PHAssetCollection) { + selectedAssetCollection = assetCollection + updateCollectionContent() + } + + internal func getAssetCollectionTitle() -> String? { + if let collection = selectedAssetCollection { + return collection.localizedTitle + } + return nil + } + + private func updateCollectionContent() { + // Sorting condition + let options = PHFetchOptions() + options.sortDescriptors = [ + // Editing a photo, e.g. in VSCO, and saving it back to camera roll + // will have the same creation date as original photo + // but different modification date + // If we sort by creation date, this edited photo will appear somewhere below side by side to the original photo, + // which might make it difficult to find if the original photo is taken long time ago + NSSortDescriptor(key: "modificationDate", ascending: false), + ] + + if let collection = selectedAssetCollection { + images = PHAsset.fetchAssets(in: collection, options: nil) + } else { + images = PHAsset.fetchAssets(with: .image, options: nil) + } + + if images.count > 0 { + changeImage(assetAtIndex(0)) + collectionView.reloadData() + collectionView.selectItem(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: .top) + if #available(iOS 9, *) { + noImageLabelsView.isHidden = true + } else { + // Fallback on earlier versions + } + } else { + if #available(iOS 9, *) { + noImageLabelsView.isHidden = false + } else { + // Fallback on earlier versions + } + } + + NotificationCenter.default.post(name: NSNotification.Name.photoLibraryReloaded, object: nil) + } + + func assetAtIndex(_ index: Int) -> PHAsset { + // Return results in reverse order + return images[images.count - index - 1] + } + } internal extension UICollectionView { From bafe958f05b08bf1091c66ccf90d59e1c6709d9d Mon Sep 17 00:00:00 2001 From: John Kuan Date: Sun, 3 Dec 2017 20:13:35 +0800 Subject: [PATCH 07/13] updated FusumaViewController --- Sources/FusumaViewController.swift | 118 +++++++++++++++++++++++------ Sources/FusumaViewController.xib | 70 +---------------- 2 files changed, 96 insertions(+), 92 deletions(-) diff --git a/Sources/FusumaViewController.swift b/Sources/FusumaViewController.swift index df7f83f9..6ffb5ab2 100644 --- a/Sources/FusumaViewController.swift +++ b/Sources/FusumaViewController.swift @@ -117,17 +117,38 @@ public struct ImageMetadata { @IBOutlet weak var cameraShotContainer: UIView! @IBOutlet weak var videoShotContainer: UIView! - @IBOutlet weak var titleLabel: UILabel! - @IBOutlet weak var menuView: UIView! - @IBOutlet weak var closeButton: UIButton! +// @IBOutlet weak var titleLabel: UILabel! +// @IBOutlet weak var menuView: UIView! +// @IBOutlet weak var closeButton: UIButton! @IBOutlet weak var libraryButton: UIButton! @IBOutlet weak var cameraButton: UIButton! @IBOutlet weak var videoButton: UIButton! - @IBOutlet weak var doneButton: UIButton! +// @IBOutlet weak var doneButton: UIButton! lazy var albumView = FSAlbumView.instance() lazy var cameraView = FSCameraView.instance() lazy var videoView = FSVideoCameraView.instance() + + lazy var arrowableTitleView: ArrowableTitleView = { + let x = UIScreen.main.bounds.width / 2 - 120 + let view = ArrowableTitleView(frame: CGRect(x: x, y: 0, width: 120, height: 40), delegate: self) + view.accessibilityIdentifier = "SelectAlbumButton" + return view + }() + + lazy var closeButton: UIBarButtonItem = { + let button = UIBarButtonItem(image: UIImage(named: "icon_delete_sign"), style: .plain, target: self, action: #selector(FusumaViewController.closeButtonPressed(_:))) + button.accessibilityIdentifier = NSLocalizedString("Close", comment: "Close") + button.accessibilityIdentifier = "CloseButton" + return button + }() + + lazy var doneButton: UIBarButtonItem = { + let button = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(FusumaViewController.doneButtonPressed(_:))) + button.accessibilityLabel = NSLocalizedString("Done", comment: "Done") + button.accessibilityIdentifier = "DoneButton" + return button + }() fileprivate var hasGalleryPermission: Bool { @@ -146,7 +167,7 @@ public struct ImageMetadata { override public func viewDidLoad() { super.viewDidLoad() - + self.view.backgroundColor = fusumaBackgroundColor cameraView.delegate = self @@ -157,8 +178,8 @@ public struct ImageMetadata { cameraButton.setTitle(fusumaCameraTitle, for: .normal) videoButton.setTitle(fusumaVideoTitle, for: .normal) - menuView.backgroundColor = fusumaBackgroundColor - menuView.addBottomBorder(UIColor.black, width: 1.0) +// menuView.backgroundColor = fusumaBackgroundColor +// menuView.addBottomBorder(UIColor.black, width: 1.0) albumView.allowMultipleSelection = allowMultipleSelection @@ -172,19 +193,29 @@ public struct ImageMetadata { let checkImage = fusumaCheckImage != nil ? fusumaCheckImage : UIImage(named: "ic_check", in: bundle, compatibleWith: nil) let closeImage = fusumaCloseImage != nil ? fusumaCloseImage : UIImage(named: "ic_close", in: bundle, compatibleWith: nil) - closeButton.setImage(closeImage?.withRenderingMode(.alwaysTemplate), for: .normal) - doneButton.setImage(checkImage?.withRenderingMode(.alwaysTemplate), for: .normal) - closeButton.setImage(closeImage?.withRenderingMode(.alwaysTemplate), for: .selected) - doneButton.setImage(checkImage?.withRenderingMode(.alwaysTemplate), for: .selected) - closeButton.setImage(closeImage?.withRenderingMode(.alwaysTemplate), for: .highlighted) - doneButton.setImage(checkImage?.withRenderingMode(.alwaysTemplate), for: .highlighted) + closeButton.setBackgroundImage(closeImage?.withRenderingMode(.alwaysTemplate), for: .normal, barMetrics: UIBarMetrics.default) + + doneButton.setBackgroundImage(checkImage?.withRenderingMode(.alwaysTemplate), for: .normal, barMetrics: UIBarMetrics.default) + closeButton.setBackgroundImage(closeImage?.withRenderingMode(.alwaysTemplate), for: .selected, barMetrics: UIBarMetrics.default) + doneButton.setBackgroundImage(checkImage?.withRenderingMode(.alwaysTemplate), for: .selected, barMetrics: UIBarMetrics.default) + closeButton.setBackgroundImage(closeImage?.withRenderingMode(.alwaysTemplate), for: .highlighted, barMetrics: UIBarMetrics.default) + doneButton.setBackgroundImage(checkImage?.withRenderingMode(.alwaysTemplate), for: .highlighted, barMetrics: UIBarMetrics.default) photoLibraryViewerContainer.addSubview(albumView) cameraShotContainer.addSubview(cameraView) videoShotContainer.addSubview(videoView) - titleLabel.textColor = fusumaTintColor - titleLabel.font = fusumaTitleFont +// titleLabel.textColor = fusumaTintColor +// titleLabel.font = fusumaTitleFont + + // start loading indicator + NotificationCenter.default.addObserver(self, selector: #selector(FusumaViewController.stopLoadingIndicator), name: NSNotification.Name.photoLibraryReloaded, object: nil) + startLoadingIndicator() + + navigationItem.titleView = arrowableTitleView + navigationItem.leftBarButtonItem = closeButton + navigationItem.rightBarButtonItem = doneButton + if availableModes.count == 0 || availableModes.count >= 4 { @@ -339,7 +370,7 @@ public struct ImageMetadata { return true } - @IBAction func closeButtonPressed(_ sender: UIButton) { + @IBAction func closeButtonPressed(_ sender: UIBarButtonItem) { self.delegate?.fusumaWillClosed() @@ -349,7 +380,7 @@ public struct ImageMetadata { } } - @IBAction func libraryButtonPressed(_ sender: UIButton) { + @IBAction func libraryButtonPressed(_ sender: UIBarButtonItem) { changeMode(FusumaMode.library) } @@ -582,33 +613,36 @@ private extension FusumaViewController { case .library: - titleLabel.text = NSLocalizedString(fusumaCameraRollTitle, comment: fusumaCameraRollTitle) + let title = albumView.getAssetCollectionTitle() ?? NSLocalizedString(fusumaCameraRollTitle, comment: fusumaCameraRollTitle) + arrowableTitleView.setTitle(text: title, hideArrow: false) + arrowableTitleView.isHidden = false highlightButton(libraryButton) self.view.bringSubview(toFront: photoLibraryViewerContainer) case .camera: - titleLabel.text = NSLocalizedString(fusumaCameraTitle, comment: fusumaCameraTitle) + arrowableTitleView.setTitle(text: NSLocalizedString(fusumaCameraTitle, comment: fusumaCameraTitle), hideArrow: true) highlightButton(cameraButton) self.view.bringSubview(toFront: cameraShotContainer) cameraView.startCamera() case .video: - titleLabel.text = NSLocalizedString(fusumaVideoTitle, comment: fusumaVideoTitle) + arrowableTitleView.setTitle(text: fusumaVideoTitle, hideArrow: true) + arrowableTitleView.isHidden = false highlightButton(videoButton) self.view.bringSubview(toFront: videoShotContainer) videoView.startCamera() } - self.view.bringSubview(toFront: menuView) +// self.view.bringSubview(toFront: menuView) } func updateDoneButtonVisibility() { if !hasGalleryPermission { - self.doneButton.isHidden = true + self.doneButton.isEnabled = false return } @@ -616,11 +650,11 @@ private extension FusumaViewController { case .library: - self.doneButton.isHidden = false + self.doneButton.isEnabled = true default: - self.doneButton.isHidden = true + self.doneButton.isEnabled = false } } @@ -662,3 +696,39 @@ private extension FusumaViewController { } } } + +extension FusumaViewController: ArrowableTitleViewDelegate { + public func viewDidTapped(_ view: ArrowableTitleView, state: Bool) { + guard mode == .library else { return } + let albumListVC = FSAlbumSelectionViewController(delegate: self) + let nav = UINavigationController(rootViewController: albumListVC) + present(nav, animated: true, completion: nil) + } +} + +extension FusumaViewController: FSAlbumSelectionViewControllerDelegate { + func didSelectAlbum(sender: FSAlbumSelectionViewController, albumSelected: AlbumModel) { + dismiss(animated: true, completion: nil) + + albumView.updateAssetCollection(assetCollection: albumSelected.collection) + arrowableTitleView.setTitle(text: albumSelected.collection.localizedTitle!, hideArrow: false) + arrowableTitleView.toggle() + } + + func didSelectCancel(sender: FSAlbumSelectionViewController) { + dismiss(animated: true, completion: nil) + arrowableTitleView.toggle() + } +} + +extension FusumaViewController { + @objc open func stopLoadingIndicator() { + // create your own indicator + // BPProgressHUD.dismiss() + } + + @objc open func startLoadingIndicator() { + // create your own indicator + // BPProgressHUD.showDark() + } +} diff --git a/Sources/FusumaViewController.xib b/Sources/FusumaViewController.xib index 4126bb1b..d25d5777 100644 --- a/Sources/FusumaViewController.xib +++ b/Sources/FusumaViewController.xib @@ -1,30 +1,21 @@ - + - + - - - AvenirNext-DemiBold - - - - - - @@ -47,58 +38,6 @@ - - - - - - - - - - - - - - - - - - - - - @@ -159,15 +98,12 @@ - - - @@ -181,8 +117,6 @@ - - From 77a3a8a97a224909885e6cc739acdaae72994d0a Mon Sep 17 00:00:00 2001 From: John Kuan Date: Mon, 4 Dec 2017 12:23:23 +0800 Subject: [PATCH 08/13] attempt to fix the height issue on arrowabletitleview --- Sources/ArrowImageView.swift | 3 ++- Sources/ArrowableTitleView.swift | 25 ++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Sources/ArrowImageView.swift b/Sources/ArrowImageView.swift index 30261484..3182ac1d 100644 --- a/Sources/ArrowImageView.swift +++ b/Sources/ArrowImageView.swift @@ -13,13 +13,14 @@ import UIKit var duration: Double = 0.3 func setup() { + transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2)) translatesAutoresizingMaskIntoConstraints = false setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .horizontal) setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .horizontal) setContentHuggingPriority(UILayoutPriorityRequired, for: .horizontal) setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .vertical) setContentHuggingPriority(UILayoutPriorityRequired, for: .vertical) - transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2)) + self.addConstraint(NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 12.0)) } init() { diff --git a/Sources/ArrowableTitleView.swift b/Sources/ArrowableTitleView.swift index 35a2bf39..2044c1ed 100644 --- a/Sources/ArrowableTitleView.swift +++ b/Sources/ArrowableTitleView.swift @@ -24,20 +24,21 @@ final public class ArrowableTitleView: UIView { } else { label.font = UIFont(name: "HelveticaNeue-Medium", size: 17) } + label.addConstraint(NSLayoutConstraint(item: label, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 40.0)) label.textAlignment = .center - label.translatesAutoresizingMaskIntoConstraints = false + label.translatesAutoresizingMaskIntoConstraints = true label.textColor = .white + label.setContentHuggingPriority(UILayoutPriorityRequired, for: .horizontal) + label.setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .horizontal) + label.setContentHuggingPriority(UILayoutPriorityRequired, for: .vertical) + label.setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .vertical) return label }() lazy var chevronView: ArrowImageView = { let imageV = ArrowImageView(template: true) - - imageV.setContentHuggingPriority(UILayoutPriorityRequired, for: .horizontal) - imageV.setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .horizontal) imageV.tintColor = .white - imageV.translatesAutoresizingMaskIntoConstraints = false imageV.isHidden = true return imageV }() @@ -67,8 +68,6 @@ final public class ArrowableTitleView: UIView { func setupViews() { translatesAutoresizingMaskIntoConstraints = false - - if #available(iOS 9, *) { heightAnchor.constraint(equalToConstant: 40.0).isActive = true stack.leftAnchor.constraint(equalTo: leftAnchor).isActive = true @@ -83,15 +82,15 @@ final public class ArrowableTitleView: UIView { chevronView.addConstraints([ NSLayoutConstraint(item: chevronView, attribute: .left, relatedBy: .equal, toItem: titleLabel, attribute: .right, multiplier: 1.0, constant: 8.0), NSLayoutConstraint(item: chevronView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0), - NSLayoutConstraint(item: titleLabel, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0), - NSLayoutConstraint(item: titleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 40.0) + NSLayoutConstraint(item: chevronView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0), + NSLayoutConstraint(item: chevronView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 40.0) ]) } let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(selected)) addGestureRecognizer(tap) // there is some resizing issue if do not set this back to true - translatesAutoresizingMaskIntoConstraints = true +// translatesAutoresizingMaskIntoConstraints = true sizeToFit() } @@ -106,7 +105,7 @@ final public class ArrowableTitleView: UIView { } } - func setTitle(text: String, hideArrow: Bool) { + public func setTitle(text: String, hideArrow: Bool) { translatesAutoresizingMaskIntoConstraints = false titleLabel.text = text titleLabel.sizeToFit() @@ -118,6 +117,10 @@ final public class ArrowableTitleView: UIView { translatesAutoresizingMaskIntoConstraints = true } } + + public func setTitleColor(color: UIColor) { + titleLabel.textColor = color + } @objc func selected() { toggle() From d2ecf97c26c5bff3cc7d4a753946c3b5b98743da Mon Sep 17 00:00:00 2001 From: John Kuan Date: Mon, 4 Dec 2017 12:24:55 +0800 Subject: [PATCH 09/13] fix position of the cropView after removing menuView fix image duplication issue on close and done buttons --- Sources/FSAlbumView.swift | 4 ++-- Sources/FSAlbumView.xib | 8 ++++---- Sources/FSVideoCameraView.xib | 16 ++++++++-------- Sources/FusumaViewController.swift | 6 ++++-- Sources/FusumaViewController.xib | 13 +++++++------ 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/Sources/FSAlbumView.swift b/Sources/FSAlbumView.swift index 2d753eb3..4b8fa50f 100644 --- a/Sources/FSAlbumView.swift +++ b/Sources/FSAlbumView.swift @@ -83,7 +83,7 @@ final class FSAlbumView: UIView, UICollectionViewDataSource, UICollectionViewDel fileprivate var dragDirection = Direction.up - private let imageCropViewOriginalConstraintTop: CGFloat = 50 + private let imageCropViewOriginalConstraintTop: CGFloat = 0.0 private let imageCropViewMinimalVisibleHeight: CGFloat = 100 private var imaginaryCollectionViewOffsetStartPosY: CGFloat = 0.0 @@ -121,7 +121,7 @@ final class FSAlbumView: UIView, UICollectionViewDataSource, UICollectionViewDel self.addGestureRecognizer(panGesture) collectionViewConstraintHeight.constant = self.frame.height - imageCropViewContainer.frame.height - imageCropViewOriginalConstraintTop - imageCropViewConstraintTop.constant = 50 + imageCropViewConstraintTop.constant = 0 dragDirection = Direction.up imageCropViewContainer.layer.shadowColor = UIColor.black.cgColor diff --git a/Sources/FSAlbumView.xib b/Sources/FSAlbumView.xib index 9bc1d03f..092c2f9c 100644 --- a/Sources/FSAlbumView.xib +++ b/Sources/FSAlbumView.xib @@ -1,11 +1,11 @@ - + - + @@ -45,7 +45,7 @@ - + @@ -68,7 +68,7 @@ - + diff --git a/Sources/FSVideoCameraView.xib b/Sources/FSVideoCameraView.xib index bbf5317d..3ca62ac1 100644 --- a/Sources/FSVideoCameraView.xib +++ b/Sources/FSVideoCameraView.xib @@ -1,11 +1,11 @@ - + - + @@ -17,17 +17,17 @@ - + - +