diff --git a/Yep.xcodeproj/project.pbxproj b/Yep.xcodeproj/project.pbxproj index 4258a846..f4b246e2 100644 --- a/Yep.xcodeproj/project.pbxproj +++ b/Yep.xcodeproj/project.pbxproj @@ -75,6 +75,8 @@ 503CAB431AC00CB100DFE830 /* ConversationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503CAB421AC00CB100DFE830 /* ConversationViewController.swift */; }; 504F41251ACE2D1D00FBB19A /* SampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504F41241ACE2D1D00FBB19A /* SampleView.swift */; }; 504F41571AD4D35400FBB19A /* ProfileLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504F41561AD4D35400FBB19A /* ProfileLayout.swift */; }; + 504F415A1AD5028200FBB19A /* SkillCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504F41581AD5028200FBB19A /* SkillCell.swift */; }; + 504F415B1AD5028200FBB19A /* SkillCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 504F41591AD5028200FBB19A /* SkillCell.xib */; }; 506EC20A1AC95BFC00CE42B3 /* UIFont+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506EC2091AC95BFC00CE42B3 /* UIFont+Yep.swift */; }; 5076C56A1AC46B9F00B22952 /* FayeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5076C5691AC46B9F00B22952 /* FayeService.swift */; }; 508D17401ACCD2740092D666 /* YepButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508D173F1ACCD2740092D666 /* YepButton.swift */; }; @@ -189,6 +191,8 @@ 503CAB421AC00CB100DFE830 /* ConversationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationViewController.swift; path = ViewControllers/Conversation/ConversationViewController.swift; sourceTree = ""; }; 504F41241ACE2D1D00FBB19A /* SampleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SampleView.swift; path = Views/AudioWaves/SampleView.swift; sourceTree = ""; }; 504F41561AD4D35400FBB19A /* ProfileLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfileLayout.swift; path = ViewControllers/Profile/ProfileLayout.swift; sourceTree = ""; }; + 504F41581AD5028200FBB19A /* SkillCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SkillCell.swift; path = Views/Cells/Skill/SkillCell.swift; sourceTree = ""; }; + 504F41591AD5028200FBB19A /* SkillCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SkillCell.xib; path = Views/Cells/Skill/SkillCell.xib; sourceTree = ""; }; 506EC2091AC95BFC00CE42B3 /* UIFont+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIFont+Yep.swift"; path = "Extensions/UIFont+Yep.swift"; sourceTree = ""; }; 5076C5691AC46B9F00B22952 /* FayeService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FayeService.swift; path = Services/FayeService.swift; sourceTree = ""; }; 508D173F1ACCD2740092D666 /* YepButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepButton.swift; path = Views/Buttons/YepButton.swift; sourceTree = ""; }; @@ -437,6 +441,7 @@ children = ( 5023DE3B1AB697B000B3EE96 /* Conversation */, 502AE5421AB88866005BD199 /* SkillRank */, + 504F415C1AD5028500FBB19A /* Skill */, 502AE5501AB93178005BD199 /* ProfileHeader */, 502AE5551AB93218005BD199 /* ProfileFooter */, 50E114FE1ABC549800F13000 /* Contacts */, @@ -624,6 +629,15 @@ name = Conversation; sourceTree = ""; }; + 504F415C1AD5028500FBB19A /* Skill */ = { + isa = PBXGroup; + children = ( + 504F41581AD5028200FBB19A /* SkillCell.swift */, + 504F41591AD5028200FBB19A /* SkillCell.xib */, + ); + name = Skill; + sourceTree = ""; + }; 508D17491ACCF4740092D666 /* ChatLeftAudio */ = { isa = PBXGroup; children = ( @@ -826,6 +840,7 @@ buildActionMask = 2147483647; files = ( 5023DE381AB6979600B3EE96 /* ConversationCell.xib in Resources */, + 504F415B1AD5028200FBB19A /* SkillCell.xib in Resources */, 502AE5591AB932D5005BD199 /* ProfileSectionHeaderReusableView.xib in Resources */, 502AE5411AB88862005BD199 /* SkillRankCell.xib in Resources */, 502AE5541AB93215005BD199 /* ProfileFooterCell.xib in Resources */, @@ -980,6 +995,7 @@ 502AE5531AB93215005BD199 /* ProfileFooterCell.swift in Sources */, 0A5D83991AC5D91E000045BF /* ContactsSearchTableViewController.swift in Sources */, 0A944C251AC8BB2F00037A06 /* YepStorageService.swift in Sources */, + 504F415A1AD5028200FBB19A /* SkillCell.swift in Sources */, 5023DE521AB6C51300B3EE96 /* CGRect+Yep.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Yep/Base.lproj/Main.storyboard b/Yep/Base.lproj/Main.storyboard index 7419f8fc..2abbc0cd 100644 --- a/Yep/Base.lproj/Main.storyboard +++ b/Yep/Base.lproj/Main.storyboard @@ -238,7 +238,7 @@ - + diff --git a/Yep/Extensions/UIFont+Yep.swift b/Yep/Extensions/UIFont+Yep.swift index 562eb697..54856133 100644 --- a/Yep/Extensions/UIFont+Yep.swift +++ b/Yep/Extensions/UIFont+Yep.swift @@ -12,4 +12,8 @@ extension UIFont { class func chatTextFont() -> UIFont { return UIFont.systemFontOfSize(16) } + + class func skillTextFont() -> UIFont { + return UIFont(name: "Helvetica-Light", size: 14)! + } } diff --git a/Yep/Images.xcassets/skill_bubble.imageset/Contents.json b/Yep/Images.xcassets/skill_bubble.imageset/Contents.json new file mode 100644 index 00000000..db5d9853 --- /dev/null +++ b/Yep/Images.xcassets/skill_bubble.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "skill_bubble.pdf", + "resizing" : { + "mode" : "3-part-horizontal", + "center" : { + "mode" : "fill", + "width" : 1 + }, + "capInsets" : { + "right" : 12, + "left" : 12 + } + } + } + ], + "info" : { + "version" : 1, + "author" : "xcode", + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Yep/Images.xcassets/skill_bubble.imageset/skill_bubble.pdf b/Yep/Images.xcassets/skill_bubble.imageset/skill_bubble.pdf new file mode 100644 index 00000000..21cf8eb5 Binary files /dev/null and b/Yep/Images.xcassets/skill_bubble.imageset/skill_bubble.pdf differ diff --git a/Yep/ViewControllers/Profile/ProfileLayout.swift b/Yep/ViewControllers/Profile/ProfileLayout.swift index 7cabbb7b..de0aaad3 100644 --- a/Yep/ViewControllers/Profile/ProfileLayout.swift +++ b/Yep/ViewControllers/Profile/ProfileLayout.swift @@ -32,6 +32,55 @@ class ProfileLayout: UICollectionViewFlowLayout { } } + // 先按照每个 item 的 centerY 分组 + var rowCollections = [CGFloat: [UICollectionViewLayoutAttributes]]() + for (index, attributes) in enumerate(layoutAttributes) { + let centerY = CGRectGetMidY(attributes.frame) + + if let rowCollection = rowCollections[centerY] { + var rowCollection = rowCollection + rowCollection.append(attributes) + rowCollections[centerY] = rowCollection + + } else { + rowCollections[centerY] = [attributes] + } + } + + // 再调整每一行的 item 的 frame + for (key, rowCollection) in rowCollections { + let rowItemsCount = rowCollection.count + + // 每一行总的 InteritemSpacing + let aggregateInteritemSpacing = minimumInteritemSpacing * CGFloat(rowItemsCount - 1) + + // 每一行所有 items 的宽度 + var aggregateItemsWidth: CGFloat = 0 + for attributes in rowCollection { + aggregateItemsWidth += CGRectGetWidth(attributes.frame) + } + + // 计算出有效的 width 和需要偏移的 offset + let alignmentWidth = aggregateItemsWidth + aggregateInteritemSpacing + let alignmentOffsetX = (CGRectGetWidth(collectionView!.bounds) - alignmentWidth) / 2 + + // 调整每个 item 的 origin.x 即可 + var previousFrame = CGRectZero + for attributes in rowCollection { + var itemFrame = attributes.frame + + if CGRectEqualToRect(previousFrame, CGRectZero) { + itemFrame.origin.x = alignmentOffsetX + } else { + itemFrame.origin.x = CGRectGetMaxX(previousFrame) + minimumInteritemSpacing + } + + attributes.frame = itemFrame + + previousFrame = itemFrame + } + } + return layoutAttributes } diff --git a/Yep/ViewControllers/Profile/ProfileViewController.swift b/Yep/ViewControllers/Profile/ProfileViewController.swift index e528d190..0822c4c1 100644 --- a/Yep/ViewControllers/Profile/ProfileViewController.swift +++ b/Yep/ViewControllers/Profile/ProfileViewController.swift @@ -16,7 +16,7 @@ class ProfileViewController: UIViewController { @IBOutlet weak var profileCollectionView: UICollectionView! - let skillRanckCellIdentifier = "SkillRankCell" + let skillCellIdentifier = "SkillCell" let headerCellIdentifier = "ProfileHeaderCell" let footerCellIdentifier = "ProfileFooterCell" let sectionHeaderIdentifier = "ProfileSectionHeaderReusableView" @@ -27,12 +27,9 @@ class ProfileViewController: UIViewController { }() lazy var sectionLeftEdgeInset: CGFloat = { return 20 }() lazy var sectionRightEdgeInset: CGFloat = { return 20 }() - lazy var sectionBottomEdgeInset: CGFloat = { return 20 }() + lazy var sectionBottomEdgeInset: CGFloat = { return 15 }() - lazy var cellWidth: CGFloat = { - return (self.collectionViewWidth - (self.sectionLeftEdgeInset + self.sectionRightEdgeInset)) / 3 - }() - let cellHeight: CGFloat = 40 + let skillCellHeight: CGFloat = 24 let introductionText = "I would like to learn Design or Speech, I can teach you iOS Dev in return. 😃" @@ -41,6 +38,9 @@ class ProfileViewController: UIViewController { ["skill":"iOS Dev", "rank":3], ["skill":"Linux", "rank":2], ["skill":"Cook", "rank":1], + ["skill":"Love", "rank":1], + ["skill":"Walk", "rank":1], + ["skill":"Eat", "rank":1], ] let learningSkills = [ @@ -48,6 +48,8 @@ class ProfileViewController: UIViewController { ["skill":"Speech", "rank":0], ] + let skillTextAttributes = [NSFontAttributeName: UIFont.skillTextFont()] + lazy var footerCellHeight: CGFloat = { let attributes = [NSFontAttributeName: profileIntroductionLabelFont] let labelWidth = self.collectionViewWidth - (profileIntroductionLabelLeadingSpaceToContainer + profileIntroductionLabelTrailingSpaceToContainer) @@ -58,7 +60,7 @@ class ProfileViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - profileCollectionView.registerNib(UINib(nibName: skillRanckCellIdentifier, bundle: nil), forCellWithReuseIdentifier: skillRanckCellIdentifier) + profileCollectionView.registerNib(UINib(nibName: skillCellIdentifier, bundle: nil), forCellWithReuseIdentifier: skillCellIdentifier) profileCollectionView.registerNib(UINib(nibName: headerCellIdentifier, bundle: nil), forCellWithReuseIdentifier: headerCellIdentifier) profileCollectionView.registerNib(UINib(nibName: footerCellIdentifier, bundle: nil), forCellWithReuseIdentifier: footerCellIdentifier) profileCollectionView.registerNib(UINib(nibName: sectionHeaderIdentifier, bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: sectionHeaderIdentifier) @@ -66,7 +68,7 @@ class ProfileViewController: UIViewController { profileCollectionView.alwaysBounceVertical = true - self.automaticallyAdjustsScrollViewInsets = false + automaticallyAdjustsScrollViewInsets = false if let tabBarController = tabBarController { profileCollectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: CGRectGetHeight(tabBarController.tabBar.bounds), right: 0) @@ -78,14 +80,16 @@ class ProfileViewController: UIViewController { navigationController.navigationBar.shadowImage = UIImage() navigationController.navigationBar.barStyle = UIBarStyle.BlackTranslucent navigationController.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default) + + let textAttributes = [ + NSForegroundColorAttributeName: UIColor.whiteColor(), + NSFontAttributeName: UIFont(name: "HelveticaNeue-CondensedBlack", size: 20)! + ] + + navigationController.navigationBar.titleTextAttributes = textAttributes + + navigationItem.title = YepUserDefaults.nickname() } - - let textAttributes = [ - NSForegroundColorAttributeName: UIColor.whiteColor(), - NSFontAttributeName: UIFont(name: "HelveticaNeue-CondensedBlack", size: 20)! - ] - - self.navigationController?.navigationBar.titleTextAttributes = textAttributes } override func viewWillAppear(animated: Bool) { @@ -151,9 +155,9 @@ extension ProfileViewController: UICollectionViewDataSource, UICollectionViewDel enum ProfileSection: Int { case Header = 0 + case Footer case Master case Learning - case Footer } func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { @@ -168,10 +172,10 @@ extension ProfileViewController: UICollectionViewDataSource, UICollectionViewDel return 1 case ProfileSection.Master.rawValue: - return 3 + return masterSkills.count case ProfileSection.Learning.rawValue: - return 2 + return learningSkills.count case ProfileSection.Footer.rawValue: return 1 @@ -229,24 +233,19 @@ extension ProfileViewController: UICollectionViewDataSource, UICollectionViewDel return cell case ProfileSection.Master.rawValue: - let cell = collectionView.dequeueReusableCellWithReuseIdentifier(skillRanckCellIdentifier, forIndexPath: indexPath) as! SkillRankCell - - cell.rankView.barColor = UIColor.skillMasterColor() + let cell = collectionView.dequeueReusableCellWithReuseIdentifier(skillCellIdentifier, forIndexPath: indexPath) as! SkillCell let skillInfo = masterSkills[indexPath.row % masterSkills.count] cell.skillLabel.text = skillInfo["skill"] as? String - cell.rankView.rank = skillInfo["rank"] as! Int + return cell case ProfileSection.Learning.rawValue: - let cell = collectionView.dequeueReusableCellWithReuseIdentifier(skillRanckCellIdentifier, forIndexPath: indexPath) as! SkillRankCell - - cell.rankView.barColor = UIColor.skillLearningColor() + let cell = collectionView.dequeueReusableCellWithReuseIdentifier(skillCellIdentifier, forIndexPath: indexPath) as! SkillCell let skillInfo = learningSkills[indexPath.row % learningSkills.count] cell.skillLabel.text = skillInfo["skill"] as? String - cell.rankView.rank = skillInfo["rank"] as! Int return cell @@ -258,7 +257,7 @@ extension ProfileViewController: UICollectionViewDataSource, UICollectionViewDel return cell default: - let cell = collectionView.dequeueReusableCellWithReuseIdentifier(skillRanckCellIdentifier, forIndexPath: indexPath) as! SkillRankCell + let cell = collectionView.dequeueReusableCellWithReuseIdentifier(skillCellIdentifier, forIndexPath: indexPath) as! SkillCell return cell } @@ -272,18 +271,12 @@ extension ProfileViewController: UICollectionViewDataSource, UICollectionViewDel switch indexPath.section { - case ProfileSection.Header.rawValue: - header.titleLabel.text = "" - case ProfileSection.Master.rawValue: header.titleLabel.text = NSLocalizedString("Master", comment: "") case ProfileSection.Learning.rawValue: header.titleLabel.text = NSLocalizedString("Learning", comment: "") - case ProfileSection.Footer.rawValue: - header.titleLabel.text = NSLocalizedString("Introduction", comment: "") - default: header.titleLabel.text = "" } @@ -325,10 +318,28 @@ extension ProfileViewController: UICollectionViewDataSource, UICollectionViewDel return CGSizeMake(collectionViewWidth, collectionViewWidth * profileAvatarAspectRatio) case ProfileSection.Master.rawValue: - return CGSizeMake(cellWidth, cellHeight) + + let skillInfo = masterSkills[indexPath.row % masterSkills.count] + + if let skillString = skillInfo["skill"] as? String { + let rect = skillString.boundingRectWithSize(CGSize(width: CGFloat(FLT_MAX), height: skillCellHeight), options: .UsesLineFragmentOrigin | .UsesFontLeading, attributes: skillTextAttributes, context: nil) + + return CGSizeMake(rect.width + 24, skillCellHeight) + } + + return CGSizeZero case ProfileSection.Learning.rawValue: - return CGSizeMake(cellWidth, cellHeight) + + let skillInfo = learningSkills[indexPath.row % learningSkills.count] + + if let skillString = skillInfo["skill"] as? String { + let rect = skillString.boundingRectWithSize(CGSize(width: CGFloat(FLT_MAX), height: skillCellHeight), options: .UsesLineFragmentOrigin | .UsesFontLeading, attributes: skillTextAttributes, context: nil) + + return CGSizeMake(rect.width + 24, skillCellHeight) + } + + return CGSizeZero case ProfileSection.Footer.rawValue: return CGSizeMake(collectionViewWidth, footerCellHeight) @@ -340,11 +351,11 @@ extension ProfileViewController: UICollectionViewDataSource, UICollectionViewDel func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { - if section == ProfileSection.Header.rawValue { + if section == ProfileSection.Header.rawValue || section == ProfileSection.Footer.rawValue { return CGSizeMake(collectionViewWidth, 0) } else { - return CGSizeMake(collectionViewWidth, cellHeight) + return CGSizeMake(collectionViewWidth, 40) } } diff --git a/Yep/Views/Cells/ProfileFooter/ProfileFooterCell.xib b/Yep/Views/Cells/ProfileFooter/ProfileFooterCell.xib index fe905059..1d981173 100644 --- a/Yep/Views/Cells/ProfileFooter/ProfileFooterCell.xib +++ b/Yep/Views/Cells/ProfileFooter/ProfileFooterCell.xib @@ -1,7 +1,7 @@ - + - + @@ -12,9 +12,9 @@ - - - + + - diff --git a/Yep/Views/Cells/Skill/SkillCell.swift b/Yep/Views/Cells/Skill/SkillCell.swift new file mode 100644 index 00000000..f124a778 --- /dev/null +++ b/Yep/Views/Cells/Skill/SkillCell.swift @@ -0,0 +1,23 @@ +// +// SkillCell.swift +// Yep +// +// Created by NIX on 15/4/8. +// Copyright (c) 2015年 Catch Inc. All rights reserved. +// + +import UIKit + +class SkillCell: UICollectionViewCell { + + @IBOutlet weak var backgroundImageView: UIImageView! + + @IBOutlet weak var skillLabel: UILabel! + + override func awakeFromNib() { + super.awakeFromNib() + + skillLabel.font = UIFont.skillTextFont() + } + +} diff --git a/Yep/Views/Cells/Skill/SkillCell.xib b/Yep/Views/Cells/Skill/SkillCell.xib new file mode 100644 index 00000000..05a616f5 --- /dev/null +++ b/Yep/Views/Cells/Skill/SkillCell.xib @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yep/Views/ReusableViews/ProfileSectionHeader/ProfileSectionHeaderReusableView.xib b/Yep/Views/ReusableViews/ProfileSectionHeader/ProfileSectionHeaderReusableView.xib index 0baf45b7..79a1b619 100644 --- a/Yep/Views/ReusableViews/ProfileSectionHeader/ProfileSectionHeaderReusableView.xib +++ b/Yep/Views/ReusableViews/ProfileSectionHeader/ProfileSectionHeaderReusableView.xib @@ -1,7 +1,7 @@ - + - + @@ -10,9 +10,9 @@ -