on master help sign in
14 files changed+367−37
since splitsnapshots
unifiedsplit
Melk.xcodeproj/project.pbxproj+14−2 Melk.xcodeproj/xcuserdata/ilya.xcuserdatad/xcschemes/Melk.xcscheme+3−3 Melk/CardContainer.swift+100 Melk/CardView.swift+54−12 Melk/CollectionView.swift+3−1 Melk/MainCoordinator.swift+8−2 Melk/MainViewController.swift+22−2 Melk/SecondaryCoordinator.swift+12 Melk/SecondaryViewController.swift+111−3 Melk/StackView.swift+3−1 Melk/TableViewCells.swift+12−3 Melk/VKData.swift+7−7 Melk/VKServer.swift+1−1 README.md+17
Melk.xcodeproj/project.pbxproj+14−2
@@ -10,6 +10,7 @@
1010 0919AEA71F0D3D9D00894FFA /* SecondaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919AEA61F0D3D9D00894FFA /* SecondaryViewController.swift */; };
1111 0919AEA91F0D459600894FFA /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919AEA81F0D459600894FFA /* CardView.swift */; };
1212 0919AEAD1F0D706700894FFA /* StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0919AEAC1F0D706700894FFA /* StackView.swift */; };
13+ 09231E351F2394ED007C836D /* CardContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09231E341F2394ED007C836D /* CardContainer.swift */; };
1314 09939A561F097DA90014719D /* TableViewCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09939A551F097DA90014719D /* TableViewCells.swift */; };
1415 09A456361F0ED9A800BAC32D /* CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A456351F0ED9A800BAC32D /* CollectionView.swift */; };
1516 09AFAB041F159C9200EBD200 /* StreamingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09AFAB031F159C9200EBD200 /* StreamingService.swift */; };
@@ -69,6 +70,7 @@
6970 0919AEA61F0D3D9D00894FFA /* SecondaryViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondaryViewController.swift; sourceTree = "<group>"; };
7071 0919AEA81F0D459600894FFA /* CardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardView.swift; sourceTree = "<group>"; };
7172 0919AEAC1F0D706700894FFA /* StackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackView.swift; sourceTree = "<group>"; };
73+ 09231E341F2394ED007C836D /* CardContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardContainer.swift; sourceTree = "<group>"; };
7274 09939A551F097DA90014719D /* TableViewCells.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewCells.swift; sourceTree = "<group>"; };
7375 09A456351F0ED9A800BAC32D /* CollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionView.swift; sourceTree = "<group>"; };
7476 09AFAAFF1F159AE300EBD200 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/iOS/SwiftyJSON.framework; sourceTree = SOURCE_ROOT; };
@@ -117,11 +119,21 @@
117119/* End PBXFrameworksBuildPhase section */
118120
119121/* Begin PBXGroup section */
122+ 09231E331F2394C5007C836D /* Card */ = {
123+ isa = PBXGroup;
124+ children = (
125+ 0919AEA81F0D459600894FFA /* CardView.swift */,
126+ 09231E341F2394ED007C836D /* CardContainer.swift */,
127+ );
128+ name = Card;
129+ sourceTree = "<group>";
130+ };
120131 098397F71F1E887F008C88DD /* Main */ = {
121132 isa = PBXGroup;
122133 children = (
123134 09BF420F1F0833C100AFAD57 /* MainCoordinator.swift */,
124135 09BF42551F08413000AFAD57 /* MainViewController.swift */,
136+ 09939A551F097DA90014719D /* TableViewCells.swift */,
125137 );
126138 name = Main;
127139 sourceTree = "<group>";
@@ -176,12 +188,11 @@
176188 098397F71F1E887F008C88DD /* Main */,
177189 098397F81F1E888F008C88DD /* Secondary */,
178190 098397F91F1E88A3008C88DD /* Streaming */,
191+ 09231E331F2394C5007C836D /* Card */,
179192 09BF41EE1F07F71F00AFAD57 /* AppDelegate.swift */,
180193 09BF42111F08341B00AFAD57 /* MyWeirdness.swift */,
181194 09BF42121F08341B00AFAD57 /* PublicCrap.swift */,
182195 09F03F351F1674ED000CE916 /* VKServer.swift */,
183− 0919AEA81F0D459600894FFA /* CardView.swift */,
184− 09939A551F097DA90014719D /* TableViewCells.swift */,
185196 09BF420D1F0833A700AFAD57 /* LocalVKDataTypes.swift */,
186197 09BF42151F0834D400AFAD57 /* VKPublicCrap.swift */,
187198 09BF42171F08352E00AFAD57 /* VKData.swift */,
@@ -294,6 +305,7 @@
294305 09BF42181F08352E00AFAD57 /* VKData.swift in Sources */,
295306 09BF42101F0833C100AFAD57 /* MainCoordinator.swift in Sources */,
296307 09A456361F0ED9A800BAC32D /* CollectionView.swift in Sources */,
308+ 09231E351F2394ED007C836D /* CardContainer.swift in Sources */,
297309 09BF42131F08341B00AFAD57 /* MyWeirdness.swift in Sources */,
298310 0919AEA91F0D459600894FFA /* CardView.swift in Sources */,
299311 09BF42321F08388100AFAD57 /* SecondaryCoordinator.swift in Sources */,
Melk.xcodeproj/xcuserdata/ilya.xcuserdatad/xcschemes/Melk.xcscheme+3−3
@@ -42,9 +42,9 @@
4242 </AdditionalOptions>
4343 </TestAction>
4444 <LaunchAction
45− buildConfiguration = "Debug"
46− selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
47− selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
45+ buildConfiguration = "Release"
46+ selectedDebuggerIdentifier = ""
47+ selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
4848 launchStyle = "0"
4949 useCustomWorkingDirectory = "NO"
5050 ignoresPersistentStateOnLaunch = "NO"
Melk/CardContainer.swiftnew file+100
@@ -0,0 +1,100 @@
1+//
2+// CardContainer.swift
3+// Melk
4+//
5+// Created by Ilya Kos on 7/22/17.
6+// Copyright © 2017 Ilya Kos. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class CardContainer: UIView {
12+
13+ func animate() {
14+ blurView.effect = blur
15+ card.transform = smallTransform
16+ card.layer.opacity = 0
17+ shadowView.layer.opacity = 0
18+ shadowView.transform = smallTransform
19+ shadowView.layer.shadowRadius = 0
20+ UIView.animate(withDuration: 0.5, delay: 0, options: [.curveEaseInOut], animations: {
21+ self.card.transform = self.normalTransform
22+ self.shadowView.transform = self.normalTransform
23+ self.shadowView.layer.shadowRadius = self.shadowRadius
24+ self.shadowView.layer.opacity = 1
25+ self.blurView.effect = nil
26+ self.card.layer.opacity = 1
27+ }, completion: nil)
28+ }
29+
30+ // MARK: Passthrough to CardView
31+ var post: WallPost? {
32+ get {
33+ return card.post
34+ }
35+ set {
36+ card.post = newValue
37+ }
38+ }
39+
40+ var minWidth: CGFloat {
41+ get {
42+ return card.minWidth
43+ }
44+ set {
45+ card.minWidth = newValue
46+ }
47+ }
48+
49+
50+ var height: CGFloat {
51+ get {
52+ return card.height
53+ }
54+ set {
55+ card.height = newValue
56+ }
57+ }
58+
59+ private let card: CardView
60+ private let blurView = UIVisualEffectView()
61+ private let shadowView = UIView()
62+ private let blur = UIBlurEffect(style: UIBlurEffectStyle.light)
63+ private let scale: CGFloat = 0.5
64+ private let smallTransform: CGAffineTransform
65+ private let normalTransform = CGAffineTransform(scaleX: 1, y: 1)
66+ private let shadowRadius: CGFloat = 12
67+ private let shadowOpacity: Float = 0.15
68+ private let shadowOffset: CGFloat = 16
69+
70+ override init(frame: CGRect) {
71+ card = CardView(frame: frame)
72+ smallTransform = CGAffineTransform(scaleX: scale, y: scale)
73+ super.init(frame: frame)
74+ shadowView.layer.shadowColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1).cgColor
75+ shadowView.layer.shadowRadius = shadowRadius
76+ shadowView.layer.shadowOpacity = shadowOpacity
77+ shadowView.backgroundColor = UIColor.clear
78+ shadowView.clipsToBounds = false
79+ shadowView.layer.shadowOffset = CGSize.zero
80+ addSubview(shadowView)
81+ addSubview(card)
82+ addSubview(blurView)
83+ }
84+
85+ required init?(coder aDecoder: NSCoder) {
86+ fatalError("init(coder:) has not been implemented")
87+ }
88+
89+ override func layoutSubviews() {
90+ super.layoutSubviews()
91+ card.layoutIfNeeded()
92+ self.frame = card.frame
93+ shadowView.layer.shadowPath = CGPath(roundedRect: card.frame, cornerWidth: card.layer.cornerRadius, cornerHeight: card.layer.cornerRadius,
94+ transform: nil)
95+ shadowView.layer.shadowOffset.height = shadowOffset
96+ shadowView.frame = card.frame
97+ blurView.frame = CGRect(x: -shadowRadius, y: 0, width: card.frame.width + shadowRadius*2, height: card.frame.height + shadowRadius + shadowOffset)
98+ }
99+
100+}
Melk/CardView.swift+54−12
@@ -16,14 +16,14 @@ class CardView: UIView {
1616 update()
1717 }
1818 }
19− var width: CGFloat = 300 {
19+ var minWidth: CGFloat = 300 {
2020 didSet {
21− update()
21+// update()
2222 }
2323 }
2424 var height: CGFloat = 400 {
2525 didSet {
26− update()
26+// update()
2727 }
2828 }
2929
@@ -31,17 +31,32 @@ class CardView: UIView {
3131 super.init(frame: frame)
3232 layer.masksToBounds = true
3333 layer.cornerRadius = 20
34− backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
34+ gradient.colors = [#colorLiteral(red: 0.9841352105, green: 0.9841352105, blue: 0.9841352105, alpha: 1).cgColor, #colorLiteral(red: 0.9456828237, green: 0.9456828237, blue: 0.9456828237, alpha: 1).cgColor]
35+ gradient.startPoint = CGPoint(x: 0.5, y: 0)
36+ gradient.endPoint = CGPoint(x: 0.5, y: 1)
37+ layer.addSublayer(gradient)
38+// backgroundColor = #colorLiteral(red: 0.9638966278, green: 0.9734401588, blue: 0.9734401588, alpha: 1)
3539 }
3640
3741 required init?(coder aDecoder: NSCoder) {
3842 fatalError("init(coder:) has not been implemented")
3943 }
44+
45+ private let gradient = CAGradientLayer()
46+
4047
4148 private let padding = CGPoint(x: 32, y: 32)
4249 private let vPadding1: CGFloat = 16
4350 private let vPadding2: CGFloat = 16
44− private let minTextHeight: CGFloat = 50
51+ private var minTextHeight: CGFloat {
52+ return CGFloat(height * 0.2)
53+ }
54+ private let gradientRatio: CGFloat = 0.2
55+ private let minGradientAmount: CGFloat = 70
56+ private let hiddenByGradient: CGFloat = 10
57+ private var maxAttachmentDim: CGFloat {
58+ return minWidth / 2.3
59+ }
4560
4661// private let avatar = UIImage()
4762// private let name = UIImage()
@@ -54,10 +69,9 @@ class CardView: UIView {
5469 addSubview(largeStack)
5570
5671 let userView = StackView(axis: .horizontal, alignment: .center)
57−// userView.backgroundColor = #colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1)
5872
5973
60− let avatarDim: CGFloat = 50
74+ let avatarDim: CGFloat = 64
6175 let avatar = UIImageView()
6276 post?.owner?.avatar?.load(into: avatar)
6377 avatar.layer.cornerRadius = avatarDim/2
@@ -74,6 +88,7 @@ class CardView: UIView {
7488 let lastName = post?.owner?.lastName ?? ""
7589 userName.text = "\(firstName) \(lastName)"
7690 userName.font = UIFont.systemFont(ofSize: 28, weight: UIFontWeightHeavy)
91+ userName.textColor = UIColor.darkText
7792 userName.sizeToFit()
7893 userView.addArrangedSubview(.view(userName))
7994
@@ -84,12 +99,16 @@ class CardView: UIView {
8499 largeStack.addArrangedSubview(.spacer(vPadding1))
85100
86101
102+ let width = max(minWidth - 2 * padding.x, userView.frame.width)
103+
104+
87105 let photoView = CollectionView()
88106 photoView.alignment = .top
89107 photoView.horizontalPadding = 8
90108 photoView.verticalPadding = 8
91109 photoView.padding = CGPoint.zero
92− var maxPhotoDim: CGFloat = 150
110+ photoView.maxWidth = width
111+ var maxPhotoDim: CGFloat = maxAttachmentDim
93112 if let attachments = post?.attachments {
94113 for attachment in attachments {
95114 if let v = attachment.value {
@@ -98,6 +117,8 @@ class CardView: UIView {
98117 if var pWidth = photo.width.value >>> {CGFloat($0)},
99118 var pHeight = photo.height.value >>> {CGFloat($0)} {
100119 let photoV = UIImageView()
120+ photoV.layer.minificationFilter = kCAFilterTrilinear
121+ photoV.contentMode = .scaleAspectFill
101122 photo.image?.load(into: photoV)
102123 var k: CGFloat!
103124 if pWidth > pHeight {
@@ -120,7 +141,7 @@ class CardView: UIView {
120141 photoView.layoutIfNeeded()
121142
122143 var textHeight = height
123− textHeight -= padding.y + vPadding1 + userView.frame.height
144+ textHeight -= padding.y * 2 + vPadding1 + userView.frame.height
124145 if let count = post?.attachments.count{
125146 if count > 0 {
126147 textHeight -= vPadding2 + photoView.frame.height
@@ -132,12 +153,28 @@ class CardView: UIView {
132153
133154 let body = UILabel()
134155 body.numberOfLines = 0
135− let bodyWidth: Double = Double(width - padding.x * 2)
156+ let bodyWidth: Double = Double(width)
136157 body.font = UIFont.systemFont(ofSize: 22, weight: UIFontWeightSemibold)
137158 body.text = post?.body
159+ body.textColor = UIColor.darkText
138160 let size = body.textRect(forBounds: CGRect(x: 0, y: 0, width: bodyWidth, height: Double.infinity), limitedToNumberOfLines: 0).size
139− body.frame.size.height = min(size.height, textHeight)
140161 body.frame.size.width = size.width
162+ if size.height > textHeight {
163+ body.frame.size.height = textHeight
164+ let maskView = UIView()
165+ let maskLayer = CAGradientLayer()
166+ maskLayer.colors = [UIColor.black.cgColor, UIColor.clear.cgColor]
167+ maskLayer.endPoint = CGPoint(x: 0.5, y: 1 - (hiddenByGradient/textHeight))
168+ let minPoint = minGradientAmount / textHeight
169+ maskLayer.startPoint = CGPoint(x: 0.5, y: 1 - max(minPoint, gradientRatio))
170+ print(maskLayer.startPoint)
171+ maskLayer.frame = body.bounds
172+ maskView.frame = body.bounds
173+ maskView.layer.addSublayer(maskLayer)
174+ body.mask = maskView
175+ } else {
176+ body.frame.size.height = size.height
177+ }
141178
142179 largeStack.addArrangedSubview(.view(body))
143180
@@ -148,7 +185,12 @@ class CardView: UIView {
148185 }
149186 }
150187
151− frame.size = CGSize(width: width, height: height)
188+ largeStack.layoutIfNeeded()
189+
190+ frame.size = CGSize(width: width + padding.x * 2, height: min(height, largeStack.frame.height))
191+
192+ gradient.frame = self.bounds
193+ gradient.endPoint = CGPoint(x: 0.5, y: bounds.height / height)
152194
153195 }
154196}
Melk/CollectionView.swift+3−1
@@ -94,7 +94,9 @@ class CollectionView: UIView {
9494 }
9595 }
9696 lastStack.layoutIfNeeded()
97− largeStack.addArrangedSubview(.spacer(verticalPadding))
97+ if !firstStack {
98+ largeStack.addArrangedSubview(.spacer(verticalPadding))
99+ }
98100 largeStack.addArrangedSubview(.view(lastStack))
99101 largeStack.frame.origin = CGPoint.zero
100102 largeStack.layoutIfNeeded()
Melk/MainCoordinator.swift+8−2
@@ -27,6 +27,14 @@ class MainCoordinator {
2727 deleteRealmIfMigrationNeeded: true)
2828
2929 navC = UINavigationController(rootViewController: mainVC)
30+
31+ mainVC.present = { tags in
32+ if tags.count > 0 {
33+ self.secondaryCoord.present(tag: tags.first!)
34+ self.stream.rules = tags
35+ self.stream.connect()
36+ }
37+ }
3038 updateScreens()
3139 stream.handleResult = { result in
3240 self.secondaryCoord.add(result: result)
@@ -44,8 +52,6 @@ class MainCoordinator {
4452// }
4553 print("didAuthorize")
4654 window.rootViewController = navC
47− stream.rules = ["я", "а"]
48− stream.connect()
4955 }
5056
5157 func updateScreens() {
Melk/MainViewController.swift+22−2
@@ -29,7 +29,9 @@ class MainViewController: UIViewController {
2929 }
3030
3131 @objc private func presentTags() {
32−
32+ present?(
33+ mainView.tags.flatMap({$0}).filter({$0 != ""})
34+ )
3335 }
3436}
3537
@@ -45,6 +47,8 @@ class MainView: UIView {
4547 tableView.rowHeight = UITableViewAutomaticDimension
4648 tableView.register(TitleTableViewCell.self, forCellReuseIdentifier: TitleTableViewCell.identifier)
4749 tableView.register(TagTableViewCell.self, forCellReuseIdentifier: TagTableViewCell.identifier)
50+ tableView.separatorStyle = .none
51+ tableView.allowsSelection = false
4852 setNeedsLayout()
4953 }
5054
@@ -52,6 +56,17 @@ class MainView: UIView {
5256 tableView.frame = bounds
5357 }
5458
59+ fileprivate var tags: [String?] = [nil] {
60+ didSet {
61+ if tags.last! != nil && tags.last! != "" {
62+ if tags.count < 10 {
63+ tags.append(nil)
64+ tableView.insertRows(at: [IndexPath.init(row: tags.count - 1, section: 0)], with: .automatic)
65+ }
66+ }
67+ }
68+ }
69+
5570 required init?(coder aDecoder: NSCoder) {
5671 fatalError("init(coder:) has not been implemented")
5772 }
@@ -62,13 +77,18 @@ extension MainView: UITableViewDataSource {
6277 if let cell = tableView.dequeueReusableCell(withIdentifier: TagTableViewCell.identifier) as? TagTableViewCell {
6378// cell.titleText = "Testing testing 1 2 3"
6479 cell.style = indexPath.item == 0 ? .large : .normal
80+ let index = indexPath.row
81+ cell.tagDidChange = { tag in
82+ self.tags[index] = tag
83+ }
84+ cell.tagString = tags[index]
6585 return cell
6686 }
6787 return UITableViewCell()
6888 }
6989
7090 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
71− return 3
91+ return tags.count
7292 }
7393
7494 func numberOfSections(in tableView: UITableView) -> Int {
Melk/SecondaryCoordinator.swift+12
@@ -12,12 +12,21 @@ class SecondaryCoordinator {
1212
1313 var windows: [UIWindow] = []
1414
15+ private var didStartPresenting = false
16+ private var tag: String = ""
17+
1518 func add(result: StreamingServiceResult) {
1619 for vc in VCs {
1720 vc.add(result: result)
1821 }
1922 }
2023
24+ func present(tag newTag: String) {
25+ didStartPresenting = true
26+ self.tag = newTag
27+ VCs.map({ $0.present(tag: newTag) })
28+ }
29+
2130 init() {
2231
2332 }
@@ -36,6 +45,9 @@ class SecondaryCoordinator {
3645 VCs.append(VC)
3746 window.rootViewController = VC
3847 windows.append(window)
48+ if didStartPresenting {
49+ VC.present(tag: tag)
50+ }
3951 }
4052 }
4153}
Melk/SecondaryViewController.swift+111−3
@@ -19,6 +19,10 @@ class SecondaryViewController: UIViewController {
1919 sView.add(result: result)
2020 }
2121
22+ func present(tag: String) {
23+ sView.present(tag: tag)
24+ }
25+
2226 required init?(coder aDecoder: NSCoder) {
2327 fatalError("init(coder:) has not been implemented")
2428 }
@@ -27,19 +31,123 @@ class SecondaryViewController: UIViewController {
2731}
2832
2933class SecondaryView: UIView {
34+
35+ var mainTag: String? {
36+ get {
37+ return tagLabel.text
38+ }
39+ set {
40+ tagLabel.text = newValue
41+
42+ }
43+ }
44+
45+
46+ private let spacer: CGFloat = 32
47+ private let bottomSpacer: CGFloat = 64
48+ private let tagSpacer: CGFloat = 32
49+ private let tagLabel = UILabel()
50+ private let melkLabel = UILabel()
51+
52+ func present(tag: String) {
53+ UIView.perform(.delete, on: [melkLabel], options: [], animations: nil) { (_) in
54+ self.melkLabel.removeFromSuperview()
55+ self.mainTag = tag
56+ self.tagLabel.sizeToFit()
57+ self.layoutIfNeeded(animated: true)
58+ }
59+ }
60+
3061 func add(result: StreamingServiceResult) {
3162 switch result {
3263 case .post(let post):
64+ let card = CardContainer()
65+ card.minWidth = min(bounds.width / 2.3, 550)
66+ card.height = cardHeight
3367 card.post = post
68+ cards.addArrangedSubview(.spacer(spacer))
69+ cards.addArrangedSubview(.view(card))
70+ card.animate()
3471 }
72+ self.layoutIfNeeded(animated: true)
73+// self.setNeedsLayout(animated: true) // Doesn't work for some reason
3574 }
3675
37− let card = CardView()
76+// private let card = CardView()
77+ private let cards = StackView(axis: .horizontal, alignment: .top)
3878
3979 override init(frame: CGRect) {
4080 super.init(frame: frame)
41− card.frame.origin = CGPoint(x: 20, y: 20)
42− addSubview(card)
81+ backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
82+ addSubview(cards)
83+
84+ melkLabel.text = "Melk"
85+ melkLabel.font = UIFont.systemFont(ofSize: 200, weight: UIFontWeightHeavy)
86+ melkLabel.textColor = #colorLiteral(red: 0.9601849914, green: 0.9601849914, blue: 0.9601849914, alpha: 1)
87+ addSubview(melkLabel)
88+
89+ tagLabel.font = UIFont.systemFont(ofSize: 69, weight: UIFontWeightBold)
90+ tagLabel.textColor = #colorLiteral(red: 0.3176470588, green: 0.3179988265, blue: 0.3179988265, alpha: 1)
91+ addSubview(tagLabel)
92+ }
93+
94+ func setNeedsLayout(animated: Bool) {
95+ self.animate = animated
96+ self.setNeedsLayout()
97+ }
98+ func layoutIfNeeded(animated: Bool) {
99+ self.animate = animated
100+ self.layoutIfNeeded()
101+ }
102+ private var animate: Bool = false
103+
104+ private func cleanCards() {
105+ var offset = 0
106+ outLoop: for (i, element) in cards.arrangedSubviews.enumerated() {
107+ switch element {
108+ case .view(let card):
109+ let cardFrame = convert(card.frame, from: cards)
110+ if cardFrame.intersects(bounds) {
111+ break outLoop
112+ } else {
113+ cards.arrangedSubviews.remove(at: i-1-offset)
114+ cards.arrangedSubviews.remove(at: i-1-offset)
115+ offset += 2
116+ }
117+ case .spacer(_):
118+ break
119+ }
120+ }
121+ }
122+
123+ private let padding = CGPoint(x: 32, y: 16)
124+ private var cardHeight: CGFloat = 400
125+
126+ override func layoutSubviews() {
127+ super.layoutSubviews()
128+
129+ melkLabel.sizeToFit()
130+ melkLabel.center = center
131+
132+ let updates = {
133+ self.tagLabel.sizeToFit()
134+ self.tagLabel.center.x = self.center.x
135+ self.tagLabel.frame.origin.y = self.tagSpacer
136+ self.cards.frame.origin.y = self.tagLabel.frame.maxY + self.padding.y
137+ self.cards.layoutIfNeeded()
138+ self.cards.frame.origin.x = self.frame.width - self.padding.x - self.cards.frame.width
139+ }
140+ if animate {
141+ UIView.animate(withDuration: 0.7, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.6, options: [], animations: updates, completion: { _ in
142+ self.cleanCards()
143+ })
144+// UIView.animate(withDuration: 0.4, delay: 0.0, options: [.curveEaseOut], animations: updates, completion: nil)
145+ } else {
146+ updates()
147+ self.cleanCards()
148+ }
149+ cardHeight = bounds.height - tagLabel.frame.height - padding.y - tagSpacer - bottomSpacer
150+ animate = false
43151 }
44152
45153 required init?(coder aDecoder: NSCoder) {
Melk/StackView.swift+3−1
@@ -44,6 +44,8 @@ class StackView: UIView {
4444 super.layoutSubviews()
4545 print("laying out subviews")
4646
47+ subviews.map({$0.removeFromSuperview()}) // TODO: There must be a better way!
48+
4749 var height: CGFloat = 0
4850 var width: CGFloat = 0
4951 switch axis {
@@ -62,7 +64,7 @@ class StackView: UIView {
6264 height += a
6365 }
6466 case .view(let view):
65−
67+ view.layoutIfNeeded()
6668 switch axis {
6769 case .horizontal:
6870 view.frame.origin.x = width
Melk/TableViewCells.swift+12−3
@@ -57,6 +57,12 @@ class TagTableViewCell: UITableViewCell {
5757 }
5858 }
5959
60+ var tagDidChange: ((String?) -> ())?
61+
62+ @objc private func textDidChange() {
63+ tagDidChange?(tagString)
64+ }
65+
6066 var style: TagStyle = .normal {
6167 didSet {
6268 updateStyle()
@@ -70,6 +76,9 @@ class TagTableViewCell: UITableViewCell {
7076
7177 textField.borderStyle = .roundedRect
7278 textField.adjustsFontSizeToFitWidth = false
79+ textField.autocapitalizationType = .none
80+ textField.autocorrectionType = .no
81+ textField.addTarget(self, action: #selector(self.textDidChange), for: .editingChanged)
7382
7483 contentView.addSubview(textField)
7584// textField.delegate = self
@@ -92,7 +101,7 @@ class TagTableViewCell: UITableViewCell {
92101 CGRect.infinite, limitedToNumberOfLines: 1)
93102 .height + 2*2
94103
95− let frame = CGRect(x: 16, y: 8, width: bounds.width - 16*2, height: height)
104+ let frame = CGRect(x: 16, y: style == .large ? 32 : 4, width: bounds.width - 16*2, height: height)
96105 textField.frame = frame
97106 print(textField.frame)
98107 }
@@ -103,8 +112,8 @@ class TagTableViewCell: UITableViewCell {
103112
104113
105114 static let identifier = "io.Melk.tagTableViewCell"
106− static let largeHeight: CGFloat = 37.5 + 8*2
107− static let normalHeight: CGFloat = 28.0 + 8*2
115+ static let largeHeight: CGFloat = 37.5 + 16 + 32
116+ static let normalHeight: CGFloat = 28.0 + 4*2
108117}
109118
110119//extension TagTableViewCell: UITextFieldDelegate {
Melk/VKData.swift+7−7
@@ -289,13 +289,13 @@ class VKData {
289289 photo.width.value = json["width"].int
290290 photo.height.value = json["height"].int
291291 var surl: String?
292− if let imageSurl = json["photo_2560"].string {
293− surl = imageSurl
294− } else if let imageSurl = json["photo_1280"].string {
295− surl = imageSurl
296− } else if let imageSurl = json["photo_807"].string {
297− surl = imageSurl
298− } else if let imageSurl = json["photo_604"].string {
292+// if let imageSurl = json["photo_2560"].string {
293+// surl = imageSurl
294+// } else if let imageSurl = json["photo_1280"].string {
295+// surl = imageSurl
296+// } else if let imageSurl = json["photo_807"].string {
297+// surl = imageSurl
298+ /*} else*/ if let imageSurl = json["photo_604"].string {
299299 surl = imageSurl
300300 } else if let imageSurl = json["photo_130"].string {
301301 surl = imageSurl
Melk/VKServer.swift+1−1
@@ -118,6 +118,6 @@ class VKServer {
118118}
119119
120120fileprivate func url(`for` method: String) -> URL {
121− let surl = "https://api.vk.com/method/\(method)?access_token=\(accessKey)&v=\(vkAPIVersion)"
121+ let surl = "https://api.vk.com/method/\(method)?access_token=\(accessKey)&v=\(vkAPIVersion)&lang=0"
122122 return URL.init(string: surl)!
123123}
README.mdnew file+17
@@ -0,0 +1,17 @@
1+# Melk
2+
3+_Might not work properly with new streaming API. This project was developed while the API was in beta._
4+
5+[![Melk demo](http://img.youtube.com/vi/9SNc0bLDCL0/0.jpg)](http://www.youtube.com/watch?v=9SNc0bLDCL0)
6+
7+При проведении различных мероприятий организаторы довольно часто призывают людей, посещающих эти мероприятия, выложить у себя в соц сети что-нибудь об этом мероприятии, чаще всего под мероприятие даже выделяется отдельный хештег.
8+
9+Для привлечения внимания к хештегу и поощрения его использования можно выводить некоторые из выложенных с хештегом записей туда, где все смогут это увидеть.
10+
11+При проведении больших мероприятий желательно использовать большое количество независимых систем, чтобы если летит что-то одно, все остальное продолжало работать.
12+
13+Полноценный десктопный компьютер для данной цели не нужен, поэтому приложение работает просто на телефоне или планшете.
14+
15+Самый первый хештег или ключевое слово будет крупно показано вместе со всеми записями.
16+
17+После того, как будет нажата кнопка “Present”, записи с основным хештегом будут выводиться на любые подключенные к устройству экраны.