diff --git a/Source/JSONFormBuilder.swift b/Source/JSONFormBuilder.swift index 08475c57a1..7df58d611e 100644 --- a/Source/JSONFormBuilder.swift +++ b/Source/JSONFormBuilder.swift @@ -64,12 +64,6 @@ class JSONFormBuilder { titleLabel.textAlignment = .Natural - let selectedAttributes = OEXTextStyle(weight: .Normal, size: .Small, color: OEXStyles.sharedStyles().neutralBlackT()) - let unselectedAttributes = OEXTextStyle(weight: .Normal, size: .Small, color: OEXStyles.sharedStyles().neutralDark()) - typeControl.setTitleTextAttributes(selectedAttributes.attributes, forState: .Selected) - typeControl.setTitleTextAttributes(unselectedAttributes.attributes, forState: .Normal) - typeControl.tintColor = OEXStyles.sharedStyles().primaryXLightColor() - descriptionLabel.textAlignment = .Natural descriptionLabel.numberOfLines = 0 descriptionLabel.preferredMaxLayoutWidth = 200 //value doesn't seem to matter as long as it's small enough diff --git a/Source/LoadStateViewController.swift b/Source/LoadStateViewController.swift index 1ea0c53456..5f96f7ce19 100644 --- a/Source/LoadStateViewController.swift +++ b/Source/LoadStateViewController.swift @@ -180,17 +180,20 @@ class LoadStateViewController : UIViewController { self.messageView.attributedMessage = error.attributedDescriptionWithBaseStyle(self.messageStyle) self.messageView.icon = info.icon ?? .UnknownError } + else if let message = info.attributedMessage { + self.messageView.attributedMessage = message + self.messageView.icon = info.icon ?? .UnknownError + } + else if let message = info.message { + self.messageView.message = message + self.messageView.icon = info.icon ?? .UnknownError + } else if let error = info.error where error.oex_isUnknownNetworkError { self.messageView.message = Strings.courseContentUnknown self.messageView.icon = info.icon ?? .UnknownError } else { - if let message = info.attributedMessage { - self.messageView.attributedMessage = message - } - else { - self.messageView.message = info.message ?? info.error?.localizedDescription - } + self.messageView.message = info.error?.localizedDescription self.messageView.icon = info.icon ?? .UnknownError } } diff --git a/Source/OEXStyles+Swift.swift b/Source/OEXStyles+Swift.swift index bebb307c83..c79ec8a06f 100644 --- a/Source/OEXStyles+Swift.swift +++ b/Source/OEXStyles+Swift.swift @@ -37,7 +37,7 @@ extension OEXStyles { let styleAttributes = OEXTextStyle(weight: .Normal, size : .Small, color : self.neutralBlack()).attributes UISegmentedControl.appearance().setTitleTextAttributes(styleAttributes, forState: UIControlState.Selected) UISegmentedControl.appearance().setTitleTextAttributes(styleAttributes, forState: UIControlState.Normal) - UISegmentedControl.appearance().tintColor = self.neutralLight() + UISegmentedControl.appearance().tintColor = self.primaryXLightColor() UINavigationBar.appearance().translucent = false diff --git a/Source/TabContainerView.swift b/Source/TabContainerView.swift new file mode 100644 index 0000000000..d7496ab098 --- /dev/null +++ b/Source/TabContainerView.swift @@ -0,0 +1,108 @@ +// +// TabContainerView.swift +// edX +// +// Created by Akiva Leffert on 4/5/16. +// Copyright © 2016 edX. All rights reserved. +// + +import Foundation + +// Simple tab view with a segmented control at the top +class TabContainerView : UIView { + + struct Item { + let name : String + let view : UIView + let identifier : String + } + + private let control = UISegmentedControl() + + private let stackView = TZStackView() + private var activeTabBodyView : UIView? = nil + + private var currentIdentifier : String? + + override init(frame: CGRect) { + super.init(frame: frame) + stackView.insertArrangedSubview(control, atIndex: 0) + stackView.axis = .Vertical + stackView.alignment = .Fill + stackView.spacing = StandardVerticalMargin + + addSubview(stackView) + stackView.snp_makeConstraints {make in + make.leading.equalTo(self.snp_leadingMargin) + make.trailing.equalTo(self.snp_trailingMargin) + make.top.equalTo(self.snp_topMargin) + make.bottom.equalTo(self.snp_bottomMargin) + } + + control.oex_addAction({[weak self] control in + let index = (control as! UISegmentedControl).selectedSegmentIndex + self?.showTabAtIndex(index) + }, forEvents: .ValueChanged) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + var items : [Item] = [] { + didSet { + control.removeAllSegments() + + for (index, item) in items.enumerate() { + control.insertSegmentWithTitle(item.name, atIndex: control.numberOfSegments, animated: false) + if item.identifier == currentIdentifier { + showTabAtIndex(index) + } + } + if control.selectedSegmentIndex == UISegmentedControlNoSegment && items.count > 0 { + showTabAtIndex(0) + } + else { + currentIdentifier = nil + } + + if items.count < 2 { + control.hidden = true + } + } + } + + private func showTabAtIndex(index: Int) { + guard index != UISegmentedControlNoSegment else { + return + } + + activeTabBodyView?.removeFromSuperview() + + let item = items[index] + control.selectedSegmentIndex = index + currentIdentifier = item.identifier + stackView.addArrangedSubview(item.view) + activeTabBodyView = item.view + } + + private func indexOfItemWithIdentifier(identifier : String) -> Int? { + return items.firstIndexMatching {$0.identifier == identifier } + } + + func showTabWithIdentifier(identifier : String) { + if let index = indexOfItemWithIdentifier(identifier) { + showTabAtIndex(index) + } + } +} + +// Only used for testing +extension TabContainerView { + func t_isShowingViewForItem(item : Item) -> Bool { + let viewsMatch = stackView.arrangedSubviews == [control, item.view] + let indexMatches = indexOfItemWithIdentifier(item.identifier) == control.selectedSegmentIndex + let identifierMatches = currentIdentifier == item.identifier + return viewsMatch && indexMatches && identifierMatches + } +} \ No newline at end of file diff --git a/Source/UserProfileView.swift b/Source/UserProfileView.swift new file mode 100644 index 0000000000..022e4c3a5a --- /dev/null +++ b/Source/UserProfileView.swift @@ -0,0 +1,245 @@ +// +// UserProfileView.swift +// edX +// +// Created by Akiva Leffert on 4/4/16. +// Copyright © 2016 edX. All rights reserved. +// + +class UserProfileView : UIView, UIScrollViewDelegate { + + private let margin = 4 + + private class SystemLabel: UILabel { + private override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect { + return CGRectInset(super.textRectForBounds(bounds, limitedToNumberOfLines: numberOfLines), 10, 0) + } + private override func drawTextInRect(rect: CGRect) { + let newRect = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) + super.drawTextInRect(UIEdgeInsetsInsetRect(rect, newRect)) + } + } + + private let scrollView = UIScrollView() + private let usernameLabel = UILabel() + private let messageLabel = UILabel() + private let countryLabel = UILabel() + private let languageLabel = UILabel() + private let bioText = UITextView() + private let tabs = TabContainerView() + private let bioSystemMessage = SystemLabel() + private let avatarImage = ProfileImageView() + private let header = ProfileBanner() + private let bottomBackground = UIView() + + override init(frame: CGRect) { + super.init(frame: frame) + + self.addSubview(scrollView) + + setupViews() + setupConstraints() + } + + private func setupViews() { + scrollView.backgroundColor = OEXStyles.sharedStyles().primaryBaseColor() + scrollView.delegate = self + + avatarImage.borderWidth = 3.0 + scrollView.addSubview(avatarImage) + + usernameLabel.setContentHuggingPriority(1000, forAxis: .Vertical) + scrollView.addSubview(usernameLabel) + + messageLabel.hidden = true + messageLabel.numberOfLines = 0 + messageLabel.setContentHuggingPriority(1000, forAxis: .Vertical) + scrollView.addSubview(messageLabel) + + languageLabel.accessibilityHint = Strings.Profile.languageAccessibilityHint + languageLabel.setContentHuggingPriority(1000, forAxis: .Vertical) + scrollView.addSubview(languageLabel) + + countryLabel.accessibilityHint = Strings.Profile.countryAccessibilityHint + countryLabel.setContentHuggingPriority(1000, forAxis: .Vertical) + scrollView.addSubview(countryLabel) + + bioText.backgroundColor = UIColor.clearColor() + bioText.textAlignment = .Natural + bioText.scrollEnabled = false + bioText.editable = false + bioText.textContainer.lineFragmentPadding = 0; + bioText.textContainerInset = UIEdgeInsetsZero + + tabs.layoutMargins = UIEdgeInsets(top: StandardHorizontalMargin, left: StandardHorizontalMargin, bottom: StandardHorizontalMargin, right: StandardHorizontalMargin) + + tabs.items = [ + TabContainerView.Item(name: "About", view: bioText, identifier: "bio") + ] + scrollView.addSubview(tabs) + + bottomBackground.backgroundColor = bioText.backgroundColor + scrollView.insertSubview(bottomBackground, belowSubview: tabs) + + bioSystemMessage.hidden = true + bioSystemMessage.numberOfLines = 0 + bioSystemMessage.backgroundColor = OEXStyles.sharedStyles().neutralXLight() + scrollView.insertSubview(bioSystemMessage, aboveSubview: tabs) + + header.style = .LightContent + header.backgroundColor = scrollView.backgroundColor + header.hidden = true + self.addSubview(header) + + bottomBackground.backgroundColor = OEXStyles.sharedStyles().standardBackgroundColor() + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupConstraints() { + scrollView.snp_makeConstraints { (make) -> Void in + make.edges.equalTo(self) + } + avatarImage.snp_makeConstraints { (make) -> Void in + make.width.equalTo(avatarImage.snp_height) + make.width.equalTo(166) + make.centerX.equalTo(scrollView) + make.top.equalTo(scrollView.snp_topMargin).offset(20) + } + + usernameLabel.snp_makeConstraints { (make) -> Void in + make.top.equalTo(avatarImage.snp_bottom).offset(margin) + make.centerX.equalTo(scrollView) + } + + messageLabel.snp_makeConstraints { (make) -> Void in + make.top.equalTo(usernameLabel.snp_bottom).offset(margin).priorityHigh() + make.centerX.equalTo(scrollView) + } + + languageLabel.snp_makeConstraints { (make) -> Void in + make.top.equalTo(messageLabel.snp_bottom) + make.centerX.equalTo(scrollView) + } + + countryLabel.snp_makeConstraints { (make) -> Void in + make.top.equalTo(languageLabel.snp_bottom) + make.centerX.equalTo(scrollView) + } + + tabs.snp_makeConstraints { (make) -> Void in + make.top.equalTo(countryLabel.snp_bottom).offset(35).priorityHigh() + make.bottom.equalTo(scrollView) + make.leading.equalTo(scrollView) + make.trailing.equalTo(scrollView) + make.width.equalTo(scrollView) + } + + bioSystemMessage.snp_makeConstraints { (make) -> Void in + make.top.equalTo(tabs) + make.bottom.greaterThanOrEqualTo(self) + make.leading.equalTo(scrollView) + make.trailing.equalTo(scrollView) + make.width.equalTo(scrollView) + } + + bottomBackground.snp_makeConstraints {make in + make.edges.equalTo(bioSystemMessage) + } + + header.snp_makeConstraints { (make) -> Void in + make.top.equalTo(scrollView) + make.leading.equalTo(scrollView) + make.trailing.equalTo(scrollView) + make.height.equalTo(56) + } + } + + private func setMessage(message: String?) { + if let message = message { + let messageStyle = OEXTextStyle(weight: .Light, size: .XSmall, color: OEXStyles.sharedStyles().primaryXLightColor()) + + messageLabel.hidden = false + messageLabel.snp_remakeConstraints { (make) -> Void in + make.top.equalTo(usernameLabel.snp_bottom).offset(margin).priorityHigh() + make.centerX.equalTo(scrollView) + } + countryLabel.hidden = true + languageLabel.hidden = true + + messageLabel.attributedText = messageStyle.attributedStringWithText(message) + } else { + messageLabel.hidden = true + messageLabel.snp_updateConstraints(closure: { (make) -> Void in + make.height.equalTo(0) + }) + + countryLabel.hidden = false + languageLabel.hidden = false + + } + } + + private func messageForProfile(profile : UserProfile, editable : Bool) -> String? { + if profile.sharingLimitedProfile { + return editable ? Strings.Profile.showingLimited : Strings.Profile.learnerHasLimitedProfile(platformName: OEXConfig.sharedConfig().platformName()) + } + else { + return nil + } + } + + func populateFields(profile: UserProfile, editable : Bool, networkManager : NetworkManager) { + let usernameStyle = OEXTextStyle(weight : .Normal, size: .XXLarge, color: OEXStyles.sharedStyles().neutralWhiteT()) + let infoStyle = OEXTextStyle(weight: .Light, size: .XSmall, color: OEXStyles.sharedStyles().primaryXLightColor()) + let bioStyle = OEXStyles.sharedStyles().textAreaBodyStyle + let messageStyle = OEXMutableTextStyle(weight: .Bold, size: .Large, color: OEXStyles.sharedStyles().neutralDark()) + messageStyle.alignment = .Center + + + usernameLabel.attributedText = usernameStyle.attributedStringWithText(profile.username) + bioSystemMessage.hidden = true + + avatarImage.remoteImage = profile.image(networkManager) + + setMessage(messageForProfile(profile, editable: editable)) + + if profile.sharingLimitedProfile { + if (profile.parentalConsent ?? false) && editable { + let message = NSMutableAttributedString(attributedString: messageStyle.attributedStringWithText(Strings.Profile.ageLimit)) + + bioSystemMessage.attributedText = message + bioSystemMessage.hidden = false + } + } else { + self.bioText.text = "" + if let language = profile.language { + let icon = Icon.Comment.attributedTextWithStyle(infoStyle) + let langText = infoStyle.attributedStringWithText(language) + languageLabel.attributedText = NSAttributedString.joinInNaturalLayout([icon, langText]) + } + if let country = profile.country { + let icon = Icon.Country.attributedTextWithStyle(infoStyle) + let countryText = infoStyle.attributedStringWithText(country) + countryLabel.attributedText = NSAttributedString.joinInNaturalLayout([icon, countryText]) + } + if let bio = profile.bio { + bioText.attributedText = bioStyle.attributedStringWithText(bio) + } else { + let message = messageStyle.attributedStringWithText(Strings.Profile.noBio) + bioSystemMessage.attributedText = message + bioSystemMessage.hidden = false + } + } + + header.showProfile(profile, networkManager: networkManager) + } + + @objc func scrollViewDidScroll(scrollView: UIScrollView) { + UIView.animateWithDuration(0.25) { + self.header.hidden = scrollView.contentOffset.y < CGRectGetMaxY(self.avatarImage.frame) + } + } +} \ No newline at end of file diff --git a/Source/UserProfileViewController.swift b/Source/UserProfileViewController.swift index 802406b5a9..42c04994be 100644 --- a/Source/UserProfileViewController.swift +++ b/Source/UserProfileViewController.swift @@ -9,44 +9,22 @@ import UIKit public class UserProfileViewController: UIViewController { - private class SystemLabel: UILabel { - private override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect { - return CGRectInset(super.textRectForBounds(bounds, limitedToNumberOfLines: numberOfLines), 10, 0) - } - private override func drawTextInRect(rect: CGRect) { - let newRect = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) - super.drawTextInRect(UIEdgeInsetsInsetRect(rect, newRect)) - } - } public typealias Environment = protocol private let environment : Environment private let profileFeed: Feed + private let editable: Bool + + private let loadController = LoadStateViewController() + private let contentView = UserProfileView(frame: CGRectZero) - private let scrollView = UIScrollView() - private let margin = 4 - - private var avatarImage: ProfileImageView! - private var usernameLabel: UILabel = UILabel() - private var messageLabel: UILabel = UILabel() - private var countryLabel: UILabel = UILabel() - private var languageLabel: UILabel = UILabel() - private let bioText: UITextView = UITextView() - private let bioSystemMessage: UILabel = SystemLabel() - - private var header: ProfileBanner! - private var spinner = SpinnerView(size: SpinnerView.Size.Large, color: SpinnerView.Color.Primary) - private let editable:Bool - - public init(environment : Environment, feed: Feed, editable:Bool = true) { + public init(environment : Environment, feed: Feed, editable: Bool = true) { self.editable = editable self.environment = environment self.profileFeed = feed super.init(nibName: nil, bundle: nil) - - bioText.textContainerInset = UIEdgeInsets(top: 8, left: 10, bottom: 8, right: 10) } required public init?(coder aDecoder: NSCoder) { @@ -54,24 +32,20 @@ public class UserProfileViewController: UIViewController { } private func addListener() { + let editable = self.editable + let networkManager = environment.networkManager profileFeed.output.listen(self, success: { [weak self] profile in - self?.spinner.removeFromSuperview() - self?.populateFields(profile) - }, failure : { [weak self] _ in - self?.spinner.removeFromSuperview() - self?.setMessage(Strings.Profile.unableToGet) - self?.bioText.text = "" + self?.contentView.populateFields(profile, editable: editable, networkManager: networkManager) + self?.loadController.state = .Loaded + }, failure : { [weak self] error in + self?.loadController.state = LoadState.failed(error, message: Strings.Profile.unableToGet) }) } override public func viewDidLoad() { super.viewDidLoad() - - view.addSubview(scrollView) - scrollView.backgroundColor = OEXStyles.sharedStyles().primaryBaseColor() - scrollView.delegate = self - - scrollView.snp_makeConstraints { (make) -> Void in + view.addSubview(contentView) + contentView.snp_makeConstraints {make in make.edges.equalTo(view) } @@ -83,182 +57,11 @@ public class UserProfileViewController: UIViewController { editButton.accessibilityLabel = Strings.Profile.editAccessibility navigationItem.rightBarButtonItem = editButton } - - navigationController?.navigationBar.tintColor = OEXStyles.sharedStyles().neutralWhite() - navigationController?.navigationBar.barTintColor = OEXStyles.sharedStyles().primaryBaseColor() - - avatarImage = ProfileImageView() - avatarImage.borderWidth = 3.0 - scrollView.addSubview(avatarImage) - - usernameLabel.setContentHuggingPriority(1000, forAxis: .Vertical) - scrollView.addSubview(usernameLabel) - - messageLabel.hidden = true - messageLabel.numberOfLines = 0 - messageLabel.setContentHuggingPriority(1000, forAxis: .Vertical) - scrollView.addSubview(messageLabel) - - languageLabel.accessibilityHint = Strings.Profile.languageAccessibilityHint - languageLabel.setContentHuggingPriority(1000, forAxis: .Vertical) - scrollView.addSubview(languageLabel) - - countryLabel.accessibilityHint = Strings.Profile.countryAccessibilityHint - countryLabel.setContentHuggingPriority(1000, forAxis: .Vertical) - scrollView.addSubview(countryLabel) - - bioText.backgroundColor = OEXStyles.sharedStyles().neutralWhiteT() - bioText.textAlignment = .Natural - bioText.scrollEnabled = false - bioText.editable = false - scrollView.addSubview(bioText) - - let whiteSpace = UIView() - whiteSpace.backgroundColor = bioText.backgroundColor - scrollView.insertSubview(whiteSpace, belowSubview: bioText) - - bioSystemMessage.hidden = true - bioSystemMessage.numberOfLines = 0 - bioSystemMessage.backgroundColor = OEXStyles.sharedStyles().neutralXLight() - scrollView.insertSubview(bioSystemMessage, aboveSubview: bioText) - - avatarImage.snp_makeConstraints { (make) -> Void in - make.width.equalTo(avatarImage.snp_height) - make.width.equalTo(166) - make.centerX.equalTo(scrollView) - make.top.equalTo(scrollView.snp_topMargin).offset(20) - } - - usernameLabel.snp_makeConstraints { (make) -> Void in - make.top.equalTo(avatarImage.snp_bottom).offset(margin) - make.centerX.equalTo(scrollView) - } - - messageLabel.snp_makeConstraints { (make) -> Void in - make.top.equalTo(usernameLabel.snp_bottom).offset(margin).priorityHigh() - make.centerX.equalTo(scrollView) - } - languageLabel.snp_makeConstraints { (make) -> Void in - make.top.equalTo(messageLabel.snp_bottom) - make.centerX.equalTo(scrollView) - } - - countryLabel.snp_makeConstraints { (make) -> Void in - make.top.equalTo(languageLabel.snp_bottom) - make.centerX.equalTo(scrollView) - } + navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: UIBarButtonItemStyle.Plain, target: nil, action: nil) - bioText.snp_makeConstraints { (make) -> Void in - make.top.equalTo(countryLabel.snp_bottom).offset(35).priorityHigh() - make.bottom.equalTo(scrollView) - make.leading.equalTo(scrollView) - make.trailing.equalTo(scrollView) - make.width.equalTo(scrollView) - } - - whiteSpace.snp_makeConstraints { (make) -> Void in - make.top.equalTo(bioText) - make.bottom.greaterThanOrEqualTo(view) - make.leading.equalTo(bioText) - make.trailing.equalTo(bioText) - make.width.equalTo(bioText) - } - - bioSystemMessage.snp_makeConstraints { (make) -> Void in - make.edges.equalTo(whiteSpace) - } - - - header = ProfileBanner(frame: CGRectZero) - header.style = .LightContent - header.backgroundColor = scrollView.backgroundColor - header.hidden = true - view.addSubview(header) - - header.snp_makeConstraints { (make) -> Void in - make.top.equalTo(scrollView) - make.leading.equalTo(scrollView) - make.trailing.equalTo(scrollView) - make.height.equalTo(56) - } - addListener() } - - private func setMessage(message: String?) { - if let message = message { - let messageStyle = OEXTextStyle(weight: .Light, size: .XSmall, color: OEXStyles.sharedStyles().primaryXLightColor()) - - messageLabel.hidden = false - messageLabel.snp_remakeConstraints { (make) -> Void in - make.top.equalTo(usernameLabel.snp_bottom).offset(margin).priorityHigh() - make.centerX.equalTo(scrollView) - } - countryLabel.hidden = true - languageLabel.hidden = true - - messageLabel.attributedText = messageStyle.attributedStringWithText(message) - } else { - messageLabel.hidden = true - messageLabel.snp_updateConstraints(closure: { (make) -> Void in - make.height.equalTo(0) - }) - - countryLabel.hidden = false - languageLabel.hidden = false - - } - } - - private func populateFields(profile: UserProfile) { - let usernameStyle = OEXTextStyle(weight : .Normal, size: .XXLarge, color: OEXStyles.sharedStyles().neutralWhiteT()) - let infoStyle = OEXTextStyle(weight: .Light, size: .XSmall, color: OEXStyles.sharedStyles().primaryXLightColor()) - let bioStyle = OEXStyles.sharedStyles().textAreaBodyStyle - let messageStyle = OEXMutableTextStyle(weight: .Bold, size: .Large, color: OEXStyles.sharedStyles().neutralDark()) - messageStyle.alignment = .Center - - - usernameLabel.attributedText = usernameStyle.attributedStringWithText(profile.username) - bioSystemMessage.hidden = true - - avatarImage.remoteImage = profile.image(environment.networkManager) - - if profile.sharingLimitedProfile { - - setMessage(editable ? Strings.Profile.showingLimited : Strings.Profile.learnerHasLimitedProfile(platformName: OEXConfig.sharedConfig().platformName())) - bioText.text = "" - - if (profile.parentalConsent ?? false) && editable { - let message = NSMutableAttributedString(attributedString: messageStyle.attributedStringWithText(Strings.Profile.ageLimit)) - - bioSystemMessage.attributedText = message - bioSystemMessage.hidden = false - } - } else { - setMessage(nil) - - if let language = profile.language { - let icon = Icon.Comment.attributedTextWithStyle(infoStyle) - let langText = infoStyle.attributedStringWithText(language) - languageLabel.attributedText = NSAttributedString.joinInNaturalLayout([icon, langText]) - } - if let country = profile.country { - let icon = Icon.Country.attributedTextWithStyle(infoStyle) - let countryText = infoStyle.attributedStringWithText(country) - countryLabel.attributedText = NSAttributedString.joinInNaturalLayout([icon, countryText]) - } - if let bio = profile.bio { - bioText.attributedText = bioStyle.attributedStringWithText(bio) - } else { - let message = messageStyle.attributedStringWithText(Strings.Profile.noBio) - bioSystemMessage.attributedText = message - bioSystemMessage.hidden = false - } - } - - header.showProfile(profile, networkManager: environment.networkManager) - } public override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) @@ -269,12 +72,3 @@ public class UserProfileViewController: UIViewController { } -extension UserProfileViewController : UIScrollViewDelegate { - - public func scrollViewDidScroll(scrollView: UIScrollView) { - UIView.animateWithDuration(0.25) { - self.header.hidden = scrollView.contentOffset.y < CGRectGetMaxY(self.avatarImage.frame) - } - - } -} diff --git a/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios8_380x568@2x.png b/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios8_380x568@2x.png index 9549a95a56..70eb03468c 100644 Binary files a/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios8_380x568@2x.png and b/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios8_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios8_rtl_380x568@2x.png b/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios8_rtl_380x568@2x.png index cb51e89d36..0dff078e0a 100644 Binary files a/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios8_rtl_380x568@2x.png and b/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios8_rtl_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios9_380x568@2x.png b/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios9_380x568@2x.png index 1aee77267b..184d759ba5 100644 Binary files a/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios9_380x568@2x.png and b/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios9_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios9_rtl_380x568@2x.png b/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios9_rtl_380x568@2x.png index e8d60132ad..9aeef0a775 100644 Binary files a/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios9_rtl_380x568@2x.png and b/Test/Snapshots/edXTests.DiscussionNewCommentViewControllerTests/testContentResponse_ios9_rtl_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios8_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios8_380x568@2x.png index 959a7ac466..a8f455fcf7 100644 Binary files a/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios8_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios8_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios8_rtl_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios8_rtl_380x568@2x.png index 61fb1b1d6c..cc6651fbf8 100644 Binary files a/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios8_rtl_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios8_rtl_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios9_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios9_380x568@2x.png index 227ef590b9..abfd750336 100644 Binary files a/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios9_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios9_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios9_rtl_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios9_rtl_380x568@2x.png index 533f51d973..4077387000 100644 Binary files a/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios9_rtl_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileEditViewControllerTests/testSnapshotPublic_ios9_rtl_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios8_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios8_380x568@2x.png index bef654b272..58f6e63162 100644 Binary files a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios8_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios8_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios8_rtl_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios8_rtl_380x568@2x.png index eec15a9e47..ee328e1d75 100644 Binary files a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios8_rtl_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios8_rtl_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios9_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios9_380x568@2x.png index 97e43fd41c..749421dafd 100644 Binary files a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios9_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios9_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios9_rtl_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios9_rtl_380x568@2x.png index f5b6c386d0..a09c04fa39 100644 Binary files a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios9_rtl_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContentPrivateProfile_ios9_rtl_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios8_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios8_380x568@2x.png index 61df526a2b..2efaf91c4b 100644 Binary files a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios8_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios8_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios8_rtl_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios8_rtl_380x568@2x.png index 6b7f6c1f44..93285fd8ab 100644 Binary files a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios8_rtl_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios8_rtl_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios9_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios9_380x568@2x.png index cbacf5ecc8..a9dea12925 100644 Binary files a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios9_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios9_380x568@2x.png differ diff --git a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios9_rtl_380x568@2x.png b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios9_rtl_380x568@2x.png index f195a567ef..d1aa39157d 100644 Binary files a/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios9_rtl_380x568@2x.png and b/Test/Snapshots/edXTests.UserProfileViewTests/testSnapshotContent_ios9_rtl_380x568@2x.png differ diff --git a/Test/TabContainerViewTests.swift b/Test/TabContainerViewTests.swift new file mode 100644 index 0000000000..686965e1fe --- /dev/null +++ b/Test/TabContainerViewTests.swift @@ -0,0 +1,54 @@ +// +// TabContainerViewTests.swift +// edX +// +// Created by Akiva Leffert on 4/5/16. +// Copyright © 2016 edX. All rights reserved. +// + +import Foundation +import XCTest + +@testable import edX + +class TabContainerViewTests : XCTestCase { + + var testItems : (TabContainerView.Item, TabContainerView.Item) = ( + TabContainerView.Item(name: "Name", view: UIView(), identifier: "item1"), + TabContainerView.Item(name: "Name", view: UIView(), identifier: "item2") + ) + + func testSettingItemsChoosesFirstItem() { + let tabView = TabContainerView() + + let (firstItem, secondItem) = testItems + tabView.items = [firstItem, secondItem] + + XCTAssertTrue(tabView.t_isShowingViewForItem(firstItem)) + } + + func testSettingEmptyItemsClears() { + let tabView = TabContainerView() + + let (firstItem, secondItem) = testItems + tabView.items = [firstItem, secondItem] + XCTAssertTrue(tabView.t_isShowingViewForItem(firstItem)) + + tabView.items = [] + XCTAssertTrue(!tabView.t_isShowingViewForItem(firstItem)) + } + + func testSwitchingTabs() { + let tabView = TabContainerView() + + let (firstItem, secondItem) = testItems + tabView.items = [firstItem, secondItem] + XCTAssertTrue(tabView.t_isShowingViewForItem(firstItem)) + + tabView.showTabWithIdentifier(secondItem.identifier) + XCTAssertTrue(tabView.t_isShowingViewForItem(secondItem)) + + tabView.showTabWithIdentifier(firstItem.identifier) + XCTAssertTrue(tabView.t_isShowingViewForItem(firstItem)) + } +} \ No newline at end of file diff --git a/edX.xcodeproj/project.pbxproj b/edX.xcodeproj/project.pbxproj index 2b8b1defb3..9cfe1b5bf0 100644 --- a/edX.xcodeproj/project.pbxproj +++ b/edX.xcodeproj/project.pbxproj @@ -376,6 +376,9 @@ 77ADF4B31AC1FACC00AC8955 /* OEXTextStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 77ADF4B21AC1FACC00AC8955 /* OEXTextStyle.m */; }; 77AE2F621ABB71840027E799 /* OEXFileUtilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 77AE2F611ABB71840027E799 /* OEXFileUtilityTests.m */; }; 77AF076F1CADD20F00A42704 /* BadgesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77AF076E1CADD20F00A42704 /* BadgesModel.swift */; }; + 77AF07731CADD44A00A42704 /* BadgesAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77AF07711CADD37800A42704 /* BadgesAPITests.swift */; }; + 77AF07751CB311B200A42704 /* UserProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77AF07741CB311B200A42704 /* UserProfileView.swift */; }; + 77AF07771CB42AC400A42704 /* TabContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77AF07761CB42AC400A42704 /* TabContainerView.swift */; }; 77AFD10B1C1A04C3001985FD /* UserAgentOverrideOverrideOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77AFD10A1C1A04C3001985FD /* UserAgentOverrideOverrideOperation.swift */; }; 77AFD10E1C1A75A5001985FD /* UserAgentGenerationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77AFD10C1C1A63C3001985FD /* UserAgentGenerationOperation.swift */; }; 77AFD1111C1B4F79001985FD /* NSString+TestExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = 77AFD1101C1B4F79001985FD /* NSString+TestExamples.m */; }; @@ -391,6 +394,7 @@ 77BECB0A1B0A8AD800894276 /* UIEdgeInsets+GeometryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77BECB091B0A8AD800894276 /* UIEdgeInsets+GeometryTests.swift */; }; 77BECB0D1B0A8BBD00894276 /* UIEdgeInsets+Geometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77BECB051B0A668000894276 /* UIEdgeInsets+Geometry.swift */; }; 77BFD86A1BB9E15B001D7BE5 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77BFD8691BB9E15B001D7BE5 /* Strings.swift */; }; + 77C6BBC81CB43A6D0026C37B /* TabContainerViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C6BBC61CB438170026C37B /* TabContainerViewTests.swift */; }; 77C901A71BBDDD630016E468 /* TableCellStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C901A61BBDDD630016E468 /* TableCellStyle.swift */; }; 77CBF55A1BFBE89300B4F121 /* CourseAnnouncementsViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77CBF5591BFBE89300B4F121 /* CourseAnnouncementsViewControllerTests.swift */; }; 77D470571C11EB4D00C6F0C9 /* ChoiceLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77D470561C11EB4D00C6F0C9 /* ChoiceLabel.swift */; }; @@ -1137,6 +1141,9 @@ 77ADF4B21AC1FACC00AC8955 /* OEXTextStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OEXTextStyle.m; sourceTree = ""; }; 77AE2F611ABB71840027E799 /* OEXFileUtilityTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OEXFileUtilityTests.m; sourceTree = ""; }; 77AF076E1CADD20F00A42704 /* BadgesModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BadgesModel.swift; path = Source/Core/Code/BadgesModel.swift; sourceTree = SOURCE_ROOT; }; + 77AF07711CADD37800A42704 /* BadgesAPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BadgesAPITests.swift; sourceTree = SOURCE_ROOT; }; + 77AF07741CB311B200A42704 /* UserProfileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserProfileView.swift; sourceTree = ""; }; + 77AF07761CB42AC400A42704 /* TabContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabContainerView.swift; sourceTree = ""; }; 77AFD10A1C1A04C3001985FD /* UserAgentOverrideOverrideOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserAgentOverrideOverrideOperation.swift; sourceTree = ""; }; 77AFD10C1C1A63C3001985FD /* UserAgentGenerationOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserAgentGenerationOperation.swift; sourceTree = ""; }; 77AFD10F1C1B4F79001985FD /* NSString+TestExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+TestExamples.h"; sourceTree = ""; }; @@ -1156,6 +1163,7 @@ 77BECB071B0A771700894276 /* OfflineModeViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OfflineModeViewTests.swift; sourceTree = ""; }; 77BECB091B0A8AD800894276 /* UIEdgeInsets+GeometryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+GeometryTests.swift"; sourceTree = ""; }; 77BFD8691BB9E15B001D7BE5 /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = BUILT_PRODUCTS_DIR; }; + 77C6BBC61CB438170026C37B /* TabContainerViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabContainerViewTests.swift; sourceTree = ""; }; 77C901A61BBDDD630016E468 /* TableCellStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableCellStyle.swift; sourceTree = ""; }; 77CBF5591BFBE89300B4F121 /* CourseAnnouncementsViewControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseAnnouncementsViewControllerTests.swift; sourceTree = ""; }; 77D470561C11EB4D00C6F0C9 /* ChoiceLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChoiceLabel.swift; sourceTree = ""; }; @@ -1820,6 +1828,7 @@ 1A5501A11BBA323700D5F9DD /* ProfileBanner.swift */, 1A6E86231BB9C6500039A216 /* OEXStyles+Profile.swift */, 1ACD163B1BB1BE63006ACC82 /* UserProfileViewController.swift */, + 77AF07741CB311B200A42704 /* UserProfileView.swift */, 1A55019F1BBA316900D5F9DD /* UserProfileEditViewController.swift */, 1AFEB1B51BBD5B95004C471D /* ProfilePictureTaker.swift */, 1A3AFFE41BD56370002846F3 /* CropViewController.swift */, @@ -2292,6 +2301,15 @@ name = API; sourceTree = ""; }; + 77AF07781CB42ACB00A42704 /* Helper Views */ = { + isa = PBXGroup; + children = ( + 199B9B761935EA5200081A09 /* ErrorView */, + 77AF07761CB42AC400A42704 /* TabContainerView.swift */, + ); + name = "Helper Views"; + sourceTree = ""; + }; 77AFD11E1C1B77D3001985FD /* Lists */ = { isa = PBXGroup; children = ( @@ -2950,9 +2968,9 @@ 198826E41952FE1A005D4D8A /* Open In Browser */, 1913DD3A194B265600573977 /* Download View */, 193B504219459BE60038E11C /* Custom Styles */, + 77AF07781CB42ACB00A42704 /* Helper Views */, 191A001D194055DD004F7902 /* Model */, 8FE04B4A1A1E24C7007F88B8 /* SocialLogin */, - 199B9B761935EA5200081A09 /* ErrorView */, E0AF4E8C1BFB19A00083753C /* PassthroughView */, 199B9B6419349DE800081A09 /* Videos */, BE0D454E192DE63900D720D6 /* Data */, @@ -2988,6 +3006,7 @@ BECB7B331924C0C3009C77F1 /* Unit Tests */ = { isa = PBXGroup; children = ( + 77C6BBC61CB438170026C37B /* TabContainerViewTests.swift */, 775391491C98A15100FA959C /* VideoBlockViewControllerTests.swift */, 775391401C97593B00FA959C /* NetworkManager+InterceptionTests.swift */, 77ADB7921C92228D006A66A1 /* XCTestCase+Stream.swift */, @@ -3885,6 +3904,7 @@ 772619B71ADDA8ED005BD7E4 /* OEXPushSettingsManager.m in Sources */, 77ADF49E1AC1ACAA00AC8955 /* OEXExternalRegistrationOptionsView.m in Sources */, B4B6D63F1A952D1C000F44E8 /* OEXRegistrationAgreementView.m in Sources */, + 77AF07771CB42AC400A42704 /* TabContainerView.swift in Sources */, 199B9B751935E35D00081A09 /* OEXHelperVideoDownload.m in Sources */, BECB7B1F1924C0C3009C77F1 /* OEXAppDelegate.m in Sources */, B4B6D6151A949F0F000F44E8 /* OEXRegistrationFormTextField.m in Sources */, @@ -4000,6 +4020,7 @@ 9E0CC1031BA9574E00A1CFDB /* SpinnerButton.swift in Sources */, 191A002419405B91004F7902 /* OEXUserDetails.m in Sources */, 77BECB0D1B0A8BBD00894276 /* UIEdgeInsets+Geometry.swift in Sources */, + 77AF07751CB311B200A42704 /* UserProfileView.swift in Sources */, 77236EEB1C21C32E006AC1A4 /* TablePaginationController.swift in Sources */, 1AFEB1B31BBD52C7004C471D /* JSONFormBuilderTextEditor.swift in Sources */, B4B6D6391A95042B000F44E8 /* OEXCheckBoxView.m in Sources */, @@ -4152,6 +4173,7 @@ 775391411C97593B00FA959C /* NetworkManager+InterceptionTests.swift in Sources */, 774B9C121B0B9D18000B1069 /* MockReachability.swift in Sources */, 773181401B6C23F9000CC050 /* DiscussionTopic+DataFactory.swift in Sources */, + 77C6BBC81CB43A6D0026C37B /* TabContainerViewTests.swift in Sources */, 770A27A11A6F172300DFC6FF /* OEXVideoSummaryTests.m in Sources */, 77E648A51C92062900B6740D /* MockResponseCache.swift in Sources */, 77567B791ACA00F900877A7B /* NSNotificationCenter+OEXSafeAccessTests.m in Sources */,