mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 03:09:56 +00:00
no message
This commit is contained in:
parent
a4de6e5fb6
commit
124e621ee2
@ -44,7 +44,7 @@
|
||||
D05CC26E1B69316F00E235A3 /* Display.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05CC2631B69316F00E235A3 /* Display.framework */; };
|
||||
D05CC2731B69316F00E235A3 /* DisplayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2721B69316F00E235A3 /* DisplayTests.swift */; };
|
||||
D05CC2A01B69326400E235A3 /* NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC29F1B69326400E235A3 /* NavigationController.swift */; };
|
||||
D05CC2A21B69326C00E235A3 /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2A11B69326C00E235A3 /* Window.swift */; };
|
||||
D05CC2A21B69326C00E235A3 /* WindowContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2A11B69326C00E235A3 /* WindowContent.swift */; };
|
||||
D05CC2E31B69552C00E235A3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2E21B69552C00E235A3 /* ViewController.swift */; };
|
||||
D05CC2E71B69555800E235A3 /* CAAnimationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2E41B69555800E235A3 /* CAAnimationUtils.swift */; };
|
||||
D05CC2EC1B69558A00E235A3 /* RuntimeUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2EA1B69558A00E235A3 /* RuntimeUtils.m */; };
|
||||
@ -82,10 +82,12 @@
|
||||
D08E903C1D2417E000533158 /* ActionSheetButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08E903B1D2417E000533158 /* ActionSheetButtonItem.swift */; };
|
||||
D08E903E1D24187900533158 /* ActionSheetItemGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08E903D1D24187900533158 /* ActionSheetItemGroup.swift */; };
|
||||
D08E90471D243C2F00533158 /* HighlightTrackingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08E90461D243C2F00533158 /* HighlightTrackingButton.swift */; };
|
||||
D096A4501EA64F580000A7AE /* ActionSheetCheckboxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D096A44F1EA64F580000A7AE /* ActionSheetCheckboxItem.swift */; };
|
||||
D0A749951E3A9E7B00AD786E /* SwitchNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A749941E3A9E7B00AD786E /* SwitchNode.swift */; };
|
||||
D0AE2CA61C94548900F2FD3C /* GenerateImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AE2CA51C94548900F2FD3C /* GenerateImage.swift */; };
|
||||
D0AE3D4D1D25C816001CCE13 /* NavigationBarTransitionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AE3D4C1D25C816001CCE13 /* NavigationBarTransitionState.swift */; };
|
||||
D0B367201C94A53A00346D2E /* StatusBarProxyNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B3671F1C94A53A00346D2E /* StatusBarProxyNode.swift */; };
|
||||
D0BE93191E8ED71100DCC1E6 /* NativeWindowHostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BE93181E8ED71100DCC1E6 /* NativeWindowHostView.swift */; };
|
||||
D0C0D28F1C997110001D2851 /* FBAnimationPerformanceTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C0D28D1C997110001D2851 /* FBAnimationPerformanceTracker.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D0C0D2901C997110001D2851 /* FBAnimationPerformanceTracker.mm in Sources */ = {isa = PBXBuildFile; fileRef = D0C0D28E1C997110001D2851 /* FBAnimationPerformanceTracker.mm */; };
|
||||
D0C2DFC61CC4431D0044FF83 /* ASTransformLayerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBB1CC4431D0044FF83 /* ASTransformLayerNode.swift */; };
|
||||
@ -102,7 +104,6 @@
|
||||
D0C85DD01D1C082E00124894 /* ActionSheetItemGroupsContainerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C85DCF1D1C082E00124894 /* ActionSheetItemGroupsContainerNode.swift */; };
|
||||
D0C85DD21D1C08AE00124894 /* ActionSheetItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C85DD11D1C08AE00124894 /* ActionSheetItemNode.swift */; };
|
||||
D0C85DD41D1C1E6A00124894 /* ActionSheetItemGroupNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C85DD31D1C1E6A00124894 /* ActionSheetItemGroupNode.swift */; };
|
||||
D0C85DD61D1C600D00124894 /* ActionSheetButtonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C85DD51D1C600D00124894 /* ActionSheetButtonNode.swift */; };
|
||||
D0CD12161CCFEB4E000DE7BC /* ScrollToTopProxyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CD12151CCFEB4E000DE7BC /* ScrollToTopProxyView.swift */; };
|
||||
D0D94A171D3814F900740E02 /* UniversalTapRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D94A161D3814F900740E02 /* UniversalTapRecognizer.swift */; };
|
||||
D0DA444C1E4DCA4A005FDCA7 /* AlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DA444B1E4DCA4A005FDCA7 /* AlertController.swift */; };
|
||||
@ -171,7 +172,7 @@
|
||||
D05CC2721B69316F00E235A3 /* DisplayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayTests.swift; sourceTree = "<group>"; };
|
||||
D05CC2741B69316F00E235A3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
D05CC29F1B69326400E235A3 /* NavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationController.swift; sourceTree = "<group>"; };
|
||||
D05CC2A11B69326C00E235A3 /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
|
||||
D05CC2A11B69326C00E235A3 /* WindowContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WindowContent.swift; sourceTree = "<group>"; };
|
||||
D05CC2E21B69552C00E235A3 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
D05CC2E41B69555800E235A3 /* CAAnimationUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAAnimationUtils.swift; sourceTree = "<group>"; };
|
||||
D05CC2EA1B69558A00E235A3 /* RuntimeUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuntimeUtils.m; sourceTree = "<group>"; };
|
||||
@ -209,10 +210,12 @@
|
||||
D08E903B1D2417E000533158 /* ActionSheetButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetButtonItem.swift; sourceTree = "<group>"; };
|
||||
D08E903D1D24187900533158 /* ActionSheetItemGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetItemGroup.swift; sourceTree = "<group>"; };
|
||||
D08E90461D243C2F00533158 /* HighlightTrackingButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighlightTrackingButton.swift; sourceTree = "<group>"; };
|
||||
D096A44F1EA64F580000A7AE /* ActionSheetCheckboxItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetCheckboxItem.swift; sourceTree = "<group>"; };
|
||||
D0A749941E3A9E7B00AD786E /* SwitchNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwitchNode.swift; sourceTree = "<group>"; };
|
||||
D0AE2CA51C94548900F2FD3C /* GenerateImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GenerateImage.swift; sourceTree = "<group>"; };
|
||||
D0AE3D4C1D25C816001CCE13 /* NavigationBarTransitionState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBarTransitionState.swift; sourceTree = "<group>"; };
|
||||
D0B3671F1C94A53A00346D2E /* StatusBarProxyNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarProxyNode.swift; sourceTree = "<group>"; };
|
||||
D0BE93181E8ED71100DCC1E6 /* NativeWindowHostView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NativeWindowHostView.swift; sourceTree = "<group>"; };
|
||||
D0C0D28D1C997110001D2851 /* FBAnimationPerformanceTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBAnimationPerformanceTracker.h; sourceTree = "<group>"; };
|
||||
D0C0D28E1C997110001D2851 /* FBAnimationPerformanceTracker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FBAnimationPerformanceTracker.mm; sourceTree = "<group>"; };
|
||||
D0C2DFBB1CC4431D0044FF83 /* ASTransformLayerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASTransformLayerNode.swift; sourceTree = "<group>"; };
|
||||
@ -229,7 +232,6 @@
|
||||
D0C85DCF1D1C082E00124894 /* ActionSheetItemGroupsContainerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetItemGroupsContainerNode.swift; sourceTree = "<group>"; };
|
||||
D0C85DD11D1C08AE00124894 /* ActionSheetItemNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetItemNode.swift; sourceTree = "<group>"; };
|
||||
D0C85DD31D1C1E6A00124894 /* ActionSheetItemGroupNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetItemGroupNode.swift; sourceTree = "<group>"; };
|
||||
D0C85DD51D1C600D00124894 /* ActionSheetButtonNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetButtonNode.swift; sourceTree = "<group>"; };
|
||||
D0CD12151CCFEB4E000DE7BC /* ScrollToTopProxyView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollToTopProxyView.swift; sourceTree = "<group>"; };
|
||||
D0D94A161D3814F900740E02 /* UniversalTapRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniversalTapRecognizer.swift; sourceTree = "<group>"; };
|
||||
D0DA444B1E4DCA4A005FDCA7 /* AlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertController.swift; sourceTree = "<group>"; };
|
||||
@ -272,7 +274,8 @@
|
||||
D015F7501D1ADC6800E269B5 /* Window */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D05CC2A11B69326C00E235A3 /* Window.swift */,
|
||||
D05CC2A11B69326C00E235A3 /* WindowContent.swift */,
|
||||
D0BE93181E8ED71100DCC1E6 /* NativeWindowHostView.swift */,
|
||||
);
|
||||
name = Window;
|
||||
sourceTree = "<group>";
|
||||
@ -298,7 +301,7 @@
|
||||
D08E90391D24159200533158 /* ActionSheetItem.swift */,
|
||||
D08E903D1D24187900533158 /* ActionSheetItemGroup.swift */,
|
||||
D08E903B1D2417E000533158 /* ActionSheetButtonItem.swift */,
|
||||
D0C85DD51D1C600D00124894 /* ActionSheetButtonNode.swift */,
|
||||
D096A44F1EA64F580000A7AE /* ActionSheetCheckboxItem.swift */,
|
||||
);
|
||||
name = "Action Sheet";
|
||||
sourceTree = "<group>";
|
||||
@ -737,6 +740,7 @@
|
||||
D05CC2A01B69326400E235A3 /* NavigationController.swift in Sources */,
|
||||
D06EE8451B7140FF00837186 /* Font.swift in Sources */,
|
||||
D0C2DFCB1CC4431D0044FF83 /* ListViewAnimation.swift in Sources */,
|
||||
D0BE93191E8ED71100DCC1E6 /* NativeWindowHostView.swift in Sources */,
|
||||
D05CC3251B695B0700E235A3 /* NavigationBarProxy.m in Sources */,
|
||||
D03E7DE61C96B96E00C07816 /* NavigationBarTransitionContainer.swift in Sources */,
|
||||
D0C85DD01D1C082E00124894 /* ActionSheetItemGroupsContainerNode.swift in Sources */,
|
||||
@ -746,7 +750,6 @@
|
||||
D05CC31D1B695A9600E235A3 /* UIBarButtonItem+Proxy.m in Sources */,
|
||||
D01E2BDE1D9049620066BF65 /* GridNode.swift in Sources */,
|
||||
D01E2BE01D90498E0066BF65 /* GridNodeScroller.swift in Sources */,
|
||||
D0C85DD61D1C600D00124894 /* ActionSheetButtonNode.swift in Sources */,
|
||||
D0C2DFD01CC4431D0044FF83 /* ListViewAccessoryItemNode.swift in Sources */,
|
||||
D0DA44501E4DCBDE005FDCA7 /* AlertContentNode.swift in Sources */,
|
||||
D0D94A171D3814F900740E02 /* UniversalTapRecognizer.swift in Sources */,
|
||||
@ -772,6 +775,7 @@
|
||||
D00C7CD21E3657570080C3D5 /* TextFieldNode.swift in Sources */,
|
||||
D0DC485F1BF949FB00F672FD /* TabBarContollerNode.swift in Sources */,
|
||||
D05CC2FA1B6955D000E235A3 /* UINavigationItem+Proxy.m in Sources */,
|
||||
D096A4501EA64F580000A7AE /* ActionSheetCheckboxItem.swift in Sources */,
|
||||
D0C2DFCE1CC4431D0044FF83 /* ListViewAccessoryItem.swift in Sources */,
|
||||
D0A749951E3A9E7B00AD786E /* SwitchNode.swift in Sources */,
|
||||
D03725C51D6DF8B9007FC290 /* ContextMenuController.swift in Sources */,
|
||||
@ -806,7 +810,7 @@
|
||||
D02383861DE0E3B4004018B6 /* ListViewIntermediateState.swift in Sources */,
|
||||
D0DA444E1E4DCA6E005FDCA7 /* AlertControllerNode.swift in Sources */,
|
||||
D0B367201C94A53A00346D2E /* StatusBarProxyNode.swift in Sources */,
|
||||
D05CC2A21B69326C00E235A3 /* Window.swift in Sources */,
|
||||
D05CC2A21B69326C00E235A3 /* WindowContent.swift in Sources */,
|
||||
D015F7541D1B0F6C00E269B5 /* SystemContainedControllerTransitionCoordinator.swift in Sources */,
|
||||
D05CC3151B695A9600E235A3 /* NavigationTransitionCoordinator.swift in Sources */,
|
||||
D03B0E701D6331FB00955575 /* StatusBarHost.swift in Sources */,
|
||||
|
||||
@ -1,24 +1,116 @@
|
||||
import Foundation
|
||||
import AsyncDisplayKit
|
||||
|
||||
public enum ActionSheetButtonColor {
|
||||
case accent
|
||||
case destructive
|
||||
case disabled
|
||||
}
|
||||
|
||||
public class ActionSheetButtonItem: ActionSheetItem {
|
||||
public let title: String
|
||||
public let color: ActionSheetButtonColor
|
||||
public let enabled: Bool
|
||||
public let action: () -> Void
|
||||
|
||||
public init(title: String, color: ActionSheetButtonColor = .accent, action: @escaping () -> Void) {
|
||||
public init(title: String, color: ActionSheetButtonColor = .accent, enabled: Bool = true, action: @escaping () -> Void) {
|
||||
self.title = title
|
||||
self.color = color
|
||||
self.enabled = enabled
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public func node() -> ActionSheetItemNode {
|
||||
let textColorIsAccent = self.color == ActionSheetButtonColor.accent
|
||||
let textColor = textColorIsAccent ? UIColor(0x007ee5) : UIColor.red
|
||||
return ActionSheetButtonNode(title: NSAttributedString(string: title, font: ActionSheetButtonNode.defaultFont, textColor: textColor), action: self.action)
|
||||
let node = ActionSheetButtonNode()
|
||||
node.setItem(self)
|
||||
return node
|
||||
}
|
||||
|
||||
public func updateNode(_ node: ActionSheetItemNode) {
|
||||
guard let node = node as? ActionSheetButtonNode else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
node.setItem(self)
|
||||
}
|
||||
}
|
||||
|
||||
public class ActionSheetButtonNode: ActionSheetItemNode {
|
||||
public static let defaultFont: UIFont = Font.regular(20.0)
|
||||
|
||||
private var item: ActionSheetButtonItem?
|
||||
|
||||
private let button: HighlightTrackingButton
|
||||
private let label: ASTextNode
|
||||
|
||||
override public init() {
|
||||
self.button = HighlightTrackingButton()
|
||||
|
||||
self.label = ASTextNode()
|
||||
self.label.isLayerBacked = true
|
||||
self.label.maximumNumberOfLines = 1
|
||||
self.label.displaysAsynchronously = false
|
||||
|
||||
super.init()
|
||||
|
||||
self.view.addSubview(self.button)
|
||||
|
||||
self.label.isUserInteractionEnabled = false
|
||||
self.addSubnode(self.label)
|
||||
|
||||
self.button.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.backgroundNode.backgroundColor = ActionSheetItemNode.highlightedBackgroundColor
|
||||
} else {
|
||||
UIView.animate(withDuration: 0.3, animations: {
|
||||
strongSelf.backgroundNode.backgroundColor = ActionSheetItemNode.defaultBackgroundColor
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
func setItem(_ item: ActionSheetButtonItem) {
|
||||
self.item = item
|
||||
|
||||
let textColor: UIColor
|
||||
switch item.color {
|
||||
case .accent:
|
||||
textColor = UIColor(0x007ee5)
|
||||
case .destructive:
|
||||
textColor = .red
|
||||
case .disabled:
|
||||
textColor = .gray
|
||||
}
|
||||
self.label.attributedText = NSAttributedString(string: item.title, font: ActionSheetButtonNode.defaultFont, textColor: textColor)
|
||||
|
||||
self.button.isEnabled = item.enabled
|
||||
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
|
||||
public override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||
return CGSize(width: constrainedSize.width, height: 57.0)
|
||||
}
|
||||
|
||||
public override func layout() {
|
||||
super.layout()
|
||||
|
||||
let size = self.bounds.size
|
||||
|
||||
self.button.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
let labelSize = self.label.measure(size)
|
||||
self.label.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - labelSize.width) / 2.0), y: floorToScreenPixels((size.height - labelSize.height) / 2.0)), size: labelSize)
|
||||
}
|
||||
|
||||
@objc func buttonPressed() {
|
||||
if let item = self.item {
|
||||
item.action()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
|
||||
public class ActionSheetButtonNode: ActionSheetItemNode {
|
||||
public static let defaultFont: UIFont = Font.regular(20.0)
|
||||
|
||||
private let action: () -> Void
|
||||
|
||||
private let button: HighlightTrackingButton
|
||||
private let label: UILabel
|
||||
private var calculatedLabelSize: CGSize?
|
||||
|
||||
public init(title: NSAttributedString, action: @escaping () -> Void) {
|
||||
self.action = action
|
||||
|
||||
self.button = HighlightTrackingButton()
|
||||
self.label = UILabel()
|
||||
|
||||
super.init()
|
||||
|
||||
self.view.addSubview(self.button)
|
||||
|
||||
self.label.attributedText = title
|
||||
self.label.numberOfLines = 1
|
||||
self.label.isUserInteractionEnabled = false
|
||||
self.view.addSubview(self.label)
|
||||
|
||||
self.button.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.backgroundNode.backgroundColor = ActionSheetItemNode.highlightedBackgroundColor
|
||||
} else {
|
||||
UIView.animate(withDuration: 0.3, animations: {
|
||||
strongSelf.backgroundNode.backgroundColor = ActionSheetItemNode.defaultBackgroundColor
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
public override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||
self.label.sizeToFit()
|
||||
self.calculatedLabelSize = self.label.frame.size
|
||||
|
||||
return CGSize(width: constrainedSize.width, height: 57.0)
|
||||
}
|
||||
|
||||
public override func layout() {
|
||||
super.layout()
|
||||
|
||||
self.button.frame = CGRect(origin: CGPoint(), size: self.calculatedSize)
|
||||
|
||||
if let calculatedLabelSize = self.calculatedLabelSize {
|
||||
self.label.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.calculatedSize.width - calculatedLabelSize.width) / 2.0), y: floorToScreenPixels((self.calculatedSize.height - calculatedLabelSize.height) / 2.0)), size: calculatedLabelSize)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func buttonPressed() {
|
||||
self.action()
|
||||
}
|
||||
}
|
||||
130
Display/ActionSheetCheckboxItem.swift
Normal file
130
Display/ActionSheetCheckboxItem.swift
Normal file
@ -0,0 +1,130 @@
|
||||
import Foundation
|
||||
import AsyncDisplayKit
|
||||
|
||||
private let checkIcon = generateImage(CGSize(width: 14.0, height: 11.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setStrokeColor(UIColor(0x007ee5).cgColor)
|
||||
context.setLineWidth(2.0)
|
||||
context.move(to: CGPoint(x: 12.0, y: 1.0))
|
||||
context.addLine(to: CGPoint(x: 4.16482734, y: 9.0))
|
||||
context.addLine(to: CGPoint(x: 1.0, y: 5.81145833))
|
||||
context.strokePath()
|
||||
})
|
||||
|
||||
public class ActionSheetCheckboxItem: ActionSheetItem {
|
||||
public let title: String
|
||||
public let label: String
|
||||
public let value: Bool
|
||||
public let action: (Bool) -> Void
|
||||
|
||||
public init(title: String, label: String, value: Bool, action: @escaping (Bool) -> Void) {
|
||||
self.title = title
|
||||
self.label = label
|
||||
self.value = value
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public func node() -> ActionSheetItemNode {
|
||||
let node = ActionSheetCheckboxItemNode()
|
||||
node.setItem(self)
|
||||
return node
|
||||
}
|
||||
|
||||
public func updateNode(_ node: ActionSheetItemNode) {
|
||||
guard let node = node as? ActionSheetCheckboxItemNode else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
node.setItem(self)
|
||||
}
|
||||
}
|
||||
|
||||
public class ActionSheetCheckboxItemNode: ActionSheetItemNode {
|
||||
public static let defaultFont: UIFont = Font.regular(20.0)
|
||||
|
||||
private var item: ActionSheetCheckboxItem?
|
||||
|
||||
private let button: HighlightTrackingButton
|
||||
private let titleNode: ASTextNode
|
||||
private let labelNode: ASTextNode
|
||||
private let checkNode: ASImageNode
|
||||
|
||||
public override init() {
|
||||
self.button = HighlightTrackingButton()
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
self.titleNode.maximumNumberOfLines = 1
|
||||
self.titleNode.isUserInteractionEnabled = false
|
||||
self.titleNode.displaysAsynchronously = false
|
||||
|
||||
self.labelNode = ASTextNode()
|
||||
self.labelNode.maximumNumberOfLines = 1
|
||||
self.labelNode.isUserInteractionEnabled = false
|
||||
self.labelNode.displaysAsynchronously = false
|
||||
|
||||
self.checkNode = ASImageNode()
|
||||
self.checkNode.isUserInteractionEnabled = false
|
||||
self.checkNode.displayWithoutProcessing = true
|
||||
self.checkNode.displaysAsynchronously = false
|
||||
self.checkNode.image = checkIcon
|
||||
|
||||
super.init()
|
||||
|
||||
self.view.addSubview(self.button)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
self.addSubnode(self.checkNode)
|
||||
|
||||
self.button.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.backgroundNode.backgroundColor = ActionSheetItemNode.highlightedBackgroundColor
|
||||
} else {
|
||||
UIView.animate(withDuration: 0.3, animations: {
|
||||
strongSelf.backgroundNode.backgroundColor = ActionSheetItemNode.defaultBackgroundColor
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
func setItem(_ item: ActionSheetCheckboxItem) {
|
||||
self.item = item
|
||||
|
||||
self.titleNode.attributedText = NSAttributedString(string: item.title, font: ActionSheetCheckboxItemNode.defaultFont, textColor: .black)
|
||||
self.labelNode.attributedText = NSAttributedString(string: item.label, font: ActionSheetCheckboxItemNode.defaultFont, textColor: UIColor(0x8e8e93))
|
||||
self.checkNode.isHidden = !item.value
|
||||
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
|
||||
public override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||
return CGSize(width: constrainedSize.width, height: 57.0)
|
||||
}
|
||||
|
||||
public override func layout() {
|
||||
super.layout()
|
||||
|
||||
let size = self.bounds.size
|
||||
|
||||
self.button.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
let labelSize = self.labelNode.measure(CGSize(width: size.width - 44.0 - 15.0 - 8.0, height: size.height))
|
||||
let titleSize = self.titleNode.measure(CGSize(width: size.width - 44.0 - labelSize.width - 15.0 - 8.0, height: size.height))
|
||||
self.titleNode.frame = CGRect(origin: CGPoint(x: 44.0, y: floorToScreenPixels((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||
self.labelNode.frame = CGRect(origin: CGPoint(x: size.width - 15.0 - labelSize.width, y: floorToScreenPixels((size.height - labelSize.height) / 2.0)), size: labelSize)
|
||||
|
||||
if let image = self.checkNode.image {
|
||||
self.checkNode.frame = CGRect(origin: CGPoint(x: floor((44.0 - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func buttonPressed() {
|
||||
if let item = self.item {
|
||||
item.action(!item.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,4 +49,10 @@ open class ActionSheetController: ViewController {
|
||||
self.actionSheetNode.setGroups(groups)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateItem(groupIndex: Int, itemIndex: Int, _ f: (ActionSheetItem) -> ActionSheetItem) {
|
||||
if self.isViewLoaded {
|
||||
self.actionSheetNode.updateItem(groupIndex: groupIndex, itemIndex: itemIndex, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,4 +155,8 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
func setGroups(_ groups: [ActionSheetItemGroup]) {
|
||||
self.itemGroupsContainerNode.setGroups(groups)
|
||||
}
|
||||
|
||||
public func updateItem(groupIndex: Int, itemIndex: Int, _ f: (ActionSheetItem) -> ActionSheetItem) {
|
||||
self.itemGroupsContainerNode.updateItem(groupIndex: groupIndex, itemIndex: itemIndex, f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,4 +2,5 @@ import Foundation
|
||||
|
||||
public protocol ActionSheetItem {
|
||||
func node() -> ActionSheetItemNode
|
||||
func updateNode(_ node: ActionSheetItemNode) -> Void
|
||||
}
|
||||
|
||||
@ -209,4 +209,8 @@ final class ActionSheetItemGroupNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
node.layer.animateAlpha(from: from, to: to, duration: duration)
|
||||
}
|
||||
}
|
||||
|
||||
func itemNode(at index: Int) -> ActionSheetItemNode {
|
||||
return self.itemNodes[index]
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import AsyncDisplayKit
|
||||
private let groupSpacing: CGFloat = 16.0
|
||||
|
||||
final class ActionSheetItemGroupsContainerNode: ASDisplayNode {
|
||||
private var groups: [ActionSheetItemGroup] = []
|
||||
private var groupNodes: [ActionSheetItemGroupNode] = []
|
||||
|
||||
override init() {
|
||||
@ -11,6 +12,8 @@ final class ActionSheetItemGroupsContainerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func setGroups(_ groups: [ActionSheetItemGroup]) {
|
||||
self.groups = groups
|
||||
|
||||
for groupNode in self.groupNodes {
|
||||
groupNode.removeFromSupernode()
|
||||
}
|
||||
@ -72,4 +75,16 @@ final class ActionSheetItemGroupsContainerNode: ASDisplayNode {
|
||||
node.animateDimViewsAlpha(from: from, to: to, duration: duration)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateItem(groupIndex: Int, itemIndex: Int, _ f: (ActionSheetItem) -> ActionSheetItem) {
|
||||
var item = self.groups[groupIndex].items[itemIndex]
|
||||
let itemNode = self.groupNodes[groupIndex].itemNode(at: itemIndex)
|
||||
item = f(item)
|
||||
item.updateNode(itemNode)
|
||||
|
||||
var groupItems = self.groups[groupIndex].items
|
||||
groupItems[itemIndex] = item
|
||||
|
||||
self.groups[groupIndex] = ActionSheetItemGroup(items: groupItems)
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,8 +45,8 @@ open class AlertController: ViewController {
|
||||
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
|
||||
override open func dismiss() {
|
||||
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
override open func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.presentingViewController?.dismiss(animated: false, completion: completion)
|
||||
}
|
||||
|
||||
public func dismissAnimated() {
|
||||
|
||||
@ -134,8 +134,8 @@ public extension CALayer {
|
||||
self.add(animation, forKey: keyPath)
|
||||
}
|
||||
|
||||
public func animateSpring(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, initialVelocity: CGFloat = 0.0, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
let animation = makeSpringBounceAnimation(keyPath, initialVelocity)
|
||||
public func animateSpring(from: AnyObject, to: AnyObject, keyPath: String, duration: Double, initialVelocity: CGFloat = 0.0, damping: CGFloat = 88.0, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
let animation = makeSpringBounceAnimation(keyPath, initialVelocity, damping)
|
||||
animation.fromValue = from
|
||||
animation.toValue = to
|
||||
animation.isRemovedOnCompletion = removeOnCompletion
|
||||
@ -187,8 +187,8 @@ public extension CALayer {
|
||||
self.animate(from: NSNumber(value: Float(from)), to: NSNumber(value: Float(to)), keyPath: "transform.scale", timingFunction: timingFunction, duration: duration, removeOnCompletion: removeOnCompletion, completion: completion)
|
||||
}
|
||||
|
||||
func animatePosition(from: CGPoint, to: CGPoint, duration: Double, timingFunction: String = kCAMediaTimingFunctionEaseInEaseOut, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if from == to {
|
||||
func animatePosition(from: CGPoint, to: CGPoint, duration: Double, timingFunction: String = kCAMediaTimingFunctionEaseInEaseOut, removeOnCompletion: Bool = true, additive: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if from == to && !force {
|
||||
if let completion = completion {
|
||||
completion(true)
|
||||
}
|
||||
@ -197,8 +197,8 @@ public extension CALayer {
|
||||
self.animate(from: NSValue(cgPoint: from), to: NSValue(cgPoint: to), keyPath: "position", timingFunction: timingFunction, duration: duration, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion)
|
||||
}
|
||||
|
||||
func animateBounds(from: CGRect, to: CGRect, duration: Double, timingFunction: String, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if from == to {
|
||||
func animateBounds(from: CGRect, to: CGRect, duration: Double, timingFunction: String, removeOnCompletion: Bool = true, additive: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if from == to && !force {
|
||||
if let completion = completion {
|
||||
completion(true)
|
||||
}
|
||||
@ -219,8 +219,8 @@ public extension CALayer {
|
||||
self.animateKeyframes(values: values.map { NSValue(cgPoint: $0) }, duration: duration, keyPath: "position")
|
||||
}
|
||||
|
||||
public func animateFrame(from: CGRect, to: CGRect, duration: Double, timingFunction: String, removeOnCompletion: Bool = true, additive: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if from == to {
|
||||
public func animateFrame(from: CGRect, to: CGRect, duration: Double, timingFunction: String, removeOnCompletion: Bool = true, additive: Bool = false, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if from == to && !force {
|
||||
if let completion = completion {
|
||||
completion(true)
|
||||
}
|
||||
@ -236,14 +236,14 @@ public extension CALayer {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.animatePosition(from: CGPoint(x: from.midX, y: from.midY), to: CGPoint(x: to.midX, y: to.midY), duration: duration, timingFunction: timingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: { value in
|
||||
self.animatePosition(from: CGPoint(x: from.midX, y: from.midY), to: CGPoint(x: to.midX, y: to.midY), duration: duration, timingFunction: timingFunction, removeOnCompletion: removeOnCompletion, additive: additive, force: force, completion: { value in
|
||||
if !value {
|
||||
interrupted = true
|
||||
}
|
||||
completedPosition = true
|
||||
partialCompletion()
|
||||
})
|
||||
self.animateBounds(from: CGRect(origin: self.bounds.origin, size: from.size), to: CGRect(origin: self.bounds.origin, size: to.size), duration: duration, timingFunction: timingFunction, removeOnCompletion: removeOnCompletion, additive: additive, completion: { value in
|
||||
self.animateBounds(from: CGRect(origin: self.bounds.origin, size: from.size), to: CGRect(origin: self.bounds.origin, size: to.size), duration: duration, timingFunction: timingFunction, removeOnCompletion: removeOnCompletion, additive: additive, force: force, completion: { value in
|
||||
if !value {
|
||||
interrupted = true
|
||||
}
|
||||
|
||||
@ -231,7 +231,41 @@ static void traceLayerSurfaces(int32_t tracingTag, int depth, CALayer * _Nonnull
|
||||
|
||||
- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key {
|
||||
if ([anim isKindOfClass:[CABasicAnimation class]]) {
|
||||
if ([key isEqualToString:@"position"]) {
|
||||
if (false && [key isEqualToString:@"bounds.origin.y"]) {
|
||||
CABasicAnimation *animCopy = [anim copy];
|
||||
CGFloat from = [animCopy.fromValue floatValue];
|
||||
CGFloat to = [animCopy.toValue floatValue];
|
||||
|
||||
animCopy.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0.0, to - from, 0.0f)];
|
||||
animCopy.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
|
||||
animCopy.keyPath = @"sublayerTransform";
|
||||
|
||||
__weak CATracingLayer *weakSelf = self;
|
||||
anim.delegate = [[CATracingLayerAnimationDelegate alloc] initWithDelegate:anim.delegate animationStopped:^{
|
||||
__strong CATracingLayer *strongSelf = weakSelf;
|
||||
if (strongSelf != nil) {
|
||||
[strongSelf invalidateUpTheTree];
|
||||
}
|
||||
}];
|
||||
|
||||
[super addAnimation:anim forKey:key];
|
||||
|
||||
CABasicAnimation *positionAnimCopy = [animCopy copy];
|
||||
positionAnimCopy.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0.0, 0.0, 0.0f)];
|
||||
positionAnimCopy.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
|
||||
positionAnimCopy.additive = true;
|
||||
positionAnimCopy.delegate = [[CATracingLayerAnimationDelegate alloc] initWithDelegate:anim.delegate animationStopped:^{
|
||||
__strong CATracingLayer *strongSelf = weakSelf;
|
||||
if (strongSelf != nil) {
|
||||
[strongSelf invalidateUpTheTree];
|
||||
}
|
||||
}];
|
||||
|
||||
[self invalidateUpTheTree];
|
||||
|
||||
[self mirrorAnimationDownTheTree:animCopy key:@"sublayerTransform"];
|
||||
[self mirrorPositionAnimationDownTheTree:positionAnimCopy key:@"sublayerTransform"];
|
||||
} else if ([key isEqualToString:@"position"]) {
|
||||
CABasicAnimation *animCopy = [anim copy];
|
||||
CGPoint from = [animCopy.fromValue CGPointValue];
|
||||
CGPoint to = [animCopy.toValue CGPointValue];
|
||||
|
||||
@ -31,8 +31,8 @@ public enum ContainedViewLayoutTransition {
|
||||
}
|
||||
|
||||
public extension ContainedViewLayoutTransition {
|
||||
func updateFrame(node: ASDisplayNode, frame: CGRect, completion: ((Bool) -> Void)? = nil) {
|
||||
if node.frame.equalTo(frame) {
|
||||
func updateFrame(node: ASDisplayNode, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
|
||||
if node.frame.equalTo(frame) && !force {
|
||||
completion?(true)
|
||||
} else {
|
||||
switch self {
|
||||
@ -44,7 +44,7 @@ public extension ContainedViewLayoutTransition {
|
||||
case let .animated(duration, curve):
|
||||
let previousFrame = node.frame
|
||||
node.frame = frame
|
||||
node.layer.animateFrame(from: previousFrame, to: frame, duration: duration, timingFunction: curve.timingFunction, completion: { result in
|
||||
node.layer.animateFrame(from: previousFrame, to: frame, duration: duration, timingFunction: curve.timingFunction, force: force, completion: { result in
|
||||
if let completion = completion {
|
||||
completion(result)
|
||||
}
|
||||
|
||||
@ -14,6 +14,14 @@ public struct Font {
|
||||
}
|
||||
}
|
||||
|
||||
public static func semibold(_ size: CGFloat) -> UIFont {
|
||||
if #available(iOS 8.2, *) {
|
||||
return UIFont.systemFont(ofSize: size, weight: UIFontWeightSemibold)
|
||||
} else {
|
||||
return CTFontCreateWithName("HelveticaNeue-Medium" as CFString, size, nil)
|
||||
}
|
||||
}
|
||||
|
||||
public static func bold(_ size: CGFloat) -> UIFont {
|
||||
if #available(iOS 8.2, *) {
|
||||
return UIFont.boldSystemFont(ofSize: size)
|
||||
|
||||
@ -12,4 +12,11 @@ public protocol GridItem {
|
||||
var section: GridSection? { get }
|
||||
func node(layout: GridNodeLayout) -> GridItemNode
|
||||
func update(node: GridItemNode)
|
||||
var aspectRatio: CGFloat { get }
|
||||
}
|
||||
|
||||
public extension GridItem {
|
||||
var aspectRatio: CGFloat {
|
||||
return 1.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,5 +2,16 @@ import Foundation
|
||||
import AsyncDisplayKit
|
||||
|
||||
open class GridItemNode: ASDisplayNode {
|
||||
|
||||
open var isVisibleInGrid = false
|
||||
open var isGridScrolling = false
|
||||
|
||||
final var cachedFrame: CGRect = CGRect()
|
||||
override open var frame: CGRect {
|
||||
get {
|
||||
return self.cachedFrame
|
||||
} set(value) {
|
||||
self.cachedFrame = value
|
||||
super.frame = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,21 +47,43 @@ public struct GridNodeScrollToItem {
|
||||
}
|
||||
}
|
||||
|
||||
public enum GridNodeLayoutType: Equatable {
|
||||
case fixed(itemSize: CGSize)
|
||||
case balanced(idealHeight: CGFloat)
|
||||
|
||||
public static func ==(lhs: GridNodeLayoutType, rhs: GridNodeLayoutType) -> Bool {
|
||||
switch lhs {
|
||||
case let .fixed(itemSize):
|
||||
if case .fixed(itemSize) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .balanced(idealHeight):
|
||||
if case .balanced(idealHeight) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct GridNodeLayout: Equatable {
|
||||
public let size: CGSize
|
||||
public let insets: UIEdgeInsets
|
||||
public let preloadSize: CGFloat
|
||||
public let itemSize: CGSize
|
||||
public let type: GridNodeLayoutType
|
||||
|
||||
public init(size: CGSize, insets: UIEdgeInsets, preloadSize: CGFloat, itemSize: CGSize) {
|
||||
public init(size: CGSize, insets: UIEdgeInsets, preloadSize: CGFloat, type: GridNodeLayoutType) {
|
||||
self.size = size
|
||||
self.insets = insets
|
||||
self.preloadSize = preloadSize
|
||||
self.itemSize = itemSize
|
||||
self.type = type
|
||||
}
|
||||
|
||||
public static func ==(lhs: GridNodeLayout, rhs: GridNodeLayout) -> Bool {
|
||||
return lhs.size.equalTo(rhs.size) && lhs.insets == rhs.insets && lhs.preloadSize.isEqual(to: rhs.preloadSize) && lhs.itemSize.equalTo(rhs.itemSize)
|
||||
return lhs.size.equalTo(rhs.size) && lhs.insets == rhs.insets && lhs.preloadSize.isEqual(to: rhs.preloadSize) && lhs.type == rhs.type
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,7 +245,7 @@ private struct WrappedGridItemNode: Hashable {
|
||||
}
|
||||
|
||||
open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
private var gridLayout = GridNodeLayout(size: CGSize(), insets: UIEdgeInsets(), preloadSize: 0.0, itemSize: CGSize())
|
||||
private var gridLayout = GridNodeLayout(size: CGSize(), insets: UIEdgeInsets(), preloadSize: 0.0, type: .fixed(itemSize: CGSize()))
|
||||
private var firstIndexInSectionOffset: Int = 0
|
||||
private var items: [GridItem] = []
|
||||
private var itemNodes: [Int: GridItemNode] = [:]
|
||||
@ -235,6 +257,8 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
public var visibleItemsUpdated: ((GridNodeVisibleItems) -> Void)?
|
||||
public var presentationLayoutUpdated: ((GridNodeCurrentPresentationLayout, ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
public final var floatingSections = false
|
||||
|
||||
public override init() {
|
||||
super.init()
|
||||
|
||||
@ -315,10 +339,12 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
self.items.insert(insertedItem.item, at: insertedItem.index)
|
||||
}
|
||||
|
||||
let sortedInsertItems = transaction.insertItems.sorted(by: { $0.index < $1.index })
|
||||
|
||||
var remappedInsertionItemNodes: [Int: GridItemNode] = [:]
|
||||
for (index, itemNode) in remappedDeletionItemNodes {
|
||||
var indexOffset = 0
|
||||
for insertedItem in transaction.insertItems {
|
||||
for insertedItem in sortedInsertItems {
|
||||
if insertedItem.index <= index + indexOffset {
|
||||
indexOffset += 1
|
||||
}
|
||||
@ -343,14 +369,26 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
generatedScrollToItem = nil
|
||||
}
|
||||
|
||||
self.applyPresentaionLayoutTransition(self.generatePresentationLayoutTransition(stationaryItems: transaction.stationaryItems, layoutTransactionOffset: layoutTransactionOffset, scrollToItem: generatedScrollToItem), removedNodes: removedNodes, updateLayoutTransition: transaction.updateLayout?.transition)
|
||||
|
||||
completion(self.displayedItemRange())
|
||||
self.applyPresentaionLayoutTransition(self.generatePresentationLayoutTransition(stationaryItems: transaction.stationaryItems, layoutTransactionOffset: layoutTransactionOffset, scrollToItem: generatedScrollToItem), removedNodes: removedNodes, updateLayoutTransition: transaction.updateLayout?.transition, completion: completion)
|
||||
}
|
||||
|
||||
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
self.updateItemNodeVisibilititesAndScrolling()
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||
if !decelerate {
|
||||
self.updateItemNodeVisibilititesAndScrolling()
|
||||
}
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
self.updateItemNodeVisibilititesAndScrolling()
|
||||
}
|
||||
|
||||
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if !self.applyingContentOffset {
|
||||
self.applyPresentaionLayoutTransition(self.generatePresentationLayoutTransition(layoutTransactionOffset: 0.0), removedNodes: [], updateLayoutTransition: nil)
|
||||
self.applyPresentaionLayoutTransition(self.generatePresentationLayoutTransition(layoutTransactionOffset: 0.0), removedNodes: [], updateLayoutTransition: nil, completion: { _ in })
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,60 +417,144 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
var items: [GridNodePresentationItem] = []
|
||||
var sections: [GridNodePresentationSection] = []
|
||||
|
||||
let itemsInRow = Int(gridLayout.size.width / gridLayout.itemSize.width)
|
||||
let itemsInRowWidth = CGFloat(itemsInRow) * gridLayout.itemSize.width
|
||||
let remainingWidth = gridLayout.size.width - itemsInRowWidth
|
||||
|
||||
let itemSpacing = floorToScreenPixels(remainingWidth / CGFloat(itemsInRow + 1))
|
||||
|
||||
var incrementedCurrentRow = false
|
||||
var nextItemOrigin = CGPoint(x: itemSpacing, y: 0.0)
|
||||
var index = 0
|
||||
var previousSection: GridSection?
|
||||
for item in self.items {
|
||||
let section = item.section
|
||||
var keepSection = true
|
||||
if let previousSection = previousSection, let section = section {
|
||||
keepSection = previousSection.isEqual(to: section)
|
||||
} else if (previousSection != nil) != (section != nil) {
|
||||
keepSection = false
|
||||
}
|
||||
|
||||
if !keepSection {
|
||||
if incrementedCurrentRow {
|
||||
nextItemOrigin.x = itemSpacing
|
||||
nextItemOrigin.y += gridLayout.itemSize.height
|
||||
incrementedCurrentRow = false
|
||||
switch gridLayout.type {
|
||||
case let .fixed(itemSize):
|
||||
let itemsInRow = Int(gridLayout.size.width / itemSize.width)
|
||||
let itemsInRowWidth = CGFloat(itemsInRow) * itemSize.width
|
||||
let remainingWidth = gridLayout.size.width - itemsInRowWidth
|
||||
|
||||
let itemSpacing = floorToScreenPixels(remainingWidth / CGFloat(itemsInRow + 1))
|
||||
|
||||
var incrementedCurrentRow = false
|
||||
var nextItemOrigin = CGPoint(x: itemSpacing, y: 0.0)
|
||||
var index = 0
|
||||
var previousSection: GridSection?
|
||||
for item in self.items {
|
||||
let section = item.section
|
||||
var keepSection = true
|
||||
if let previousSection = previousSection, let section = section {
|
||||
keepSection = previousSection.isEqual(to: section)
|
||||
} else if (previousSection != nil) != (section != nil) {
|
||||
keepSection = false
|
||||
}
|
||||
|
||||
if !keepSection {
|
||||
if incrementedCurrentRow {
|
||||
nextItemOrigin.x = itemSpacing
|
||||
nextItemOrigin.y += itemSize.height
|
||||
incrementedCurrentRow = false
|
||||
}
|
||||
|
||||
if let section = section {
|
||||
sections.append(GridNodePresentationSection(section: section, frame: CGRect(origin: CGPoint(x: 0.0, y: nextItemOrigin.y), size: CGSize(width: gridLayout.size.width, height: section.height))))
|
||||
nextItemOrigin.y += section.height
|
||||
contentSize.height += section.height
|
||||
}
|
||||
}
|
||||
previousSection = section
|
||||
|
||||
if !incrementedCurrentRow {
|
||||
incrementedCurrentRow = true
|
||||
contentSize.height += itemSize.height
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
let itemsInRow = Int(gridLayout.size.width) / Int(itemSize.width)
|
||||
let normalizedIndexOffset = self.firstIndexInSectionOffset % itemsInRow
|
||||
nextItemOrigin.x += (itemSize.width + itemSpacing) * CGFloat(normalizedIndexOffset)
|
||||
}
|
||||
|
||||
items.append(GridNodePresentationItem(index: index, frame: CGRect(origin: nextItemOrigin, size: itemSize)))
|
||||
index += 1
|
||||
|
||||
nextItemOrigin.x += itemSize.width + itemSpacing
|
||||
if nextItemOrigin.x + itemSize.width > gridLayout.size.width {
|
||||
nextItemOrigin.x = itemSpacing
|
||||
nextItemOrigin.y += itemSize.height
|
||||
incrementedCurrentRow = false
|
||||
}
|
||||
}
|
||||
case let .balanced(idealHeight):
|
||||
var weights: [Int] = []
|
||||
for item in self.items {
|
||||
weights.append(Int(item.aspectRatio * 100))
|
||||
}
|
||||
|
||||
if let section = section {
|
||||
sections.append(GridNodePresentationSection(section: section, frame: CGRect(origin: CGPoint(x: 0.0, y: nextItemOrigin.y), size: CGSize(width: gridLayout.size.width, height: section.height))))
|
||||
nextItemOrigin.y += section.height
|
||||
contentSize.height += section.height
|
||||
var totalItemSize: CGFloat = 0.0
|
||||
for i in 0 ..< self.items.count {
|
||||
totalItemSize += self.items[i].aspectRatio * idealHeight
|
||||
}
|
||||
}
|
||||
previousSection = section
|
||||
|
||||
if !incrementedCurrentRow {
|
||||
incrementedCurrentRow = true
|
||||
contentSize.height += gridLayout.itemSize.height
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
let itemsInRow = Int(gridLayout.size.width) / Int(gridLayout.itemSize.width)
|
||||
let normalizedIndexOffset = self.firstIndexInSectionOffset % itemsInRow
|
||||
nextItemOrigin.x += (gridLayout.itemSize.width + itemSpacing) * CGFloat(normalizedIndexOffset)
|
||||
}
|
||||
|
||||
items.append(GridNodePresentationItem(index: index, frame: CGRect(origin: nextItemOrigin, size: gridLayout.itemSize)))
|
||||
index += 1
|
||||
|
||||
nextItemOrigin.x += gridLayout.itemSize.width + itemSpacing
|
||||
if nextItemOrigin.x + gridLayout.itemSize.width > gridLayout.size.width {
|
||||
nextItemOrigin.x = itemSpacing
|
||||
nextItemOrigin.y += gridLayout.itemSize.height
|
||||
incrementedCurrentRow = false
|
||||
}
|
||||
let numberOfRows = max(Int(round(totalItemSize / gridLayout.size.width)), 1)
|
||||
|
||||
let partition = linearPartitionForWeights(weights, numberOfPartitions:numberOfRows)
|
||||
|
||||
var i = 0
|
||||
var offset = CGPoint(x: 0.0, y: 0.0)
|
||||
var previousItemSize: CGFloat = 0.0
|
||||
var contentMaxValueInScrollDirection: CGFloat = 0.0
|
||||
let maxWidth = gridLayout.size.width
|
||||
|
||||
let minimumInteritemSpacing: CGFloat = 1.0
|
||||
let minimumLineSpacing: CGFloat = 1.0
|
||||
|
||||
let viewportWidth: CGFloat = gridLayout.size.width
|
||||
|
||||
let preferredRowSize = idealHeight
|
||||
|
||||
var rowIndex = -1
|
||||
for row in partition {
|
||||
rowIndex += 1
|
||||
|
||||
var summedRatios: CGFloat = 0.0
|
||||
|
||||
var j = i
|
||||
var n = i + row.count
|
||||
|
||||
while j < n {
|
||||
summedRatios += self.items[j].aspectRatio
|
||||
|
||||
j += 1
|
||||
}
|
||||
|
||||
var rowSize = gridLayout.size.width - (CGFloat(row.count - 1) * minimumInteritemSpacing)
|
||||
|
||||
if rowIndex == partition.count - 1 {
|
||||
if row.count < 2 {
|
||||
rowSize = floor(viewportWidth / 3.0) - (CGFloat(row.count - 1) * minimumInteritemSpacing)
|
||||
} else if row.count < 3 {
|
||||
rowSize = floor(viewportWidth * 2.0 / 3.0) - (CGFloat(row.count - 1) * minimumInteritemSpacing)
|
||||
}
|
||||
}
|
||||
|
||||
j = i
|
||||
n = i + row.count
|
||||
|
||||
while j < n {
|
||||
let preferredAspectRatio = self.items[j].aspectRatio
|
||||
|
||||
let actualSize = CGSize(width: round(rowSize / summedRatios * (preferredAspectRatio)), height: preferredRowSize)
|
||||
|
||||
var frame = CGRect(x: offset.x, y: offset.y, width: actualSize.width, height: actualSize.height)
|
||||
if frame.origin.x + frame.size.width >= maxWidth - 2.0 {
|
||||
frame.size.width = max(1.0, maxWidth - frame.origin.x)
|
||||
}
|
||||
|
||||
items.append(GridNodePresentationItem(index: j, frame: frame))
|
||||
|
||||
offset.x += actualSize.width + minimumInteritemSpacing
|
||||
previousItemSize = actualSize.height
|
||||
contentMaxValueInScrollDirection = frame.maxY
|
||||
|
||||
j += 1
|
||||
}
|
||||
|
||||
if row.count > 0 {
|
||||
offset = CGPoint(x: 0.0, y: offset.y + previousItemSize + minimumLineSpacing)
|
||||
}
|
||||
|
||||
i += row.count
|
||||
}
|
||||
contentSize = CGSize(width: gridLayout.size.width, height: contentMaxValueInScrollDirection)
|
||||
}
|
||||
|
||||
return GridNodeItemLayout(contentSize: contentSize, items: items, sections: sections)
|
||||
@ -446,15 +568,17 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
var transitionDirectionHint: GridNodePreviousItemsTransitionDirectionHint = .up
|
||||
var transition: ContainedViewLayoutTransition = .immediate
|
||||
let contentOffset: CGPoint
|
||||
switch stationaryItems {
|
||||
var updatedStationaryItems = stationaryItems
|
||||
if scrollToItem != nil {
|
||||
updatedStationaryItems = .none
|
||||
}
|
||||
switch updatedStationaryItems {
|
||||
case .none:
|
||||
if let scrollToItem = scrollToItem {
|
||||
let itemFrame = self.itemLayout.items[scrollToItem.index]
|
||||
|
||||
var additionalOffset: CGFloat = 0.0
|
||||
if scrollToItem.adjustForTopInset {
|
||||
additionalOffset = -gridLayout.insets.top
|
||||
} else if scrollToItem.adjustForSection {
|
||||
if scrollToItem.adjustForSection {
|
||||
var adjustForSection: GridSection?
|
||||
if scrollToItem.index == 0 {
|
||||
if let itemSection = self.items[scrollToItem.index].section {
|
||||
@ -475,6 +599,12 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
if let adjustForSection = adjustForSection {
|
||||
additionalOffset = -adjustForSection.height
|
||||
}
|
||||
|
||||
if scrollToItem.adjustForTopInset {
|
||||
additionalOffset += -gridLayout.insets.top
|
||||
}
|
||||
} else if scrollToItem.adjustForTopInset {
|
||||
additionalOffset = -gridLayout.insets.top
|
||||
}
|
||||
|
||||
let displayHeight = max(0.0, self.gridLayout.size.height - self.gridLayout.insets.top - self.gridLayout.insets.bottom)
|
||||
@ -518,7 +648,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
var selectedContentOffset: CGPoint?
|
||||
for (index, itemNode) in self.itemNodes {
|
||||
if stationaryItemIndices.contains(index) {
|
||||
let currentScreenOffset = itemNode.frame.origin.y - self.scrollView.contentOffset.y
|
||||
//let currentScreenOffset = itemNode.frame.origin.y - self.scrollView.contentOffset.y
|
||||
selectedContentOffset = CGPoint(x: 0.0, y: self.itemLayout.items[index].frame.origin.y - itemNode.frame.origin.y + self.scrollView.contentOffset.y)
|
||||
break
|
||||
}
|
||||
@ -532,7 +662,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
case .all:
|
||||
var selectedContentOffset: CGPoint?
|
||||
for (index, itemNode) in self.itemNodes {
|
||||
let currentScreenOffset = itemNode.frame.origin.y - self.scrollView.contentOffset.y
|
||||
//let currentScreenOffset = itemNode.frame.origin.y - self.scrollView.contentOffset.y
|
||||
selectedContentOffset = CGPoint(x: 0.0, y: self.itemLayout.items[index].frame.origin.y - itemNode.frame.origin.y + self.scrollView.contentOffset.y)
|
||||
break
|
||||
}
|
||||
@ -548,6 +678,8 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
let upperDisplayBound = contentOffset.y + self.gridLayout.size.height + self.gridLayout.preloadSize
|
||||
|
||||
var presentationItems: [GridNodePresentationItem] = []
|
||||
|
||||
var validSections = Set<WrappedGridSection>()
|
||||
for item in self.itemLayout.items {
|
||||
if item.frame.origin.y < lowerDisplayBound {
|
||||
continue
|
||||
@ -556,12 +688,19 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
break
|
||||
}
|
||||
presentationItems.append(item)
|
||||
if self.floatingSections {
|
||||
if let section = self.items[item.index].section {
|
||||
validSections.insert(WrappedGridSection(section))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var presentationSections: [GridNodePresentationSection] = []
|
||||
for section in self.itemLayout.sections {
|
||||
if section.frame.origin.y < lowerDisplayBound {
|
||||
continue
|
||||
if !validSections.contains(WrappedGridSection(section.section)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if section.frame.origin.y + section.frame.size.height > upperDisplayBound {
|
||||
break
|
||||
@ -575,7 +714,21 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func applyPresentaionLayoutTransition(_ presentationLayoutTransition: GridNodePresentationLayoutTransition, removedNodes: [GridItemNode], updateLayoutTransition: ContainedViewLayoutTransition?) {
|
||||
private func lowestSectionNode() -> ASDisplayNode? {
|
||||
var lowestHeaderNode: ASDisplayNode?
|
||||
var lowestHeaderNodeIndex: Int?
|
||||
for (_, headerNode) in self.sectionNodes {
|
||||
if let index = self.subnodes.index(of: headerNode) {
|
||||
if lowestHeaderNodeIndex == nil || index < lowestHeaderNodeIndex! {
|
||||
lowestHeaderNodeIndex = index
|
||||
lowestHeaderNode = headerNode
|
||||
}
|
||||
}
|
||||
}
|
||||
return lowestHeaderNode
|
||||
}
|
||||
|
||||
private func applyPresentaionLayoutTransition(_ presentationLayoutTransition: GridNodePresentationLayoutTransition, removedNodes: [GridItemNode], updateLayoutTransition: ContainedViewLayoutTransition?, completion: (GridNodeDisplayedItemRange) -> Void) {
|
||||
var previousItemFrames: ([WrappedGridItemNode: CGRect])?
|
||||
switch presentationLayoutTransition.transition {
|
||||
case .animated:
|
||||
@ -603,29 +756,44 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
}
|
||||
applyingContentOffset = false
|
||||
|
||||
let lowestSectionNode: ASDisplayNode? = self.lowestSectionNode()
|
||||
|
||||
var existingItemIndices = Set<Int>()
|
||||
for item in presentationLayoutTransition.layout.items {
|
||||
existingItemIndices.insert(item.index)
|
||||
|
||||
if let itemNode = self.itemNodes[item.index] {
|
||||
itemNode.frame = item.frame
|
||||
if itemNode.frame != item.frame {
|
||||
itemNode.frame = item.frame
|
||||
}
|
||||
} else {
|
||||
let itemNode = self.items[item.index].node(layout: presentationLayoutTransition.layout.layout)
|
||||
itemNode.frame = item.frame
|
||||
self.addItemNode(index: item.index, itemNode: itemNode)
|
||||
self.addItemNode(index: item.index, itemNode: itemNode, lowestSectionNode: lowestSectionNode)
|
||||
}
|
||||
}
|
||||
|
||||
var existingSections = Set<WrappedGridSection>()
|
||||
for section in presentationLayoutTransition.layout.sections {
|
||||
for i in 0 ..< presentationLayoutTransition.layout.sections.count {
|
||||
let section = presentationLayoutTransition.layout.sections[i]
|
||||
|
||||
let wrappedSection = WrappedGridSection(section.section)
|
||||
existingSections.insert(wrappedSection)
|
||||
|
||||
var sectionFrame = section.frame
|
||||
if self.floatingSections {
|
||||
var maxY = CGFloat.greatestFiniteMagnitude
|
||||
if i != presentationLayoutTransition.layout.sections.count - 1 {
|
||||
maxY = presentationLayoutTransition.layout.sections[i + 1].frame.minY - sectionFrame.height
|
||||
}
|
||||
sectionFrame.origin.y = max(sectionFrame.minY, min(maxY, presentationLayoutTransition.layout.contentOffset.y + presentationLayoutTransition.layout.layout.insets.top))
|
||||
}
|
||||
|
||||
if let sectionNode = self.sectionNodes[wrappedSection] {
|
||||
sectionNode.frame = section.frame
|
||||
sectionNode.frame = sectionFrame
|
||||
} else {
|
||||
let sectionNode = section.section.node()
|
||||
sectionNode.frame = section.frame
|
||||
sectionNode.frame = sectionFrame
|
||||
self.addSectionNode(section: wrappedSection, sectionNode: sectionNode)
|
||||
}
|
||||
}
|
||||
@ -698,7 +866,6 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
itemNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
|
||||
}
|
||||
for (wrappedSection, sectionNode) in self.sectionNodes where existingSections.contains(wrappedSection) {
|
||||
let position = sectionNode.layer.position
|
||||
sectionNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
|
||||
}
|
||||
|
||||
@ -777,16 +944,20 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
completion(self.displayedItemRange())
|
||||
|
||||
self.updateItemNodeVisibilititesAndScrolling()
|
||||
|
||||
if let visibleItemsUpdated = self.visibleItemsUpdated {
|
||||
if presentationLayoutTransition.layout.items.count != 0 {
|
||||
let topIndex = presentationLayoutTransition.layout.items.first!.index
|
||||
let bottomIndex = presentationLayoutTransition.layout.items.last!.index
|
||||
|
||||
var topVisible: (Int, GridItem) = (topIndex, self.items[topIndex])
|
||||
var bottomVisible: (Int, GridItem) = (bottomIndex, self.items[bottomIndex])
|
||||
let bottomVisible: (Int, GridItem) = (bottomIndex, self.items[bottomIndex])
|
||||
|
||||
let lowerDisplayBound = presentationLayoutTransition.layout.contentOffset.y
|
||||
let upperDisplayBound = presentationLayoutTransition.layout.contentOffset.y + self.gridLayout.size.height
|
||||
//let upperDisplayBound = presentationLayoutTransition.layout.contentOffset.y + self.gridLayout.size.height
|
||||
|
||||
for item in presentationLayoutTransition.layout.items {
|
||||
if lowerDisplayBound.isLess(than: item.frame.maxY) {
|
||||
@ -816,11 +987,15 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func addItemNode(index: Int, itemNode: GridItemNode) {
|
||||
private func addItemNode(index: Int, itemNode: GridItemNode, lowestSectionNode: ASDisplayNode?) {
|
||||
assert(self.itemNodes[index] == nil)
|
||||
self.itemNodes[index] = itemNode
|
||||
if itemNode.supernode == nil {
|
||||
self.addSubnode(itemNode)
|
||||
if let lowestSectionNode = lowestSectionNode {
|
||||
self.insertSubnode(itemNode, belowSubnode: lowestSectionNode)
|
||||
} else {
|
||||
self.addSubnode(itemNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -848,6 +1023,20 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func updateItemNodeVisibilititesAndScrolling() {
|
||||
let visibleRect = self.scrollView.bounds
|
||||
let isScrolling = self.scrollView.isDragging || self.scrollView.isDecelerating
|
||||
for (_, itemNode) in self.itemNodes {
|
||||
let visible = itemNode.frame.intersects(visibleRect)
|
||||
if itemNode.isVisibleInGrid != visible {
|
||||
itemNode.isVisibleInGrid = visible
|
||||
}
|
||||
if itemNode.isGridScrolling != isScrolling {
|
||||
itemNode.isGridScrolling = isScrolling
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func forEachItemNode(_ f: (ASDisplayNode) -> Void) {
|
||||
for (_, node) in self.itemNodes {
|
||||
f(node)
|
||||
@ -872,4 +1061,127 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
f(row)
|
||||
}
|
||||
}
|
||||
|
||||
public func itemNodeAtPoint(_ point: CGPoint) -> ASDisplayNode? {
|
||||
for (_, node) in self.itemNodes {
|
||||
if node.frame.contains(point) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func NH_LP_TABLE_LOOKUP(_ table: inout [Int], _ i: Int, _ j: Int, _ rowsize: Int) -> Int {
|
||||
return table[i * rowsize + j]
|
||||
}
|
||||
|
||||
private func NH_LP_TABLE_LOOKUP_SET(_ table: inout [Int], _ i: Int, _ j: Int, _ rowsize: Int, _ value: Int) {
|
||||
table[i * rowsize + j] = value
|
||||
}
|
||||
|
||||
private func linearPartitionTable(_ weights: [Int], numberOfPartitions: Int) -> [Int] {
|
||||
let n = weights.count
|
||||
let k = numberOfPartitions
|
||||
|
||||
let tableSize = n * k;
|
||||
var tmpTable = Array<Int>(repeatElement(0, count: tableSize))
|
||||
|
||||
let solutionSize = (n - 1) * (k - 1)
|
||||
var solution = Array<Int>(repeatElement(0, count: solutionSize))
|
||||
|
||||
for i in 0 ..< n {
|
||||
let offset = i != 0 ? NH_LP_TABLE_LOOKUP(&tmpTable, i - 1, 0, k) : 0
|
||||
NH_LP_TABLE_LOOKUP_SET(&tmpTable, i, 0, k, Int(weights[i]) + offset)
|
||||
}
|
||||
|
||||
for j in 0 ..< k {
|
||||
NH_LP_TABLE_LOOKUP_SET(&tmpTable, 0, j, k, Int(weights[0]))
|
||||
}
|
||||
|
||||
for i in 1 ..< n {
|
||||
for j in 1 ..< k {
|
||||
var currentMin = 0
|
||||
var minX = Int.max
|
||||
|
||||
for x in 0 ..< i {
|
||||
let c1 = NH_LP_TABLE_LOOKUP(&tmpTable, x, j - 1, k)
|
||||
let c2 = NH_LP_TABLE_LOOKUP(&tmpTable, i, 0, k) - NH_LP_TABLE_LOOKUP(&tmpTable, x, 0, k)
|
||||
let cost = max(c1, c2)
|
||||
|
||||
if x == 0 || cost < currentMin {
|
||||
currentMin = cost;
|
||||
minX = x
|
||||
}
|
||||
}
|
||||
|
||||
NH_LP_TABLE_LOOKUP_SET(&tmpTable, i, j, k, currentMin)
|
||||
NH_LP_TABLE_LOOKUP_SET(&solution, i - 1, j - 1, k - 1, minX)
|
||||
}
|
||||
}
|
||||
|
||||
return solution
|
||||
}
|
||||
|
||||
private func linearPartitionForWeights(_ weights: [Int], numberOfPartitions: Int) -> [[Int]] {
|
||||
var n = weights.count
|
||||
var k = numberOfPartitions
|
||||
|
||||
if k <= 0 {
|
||||
return []
|
||||
}
|
||||
|
||||
if k >= n {
|
||||
var partition: [[Int]] = []
|
||||
for weight in weights {
|
||||
partition.append([weight])
|
||||
}
|
||||
return partition
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
return [weights]
|
||||
}
|
||||
|
||||
var solution = linearPartitionTable(weights, numberOfPartitions: numberOfPartitions)
|
||||
let solutionRowSize = numberOfPartitions - 1
|
||||
|
||||
k = k - 2;
|
||||
n = n - 1;
|
||||
|
||||
var answer: [[Int]] = []
|
||||
|
||||
while k >= 0 {
|
||||
if n < 1 {
|
||||
answer.insert([], at: 0)
|
||||
} else {
|
||||
var currentAnswer: [Int] = []
|
||||
|
||||
var i = NH_LP_TABLE_LOOKUP(&solution, n - 1, k, solutionRowSize) + 1
|
||||
let range = n + 1
|
||||
while i < range {
|
||||
currentAnswer.append(weights[i])
|
||||
i += 1
|
||||
}
|
||||
|
||||
answer.insert(currentAnswer, at: 0)
|
||||
|
||||
n = NH_LP_TABLE_LOOKUP(&solution, n - 1, k, solutionRowSize)
|
||||
}
|
||||
|
||||
k = k - 1
|
||||
}
|
||||
|
||||
var currentAnswer: [Int] = []
|
||||
var i = 0
|
||||
let range = n + 1
|
||||
while i < range {
|
||||
currentAnswer.append(weights[i])
|
||||
i += 1
|
||||
}
|
||||
|
||||
answer.insert(currentAnswer, at: 0)
|
||||
|
||||
return answer
|
||||
}
|
||||
|
||||
|
||||
@ -126,7 +126,7 @@ open class LegacyPresentedController: ViewController {
|
||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition)
|
||||
}
|
||||
|
||||
override open func dismiss() {
|
||||
override open func dismiss(completion: (() -> Void)? = nil) {
|
||||
switch self.presentation {
|
||||
case .modal:
|
||||
self.controllerNode.animateModalOut { [weak self] in
|
||||
@ -135,7 +135,7 @@ open class LegacyPresentedController: ViewController {
|
||||
} else if let controller = self?.legacyController as? TGNavigationController {
|
||||
controller.didDismiss()
|
||||
}*/
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: completion)
|
||||
}
|
||||
case .custom:
|
||||
/*if let controller = self.legacyController as? TGViewController {
|
||||
@ -143,7 +143,7 @@ open class LegacyPresentedController: ViewController {
|
||||
} else if let controller = self.legacyController as? TGNavigationController {
|
||||
controller.didDismiss()
|
||||
}*/
|
||||
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
self.presentingViewController?.dismiss(animated: false, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,7 +172,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
public func reportStackTrace(stack: String!, withSlide slide: String!) {
|
||||
NSLog("reportStackTrace stack: \(stack)\n\nslide: \(slide)")
|
||||
}
|
||||
|
||||
|
||||
override public init() {
|
||||
class DisplayLinkProxy: NSObject {
|
||||
weak var target: ListView?
|
||||
@ -459,7 +459,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
|
||||
self.updateItemHeaders()
|
||||
|
||||
for (headerId, headerNode) in self.itemHeaderNodes {
|
||||
for (_, headerNode) in self.itemHeaderNodes {
|
||||
//let position = headerNode.position
|
||||
//headerNode.position = CGPoint(x: position.x, y: position.y - deltaY)
|
||||
|
||||
@ -490,6 +490,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
|
||||
self.updateVisibleContentOffset()
|
||||
self.updateVisibleItemRange()
|
||||
self.updateItemNodesVisibilities()
|
||||
|
||||
//CATransaction.commit()
|
||||
}
|
||||
@ -2104,6 +2105,8 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
}
|
||||
}
|
||||
|
||||
self.updateItemNodesVisibilities()
|
||||
|
||||
self.updateScroller()
|
||||
self.setNeedsAnimations()
|
||||
|
||||
@ -2117,6 +2120,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
completion()
|
||||
} else {
|
||||
self.updateItemHeaders(headerNodesTransition, animateInsertion: animated || !requestItemInsertionAnimationsIndices.isEmpty)
|
||||
self.updateItemNodesVisibilities()
|
||||
|
||||
if animated {
|
||||
self.setNeedsAnimations()
|
||||
@ -2280,7 +2284,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
addHeader(previousHeaderId, previousUpperBound, previousLowerBound, previousHeaderItem, hasValidNodes)
|
||||
}
|
||||
|
||||
var currentIds = Set(self.itemHeaderNodes.keys)
|
||||
let currentIds = Set(self.itemHeaderNodes.keys)
|
||||
for id in currentIds.subtracting(visibleHeaderNodes) {
|
||||
if let headerNode = self.itemHeaderNodes.removeValue(forKey: id) {
|
||||
headerNode.removeFromSupernode()
|
||||
@ -2288,6 +2292,20 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
}
|
||||
}
|
||||
|
||||
private func updateItemNodesVisibilities() {
|
||||
let visibilityRect = CGRect(origin: CGPoint(x: 0.0, y: self.insets.top), size: CGSize(width: self.visibleSize.width, height: self.visibleSize.height - self.insets.top - self.insets.bottom))
|
||||
for itemNode in self.itemNodes {
|
||||
let itemFrame = itemNode.apparentFrame
|
||||
var visibility: ListViewItemNodeVisibility = .none
|
||||
if visibilityRect.intersects(itemFrame) {
|
||||
visibility = .visible
|
||||
}
|
||||
if visibility != itemNode.visibility {
|
||||
itemNode.visibility = visibility
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateAccessoryNodes(animated: Bool, currentTimestamp: Double) {
|
||||
var index = -1
|
||||
let count = self.itemNodes.count
|
||||
|
||||
@ -52,6 +52,12 @@ public struct ListViewItemNodeLayout {
|
||||
}
|
||||
}
|
||||
|
||||
public enum ListViewItemNodeVisibility {
|
||||
case none
|
||||
case nearlyVisible
|
||||
case visible
|
||||
}
|
||||
|
||||
open class ListViewItemNode: ASDisplayNode {
|
||||
let rotated: Bool
|
||||
final var index: Int?
|
||||
@ -85,6 +91,8 @@ open class ListViewItemNode: ASDisplayNode {
|
||||
|
||||
public final var canBeUsedAsScrollToItemAnchor: Bool = true
|
||||
|
||||
open var visibility: ListViewItemNodeVisibility = .none
|
||||
|
||||
open var canBeSelected: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
149
Display/NativeWindowHostView.swift
Normal file
149
Display/NativeWindowHostView.swift
Normal file
@ -0,0 +1,149 @@
|
||||
import Foundation
|
||||
import SwiftSignalKit
|
||||
|
||||
private let defaultOrientations: UIInterfaceOrientationMask = {
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
return .all
|
||||
} else {
|
||||
return .allButUpsideDown
|
||||
}
|
||||
}()
|
||||
|
||||
private class WindowRootViewController: UIViewController {
|
||||
var presentController: ((UIViewController, Bool, (() -> Void)?) -> Void)?
|
||||
var orientations: UIInterfaceOrientationMask = defaultOrientations {
|
||||
didSet {
|
||||
if oldValue != self.orientations {
|
||||
if self.orientations == .portrait {
|
||||
if UIDevice.current.orientation != .portrait {
|
||||
let value = UIInterfaceOrientation.portrait.rawValue
|
||||
UIDevice.current.setValue(value, forKey: "orientation")
|
||||
}
|
||||
} else {
|
||||
UIViewController.attemptRotationToDeviceOrientation()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return .default
|
||||
}
|
||||
|
||||
override var prefersStatusBarHidden: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||||
return orientations
|
||||
}
|
||||
}
|
||||
|
||||
private final class NativeWindow: UIWindow, WindowHost {
|
||||
var updateSize: ((CGSize) -> Void)?
|
||||
var layoutSubviewsEvent: (() -> Void)?
|
||||
var updateIsUpdatingOrientationLayout: ((Bool) -> Void)?
|
||||
var updateToInterfaceOrientation: (() -> Void)?
|
||||
var presentController: ((ViewController) -> Void)?
|
||||
var hitTestImpl: ((CGPoint, UIEvent?) -> UIView?)?
|
||||
|
||||
override var frame: CGRect {
|
||||
get {
|
||||
return super.frame
|
||||
} set(value) {
|
||||
let sizeUpdated = super.frame.size != value.size
|
||||
super.frame = value
|
||||
|
||||
if sizeUpdated {
|
||||
self.updateSize?(value.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var bounds: CGRect {
|
||||
get {
|
||||
return super.bounds
|
||||
}
|
||||
set(value) {
|
||||
let sizeUpdated = super.bounds.size != value.size
|
||||
super.bounds = value
|
||||
|
||||
if sizeUpdated {
|
||||
self.updateSize?(value.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.layoutSubviewsEvent?()
|
||||
}
|
||||
|
||||
override func _update(toInterfaceOrientation arg1: Int32, duration arg2: Double, force arg3: Bool) {
|
||||
self.updateIsUpdatingOrientationLayout?(true)
|
||||
super._update(toInterfaceOrientation: arg1, duration: arg2, force: arg3)
|
||||
self.updateIsUpdatingOrientationLayout?(false)
|
||||
|
||||
self.updateToInterfaceOrientation?()
|
||||
}
|
||||
|
||||
func present(_ controller: ViewController) {
|
||||
self.presentController?(controller)
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
return self.hitTestImpl?(point, event)
|
||||
}
|
||||
}
|
||||
|
||||
public func nativeWindowHostView() -> WindowHostView {
|
||||
let window = NativeWindow(frame: UIScreen.main.bounds)
|
||||
|
||||
let rootViewController = WindowRootViewController()
|
||||
window.rootViewController = rootViewController
|
||||
rootViewController.viewWillAppear(false)
|
||||
rootViewController.viewDidAppear(false)
|
||||
rootViewController.view.isHidden = true
|
||||
|
||||
let hostView = WindowHostView(view: window, isRotating: {
|
||||
return window.isRotating()
|
||||
}, updateSupportedInterfaceOrientations: { orientations in
|
||||
rootViewController.orientations = orientations
|
||||
})
|
||||
|
||||
window.updateSize = { [weak hostView] size in
|
||||
hostView?.updateSize?(size)
|
||||
}
|
||||
|
||||
window.layoutSubviewsEvent = { [weak hostView] in
|
||||
hostView?.layoutSubviews?()
|
||||
}
|
||||
|
||||
window.updateIsUpdatingOrientationLayout = { [weak hostView] value in
|
||||
hostView?.isUpdatingOrientationLayout = value
|
||||
}
|
||||
|
||||
window.updateToInterfaceOrientation = { [weak hostView] in
|
||||
hostView?.updateToInterfaceOrientation?()
|
||||
}
|
||||
|
||||
window.presentController = { [weak hostView] controller in
|
||||
hostView?.present?(controller)
|
||||
}
|
||||
|
||||
window.hitTestImpl = { [weak hostView] point, event in
|
||||
return hostView?.hitTest?(point, event)
|
||||
}
|
||||
|
||||
rootViewController.presentController = { [weak hostView] controller, animated, completion in
|
||||
if let strongSelf = hostView {
|
||||
strongSelf.present?(LegacyPresentedController(legacyController: controller, presentation: .custom))
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hostView
|
||||
}
|
||||
@ -35,7 +35,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
open var foregroundColor: UIColor = UIColor.black {
|
||||
didSet {
|
||||
if let title = self.title {
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(17.0), textColor: self.foregroundColor)
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: self.foregroundColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,7 +53,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
|
||||
private var collapsed: Bool {
|
||||
get {
|
||||
return self.frame.size.height < (20.0 + 44.0)
|
||||
return self.frame.size.height.isLess(than: 44.0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,7 +173,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
private var title: String? {
|
||||
didSet {
|
||||
if let title = self.title {
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(17.0), textColor: self.foregroundColor)
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: self.foregroundColor)
|
||||
if self.titleNode.supernode == nil {
|
||||
self.clippingNode.addSubnode(self.titleNode)
|
||||
}
|
||||
@ -544,7 +544,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
public func makeTransitionTitleNode(foregroundColor: UIColor) -> ASDisplayNode? {
|
||||
if let title = self.title {
|
||||
let node = ASTextNode()
|
||||
node.attributedText = NSAttributedString(string: title, font: Font.medium(17.0), textColor: foregroundColor)
|
||||
node.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: foregroundColor)
|
||||
return node
|
||||
} else {
|
||||
return nil
|
||||
|
||||
@ -504,4 +504,17 @@ open class NavigationController: NavigationControllerProxy, ContainableControlle
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public final var window: WindowHost? {
|
||||
if let window = self.view.window as? WindowHost {
|
||||
return window
|
||||
} else if let superwindow = self.view.window {
|
||||
for subview in superwindow.subviews {
|
||||
if let subview = subview as? WindowHost {
|
||||
return subview
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,8 @@ final class PresentationContext {
|
||||
|
||||
private var presentationDisposables = DisposableSet()
|
||||
|
||||
var topLevelSubview: UIView?
|
||||
|
||||
public func present(_ controller: ViewController) {
|
||||
let controllerReady = controller.ready.get()
|
||||
|> filter({ $0 })
|
||||
@ -40,7 +42,7 @@ final class PresentationContext {
|
||||
|> deliverOnMainQueue
|
||||
|> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(true))
|
||||
|
||||
if let view = self.view, let initialLayout = self.layout {
|
||||
if let _ = self.view, let initialLayout = self.layout {
|
||||
controller.view.frame = CGRect(origin: CGPoint(), size: initialLayout.size)
|
||||
controller.containerLayoutUpdated(initialLayout, transition: .immediate)
|
||||
|
||||
@ -60,10 +62,18 @@ final class PresentationContext {
|
||||
controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
if layout != initialLayout {
|
||||
controller.view.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
view.addSubview(controller.view)
|
||||
if let topLevelSubview = strongSelf.topLevelSubview {
|
||||
view.insertSubview(controller.view, belowSubview: topLevelSubview)
|
||||
} else {
|
||||
view.addSubview(controller.view)
|
||||
}
|
||||
controller.containerLayoutUpdated(layout, transition: .immediate)
|
||||
} else {
|
||||
view.addSubview(controller.view)
|
||||
if let topLevelSubview = strongSelf.topLevelSubview {
|
||||
view.insertSubview(controller.view, belowSubview: topLevelSubview)
|
||||
} else {
|
||||
view.addSubview(controller.view)
|
||||
}
|
||||
}
|
||||
controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
view.layer.invalidateUpTheTree()
|
||||
@ -115,7 +125,11 @@ final class PresentationContext {
|
||||
if let view = self.view, let layout = self.layout {
|
||||
for controller in self.controllers {
|
||||
controller.viewWillAppear(false)
|
||||
view.addSubview(controller.view)
|
||||
if let topLevelSubview = self.topLevelSubview {
|
||||
view.insertSubview(controller.view, belowSubview: topLevelSubview)
|
||||
} else {
|
||||
view.addSubview(controller.view)
|
||||
}
|
||||
controller.view.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
controller.containerLayoutUpdated(layout, transition: .immediate)
|
||||
controller.viewDidAppear(false)
|
||||
@ -141,4 +155,14 @@ final class PresentationContext {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func combinedSupportedOrientations() -> UIInterfaceOrientationMask {
|
||||
var mask: UIInterfaceOrientationMask = .all
|
||||
|
||||
for controller in self.controllers {
|
||||
mask = mask.intersection(controller.supportedInterfaceOrientations)
|
||||
}
|
||||
|
||||
return mask
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ public class StatusBarSurface {
|
||||
public class StatusBar: ASDisplayNode {
|
||||
public var statusBarStyle: StatusBarStyle = .Black {
|
||||
didSet {
|
||||
if self.statusBarStyle != statusBarStyle {
|
||||
if self.statusBarStyle != oldValue {
|
||||
self.layer.invalidateUpTheTree()
|
||||
}
|
||||
}
|
||||
@ -40,7 +40,7 @@ public class StatusBar: ASDisplayNode {
|
||||
return UITracingLayerView()
|
||||
}, didLoad: nil)
|
||||
|
||||
self.layer.setTraceableInfo(CATracingLayerInfo(shouldBeAdjustedToInverseTransform: true, userData: self, tracingTag: Window.statusBarTracingTag))
|
||||
self.layer.setTraceableInfo(CATracingLayerInfo(shouldBeAdjustedToInverseTransform: true, userData: self, tracingTag: WindowTracingTags.statusBar))
|
||||
|
||||
self.clipsToBounds = true
|
||||
self.isUserInteractionEnabled = false
|
||||
|
||||
@ -18,7 +18,13 @@ private func mapStatusBar(_ statusBar: StatusBar) -> MappedStatusBar {
|
||||
}
|
||||
|
||||
private func mappedSurface(_ surface: StatusBarSurface) -> MappedStatusBarSurface {
|
||||
return MappedStatusBarSurface(statusBars: surface.statusBars.map(mapStatusBar), surface: surface)
|
||||
var statusBars: [MappedStatusBar] = []
|
||||
for statusBar in surface.statusBars {
|
||||
if statusBar.statusBarStyle != .Ignore {
|
||||
statusBars.append(mapStatusBar(statusBar))
|
||||
}
|
||||
}
|
||||
return MappedStatusBarSurface(statusBars: statusBars, surface: surface)
|
||||
}
|
||||
|
||||
private func optimizeMappedSurface(statusBarSize: CGSize, surface: MappedStatusBarSurface) -> MappedStatusBarSurface {
|
||||
@ -70,12 +76,36 @@ class StatusBarManager {
|
||||
return
|
||||
}
|
||||
|
||||
var mappedSurfaces = self.surfaces.map({ optimizeMappedSurface(statusBarSize: statusBarFrame.size, surface: mappedSurface($0)) })
|
||||
var mappedSurfaces: [MappedStatusBarSurface] = []
|
||||
var mapIndex = 0
|
||||
var doNotOptimize = false
|
||||
for surface in self.surfaces {
|
||||
inner: for statusBar in surface.statusBars {
|
||||
if statusBar.statusBarStyle == .Hide {
|
||||
doNotOptimize = true
|
||||
break inner
|
||||
}
|
||||
}
|
||||
|
||||
let mapped = mappedSurface(surface)
|
||||
|
||||
if doNotOptimize {
|
||||
mappedSurfaces.append(mapped)
|
||||
} else {
|
||||
mappedSurfaces.append(optimizeMappedSurface(statusBarSize: statusBarFrame.size, surface: mapped))
|
||||
}
|
||||
mapIndex += 1
|
||||
}
|
||||
|
||||
var reduceSurfaces = true
|
||||
var reduceSurfacesStatusBarStyleAndAlpha: (StatusBarStyle, CGFloat)?
|
||||
var reduceIndex = 0
|
||||
outer: for surface in mappedSurfaces {
|
||||
for mappedStatusBar in surface.statusBars {
|
||||
if reduceIndex == 0 && mappedStatusBar.style == .Hide {
|
||||
reduceSurfaces = false
|
||||
break outer
|
||||
}
|
||||
if mappedStatusBar.frame.origin.equalTo(CGPoint()) {
|
||||
let statusBarAlpha = mappedStatusBar.statusBar?.alpha ?? 1.0
|
||||
if let reduceSurfacesStatusBarStyleAndAlpha = reduceSurfacesStatusBarStyleAndAlpha {
|
||||
@ -92,6 +122,7 @@ class StatusBarManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
reduceIndex += 1
|
||||
}
|
||||
|
||||
if reduceSurfaces {
|
||||
@ -112,16 +143,19 @@ class StatusBarManager {
|
||||
var globalStatusBar: (StatusBarStyle, CGFloat)?
|
||||
|
||||
var coveredIdentity = false
|
||||
var statusBarIndex = 0
|
||||
for i in 0 ..< mappedSurfaces.count {
|
||||
for mappedStatusBar in mappedSurfaces[i].statusBars {
|
||||
if let statusBar = mappedStatusBar.statusBar {
|
||||
if mappedStatusBar.frame.origin.equalTo(CGPoint()) && !statusBar.layer.hasPositionOrOpacityAnimations() {
|
||||
if !coveredIdentity {
|
||||
coveredIdentity = CGFloat(1.0).isLessThanOrEqualTo(statusBar.alpha)
|
||||
if i == 0 && globalStatusBar == nil {
|
||||
globalStatusBar = (mappedStatusBar.style, statusBar.alpha)
|
||||
} else {
|
||||
visibleStatusBars.append(statusBar)
|
||||
if statusBar.statusBarStyle != .Hide {
|
||||
coveredIdentity = CGFloat(1.0).isLessThanOrEqualTo(statusBar.alpha)
|
||||
if statusBarIndex == 0 && globalStatusBar == nil {
|
||||
globalStatusBar = (mappedStatusBar.style, statusBar.alpha)
|
||||
} else {
|
||||
visibleStatusBars.append(statusBar)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -130,11 +164,12 @@ class StatusBarManager {
|
||||
} else {
|
||||
if !coveredIdentity {
|
||||
coveredIdentity = true
|
||||
if i == 0 && globalStatusBar == nil {
|
||||
if statusBarIndex == 0 && globalStatusBar == nil {
|
||||
globalStatusBar = (mappedStatusBar.style, 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
statusBarIndex += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ import AsyncDisplayKit
|
||||
public enum StatusBarStyle {
|
||||
case Black
|
||||
case White
|
||||
case Ignore
|
||||
case Hide
|
||||
}
|
||||
|
||||
private enum StatusBarItemType {
|
||||
@ -142,7 +144,7 @@ private func tintStatusBarItem(_ context: DrawingContext, type: StatusBarItemTyp
|
||||
|
||||
let baseColor: UInt32
|
||||
switch style {
|
||||
case .Black:
|
||||
case .Black, .Ignore, .Hide:
|
||||
baseColor = 0x000000
|
||||
case .White:
|
||||
baseColor = 0xffffff
|
||||
@ -193,7 +195,7 @@ private func tintStatusBarItem(_ context: DrawingContext, type: StatusBarItemTyp
|
||||
|
||||
let baseColor: UInt32
|
||||
switch style {
|
||||
case .Black:
|
||||
case .Black, .Ignore, .Hide:
|
||||
baseColor = 0x000000
|
||||
case .White:
|
||||
baseColor = 0xffffff
|
||||
|
||||
@ -27,6 +27,8 @@ open class SwitchNode: ASDisplayNode {
|
||||
override open func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
(self.view as! UISwitch).backgroundColor = .white
|
||||
|
||||
(self.view as! UISwitch).setOn(self._isOn, animated: false)
|
||||
|
||||
(self.view as! UISwitch).addTarget(self, action: #selector(switchValueChanged(_:)), for: .valueChanged)
|
||||
|
||||
@ -8,6 +8,6 @@
|
||||
@end
|
||||
|
||||
CABasicAnimation * _Nonnull makeSpringAnimation(NSString * _Nonnull keyPath);
|
||||
CABasicAnimation * _Nonnull makeSpringBounceAnimation(NSString * _Nonnull keyPath, CGFloat initialVelocity);
|
||||
CABasicAnimation * _Nonnull makeSpringBounceAnimation(NSString * _Nonnull keyPath, CGFloat initialVelocity, CGFloat damping);
|
||||
CGFloat springAnimationValueAt(CABasicAnimation * _Nonnull animation, CGFloat t);
|
||||
|
||||
|
||||
@ -41,11 +41,11 @@ CABasicAnimation * _Nonnull makeSpringAnimation(NSString * _Nonnull keyPath) {
|
||||
return springAnimation;
|
||||
}
|
||||
|
||||
CABasicAnimation * _Nonnull makeSpringBounceAnimation(NSString * _Nonnull keyPath, CGFloat initialVelocity) {
|
||||
CABasicAnimation * _Nonnull makeSpringBounceAnimation(NSString * _Nonnull keyPath, CGFloat initialVelocity, CGFloat damping) {
|
||||
CASpringAnimation *springAnimation = [CASpringAnimation animationWithKeyPath:keyPath];
|
||||
springAnimation.mass = 5.0f;
|
||||
springAnimation.stiffness = 900.0f;
|
||||
springAnimation.damping = 88.0f;
|
||||
springAnimation.damping = damping;
|
||||
static bool canSetInitialVelocity = true;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
@ -118,11 +118,15 @@ public extension UIImage {
|
||||
|
||||
private func makeSubtreeSnapshot(layer: CALayer) -> UIView? {
|
||||
let view = UIView()
|
||||
//view.layer.isHidden = layer.isHidden
|
||||
view.layer.opacity = layer.opacity
|
||||
view.layer.contents = layer.contents
|
||||
view.layer.contentsRect = layer.contentsRect
|
||||
view.layer.contentsScale = layer.contentsScale
|
||||
view.layer.contentsCenter = layer.contentsCenter
|
||||
view.layer.contentsGravity = layer.contentsGravity
|
||||
view.layer.masksToBounds = layer.masksToBounds
|
||||
view.layer.cornerRadius = layer.cornerRadius
|
||||
if let sublayers = layer.sublayers {
|
||||
for sublayer in sublayers {
|
||||
let subtree = makeSubtreeSnapshot(layer: sublayer)
|
||||
|
||||
@ -33,6 +33,11 @@ open class ViewControllerPresentationArguments {
|
||||
private var containerLayout = ContainerViewLayout()
|
||||
private let presentationContext: PresentationContext
|
||||
|
||||
public final var supportedOrientations: UIInterfaceOrientationMask = .all
|
||||
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||||
return self.supportedOrientations
|
||||
}
|
||||
|
||||
public private(set) var presentationArguments: Any?
|
||||
|
||||
private var _displayNode: ASDisplayNode?
|
||||
@ -137,9 +142,9 @@ open class ViewControllerPresentationArguments {
|
||||
|
||||
let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0
|
||||
var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: max(0.0, statusBarHeight - 20.0)), size: CGSize(width: layout.size.width, height: 64.0))
|
||||
if statusBarHeight.isLessThanOrEqualTo(0.0) {
|
||||
navigationBarFrame.origin.y -= 20.0
|
||||
navigationBarFrame.size.height = 20.0 + 32.0
|
||||
if layout.statusBarHeight == nil {
|
||||
//navigationBarFrame.origin.y -= 20.0
|
||||
navigationBarFrame.size.height = 44.0
|
||||
}
|
||||
|
||||
if !self.displayNavigationBar {
|
||||
@ -169,7 +174,7 @@ open class ViewControllerPresentationArguments {
|
||||
|
||||
open func displayNodeDidLoad() {
|
||||
if let layer = self.displayNode.layer as? CATracingLayer {
|
||||
layer.setTraceableInfo(CATracingLayerInfo(shouldBeAdjustedToInverseTransform: false, userData: self.displayNode.layer, tracingTag: Window.keyboardTracingTag))
|
||||
layer.setTraceableInfo(CATracingLayerInfo(shouldBeAdjustedToInverseTransform: false, userData: self.displayNode.layer, tracingTag: WindowTracingTags.keyboard))
|
||||
}
|
||||
self.updateScrollToTopView()
|
||||
}
|
||||
@ -195,7 +200,14 @@ open class ViewControllerPresentationArguments {
|
||||
}
|
||||
|
||||
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
preconditionFailure("use present(_:in)")
|
||||
super.present(viewControllerToPresent, animated: flag, completion: completion)
|
||||
return
|
||||
|
||||
if let controller = viewControllerToPresent as? ViewController {
|
||||
self.present(controller, in: .window)
|
||||
} else {
|
||||
preconditionFailure("use present(_:in) for \(viewControllerToPresent)")
|
||||
}
|
||||
}
|
||||
|
||||
override open func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
@ -206,12 +218,12 @@ open class ViewControllerPresentationArguments {
|
||||
}
|
||||
}
|
||||
|
||||
private var window: Window? {
|
||||
if let window = self.view.window as? Window {
|
||||
public final var window: WindowHost? {
|
||||
if let window = self.view.window as? WindowHost {
|
||||
return window
|
||||
} else if let superwindow = self.view.window {
|
||||
for subview in superwindow.subviews {
|
||||
if let subview = subview as? Window {
|
||||
if let subview = subview as? WindowHost {
|
||||
return subview
|
||||
}
|
||||
}
|
||||
@ -247,6 +259,6 @@ open class ViewControllerPresentationArguments {
|
||||
super.viewDidAppear(animated)
|
||||
}
|
||||
|
||||
open func dismiss() {
|
||||
open func dismiss(completion: (() -> Void)? = nil) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,12 +11,6 @@ private class WindowRootViewController: UIViewController {
|
||||
override var prefersStatusBarHidden: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
/*override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
if let presentController = self.presentController {
|
||||
presentController(viewControllerToPresent, flag, completion)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private struct WindowLayout: Equatable {
|
||||
@ -115,9 +109,37 @@ private func containedLayoutForWindowLayout(_ layout: WindowLayout) -> Container
|
||||
return ContainerViewLayout(size: layout.size, intrinsicInsets: UIEdgeInsets(), statusBarHeight: layout.statusBarHeight, inputHeight: inputHeight)
|
||||
}
|
||||
|
||||
public class Window: UIWindow {
|
||||
public static let statusBarTracingTag: Int32 = 0
|
||||
public static let keyboardTracingTag: Int32 = 1
|
||||
public final class WindowHostView {
|
||||
public let view: UIView
|
||||
public let isRotating: () -> Bool
|
||||
|
||||
let updateSupportedInterfaceOrientations: (UIInterfaceOrientationMask) -> Void
|
||||
|
||||
var present: ((ViewController) -> Void)?
|
||||
var updateSize: ((CGSize) -> Void)?
|
||||
var layoutSubviews: (() -> Void)?
|
||||
var updateToInterfaceOrientation: (() -> Void)?
|
||||
var isUpdatingOrientationLayout = false
|
||||
var hitTest: ((CGPoint, UIEvent?) -> UIView?)?
|
||||
|
||||
init(view: UIView, isRotating: @escaping () -> Bool, updateSupportedInterfaceOrientations: @escaping (UIInterfaceOrientationMask) -> Void) {
|
||||
self.view = view
|
||||
self.isRotating = isRotating
|
||||
self.updateSupportedInterfaceOrientations = updateSupportedInterfaceOrientations
|
||||
}
|
||||
}
|
||||
|
||||
public struct WindowTracingTags {
|
||||
public static let statusBar: Int32 = 0
|
||||
public static let keyboard: Int32 = 1
|
||||
}
|
||||
|
||||
public protocol WindowHost {
|
||||
func present(_ controller: ViewController)
|
||||
}
|
||||
|
||||
public class Window1 {
|
||||
public let hostView: WindowHostView
|
||||
|
||||
private let statusBarHost: StatusBarHost?
|
||||
private let statusBarManager: StatusBarManager?
|
||||
@ -128,15 +150,15 @@ public class Window: UIWindow {
|
||||
private var windowLayout: WindowLayout
|
||||
private var updatingLayout: UpdatingLayout?
|
||||
|
||||
public var isUpdatingOrientationLayout = false
|
||||
|
||||
private let presentationContext: PresentationContext
|
||||
|
||||
private var tracingStatusBarsInvalidated = false
|
||||
|
||||
private var statusBarHidden = false
|
||||
|
||||
public init(frame: CGRect, statusBarHost: StatusBarHost?) {
|
||||
public init(hostView: WindowHostView, statusBarHost: StatusBarHost?) {
|
||||
self.hostView = hostView
|
||||
|
||||
self.statusBarHost = statusBarHost
|
||||
let statusBarHeight: CGFloat
|
||||
if let statusBarHost = statusBarHost {
|
||||
@ -156,15 +178,33 @@ public class Window: UIWindow {
|
||||
minimized = false
|
||||
}
|
||||
|
||||
self.windowLayout = WindowLayout(size: frame.size, statusBarHeight: statusBarHeight, inputHeight: 0.0, inputMinimized: minimized)
|
||||
self.windowLayout = WindowLayout(size: self.hostView.view.bounds.size, statusBarHeight: statusBarHeight, inputHeight: 0.0, inputMinimized: minimized)
|
||||
self.presentationContext = PresentationContext()
|
||||
|
||||
super.init(frame: frame)
|
||||
self.hostView.present = { [weak self] controller in
|
||||
self?.present(controller)
|
||||
}
|
||||
|
||||
self.layer.setInvalidateTracingSublayers { [weak self] in
|
||||
self.hostView.updateSize = { [weak self] size in
|
||||
self?.updateSize(size)
|
||||
}
|
||||
|
||||
self.hostView.view.layer.setInvalidateTracingSublayers { [weak self] in
|
||||
self?.invalidateTracingStatusBars()
|
||||
}
|
||||
|
||||
self.hostView.layoutSubviews = { [weak self] in
|
||||
self?.layoutSubviews()
|
||||
}
|
||||
|
||||
self.hostView.updateToInterfaceOrientation = { [weak self] in
|
||||
self?.updateToInterfaceOrientation()
|
||||
}
|
||||
|
||||
self.hostView.hitTest = { [weak self] point, event in
|
||||
return self?.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
self.keyboardManager?.minimizedUpdated = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateLayout { current in
|
||||
@ -173,24 +213,9 @@ public class Window: UIWindow {
|
||||
}
|
||||
}
|
||||
|
||||
self.presentationContext.view = self
|
||||
self.presentationContext.view = self.hostView.view
|
||||
self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: .immediate)
|
||||
|
||||
let rootViewController = WindowRootViewController()
|
||||
super.rootViewController = rootViewController
|
||||
rootViewController.viewWillAppear(false)
|
||||
rootViewController.viewDidAppear(false)
|
||||
rootViewController.view.isHidden = true
|
||||
|
||||
rootViewController.presentController = { [weak self] controller, animated, completion in
|
||||
if let strongSelf = self {
|
||||
strongSelf.present(LegacyPresentedController(legacyController: controller, presentation: .custom))
|
||||
if let completion = completion {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.statusBarChangeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil, queue: OperationQueue.main, using: { [weak self] notification in
|
||||
if let strongSelf = self {
|
||||
let statusBarHeight: CGFloat = max(20.0, (notification.userInfo?[UIApplicationStatusBarFrameUserInfoKey] as? NSValue)?.cgRectValue.height ?? 20.0)
|
||||
@ -205,7 +230,7 @@ public class Window: UIWindow {
|
||||
let keyboardFrame: CGRect = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect()
|
||||
let keyboardHeight = max(0.0, UIScreen.main.bounds.size.height - keyboardFrame.minY)
|
||||
var duration: Double = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0.0
|
||||
if duration > DBL_EPSILON {
|
||||
if duration > Double.ulpOfOne {
|
||||
duration = 0.5
|
||||
}
|
||||
let curve: UInt = (notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber)?.uintValue ?? 7
|
||||
@ -237,54 +262,36 @@ public class Window: UIWindow {
|
||||
|
||||
private func invalidateTracingStatusBars() {
|
||||
self.tracingStatusBarsInvalidated = true
|
||||
self.setNeedsLayout()
|
||||
self.hostView.view.setNeedsLayout()
|
||||
}
|
||||
|
||||
public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
for view in self.hostView.view.subviews.reversed() {
|
||||
if NSStringFromClass(type(of: view)) == "UITransitionView" {
|
||||
if let result = view.hitTest(point, with: event) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let result = self._topLevelOverlayController?.view.hitTest(point, with: event) {
|
||||
return result
|
||||
}
|
||||
|
||||
if let result = self.presentationContext.hitTest(point, with: event) {
|
||||
return result
|
||||
}
|
||||
return self.viewController?.view.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
public override var frame: CGRect {
|
||||
get {
|
||||
return super.frame
|
||||
}
|
||||
set(value) {
|
||||
let sizeUpdated = super.frame.size != value.size
|
||||
super.frame = value
|
||||
|
||||
if sizeUpdated {
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if self.isRotating() {
|
||||
transition = .animated(duration: orientationChangeDuration, curve: .easeInOut)
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
self.updateLayout { $0.update(size: value.size, transition: transition, overrideTransition: true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override var bounds: CGRect {
|
||||
get {
|
||||
return super.frame
|
||||
}
|
||||
set(value) {
|
||||
let sizeUpdated = super.bounds.size != value.size
|
||||
super.bounds = value
|
||||
|
||||
if sizeUpdated {
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if self.isRotating() {
|
||||
transition = .animated(duration: orientationChangeDuration, curve: .easeInOut)
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
self.updateLayout { $0.update(size: value.size, transition: transition, overrideTransition: true) }
|
||||
}
|
||||
func updateSize(_ value: CGSize) {
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if self.hostView.isRotating() {
|
||||
transition = .animated(duration: orientationChangeDuration, curve: .easeInOut)
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
self.updateLayout { $0.update(size: value, transition: transition, overrideTransition: true) }
|
||||
}
|
||||
|
||||
private var _rootController: ContainableController?
|
||||
@ -301,14 +308,33 @@ public class Window: UIWindow {
|
||||
if let rootController = self._rootController {
|
||||
rootController.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: .immediate)
|
||||
|
||||
self.addSubview(rootController.view)
|
||||
self.hostView.view.addSubview(rootController.view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
private var _topLevelOverlayController: ContainableController?
|
||||
public var topLevelOverlayController: ContainableController? {
|
||||
get {
|
||||
return _topLevelOverlayController
|
||||
}
|
||||
set(value) {
|
||||
if let topLevelOverlayController = self._topLevelOverlayController {
|
||||
topLevelOverlayController.view.removeFromSuperview()
|
||||
}
|
||||
self._topLevelOverlayController = value
|
||||
|
||||
if let topLevelOverlayController = self._topLevelOverlayController {
|
||||
topLevelOverlayController.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: .immediate)
|
||||
|
||||
self.hostView.view.addSubview(topLevelOverlayController.view)
|
||||
}
|
||||
|
||||
self.presentationContext.topLevelSubview = self._topLevelOverlayController?.view
|
||||
}
|
||||
}
|
||||
|
||||
private func layoutSubviews() {
|
||||
if self.tracingStatusBarsInvalidated, let statusBarManager = statusBarManager, let keyboardManager = keyboardManager {
|
||||
self.tracingStatusBarsInvalidated = false
|
||||
|
||||
@ -316,7 +342,7 @@ public class Window: UIWindow {
|
||||
statusBarManager.surfaces = []
|
||||
} else {
|
||||
var statusBarSurfaces: [StatusBarSurface] = []
|
||||
for layers in self.layer.traceableLayerSurfaces(withTag: Window.statusBarTracingTag) {
|
||||
for layers in self.hostView.view.layer.traceableLayerSurfaces(withTag: WindowTracingTags.statusBar) {
|
||||
let surface = StatusBarSurface()
|
||||
for layer in layers {
|
||||
let traceableInfo = layer.traceableInfo()
|
||||
@ -326,12 +352,12 @@ public class Window: UIWindow {
|
||||
}
|
||||
statusBarSurfaces.append(surface)
|
||||
}
|
||||
self.layer.adjustTraceableLayerTransforms(CGSize())
|
||||
self.hostView.view.layer.adjustTraceableLayerTransforms(CGSize())
|
||||
statusBarManager.surfaces = statusBarSurfaces
|
||||
}
|
||||
|
||||
var keyboardSurfaces: [KeyboardSurface] = []
|
||||
for layers in self.layer.traceableLayerSurfaces(withTag: Window.keyboardTracingTag) {
|
||||
for layers in self.hostView.view.layer.traceableLayerSurfaces(withTag: WindowTracingTags.keyboard) {
|
||||
for layer in layers {
|
||||
if let view = layer.delegate as? UITracingLayerView {
|
||||
keyboardSurfaces.append(KeyboardSurface(host: view))
|
||||
@ -339,22 +365,23 @@ public class Window: UIWindow {
|
||||
}
|
||||
}
|
||||
keyboardManager.surfaces = keyboardSurfaces
|
||||
self.hostView.updateSupportedInterfaceOrientations(self.presentationContext.combinedSupportedOrientations())
|
||||
}
|
||||
|
||||
if !Window.isDeviceRotating() {
|
||||
if !self.isUpdatingOrientationLayout {
|
||||
if !UIWindow.isDeviceRotating() {
|
||||
if !self.hostView.isUpdatingOrientationLayout {
|
||||
self.commitUpdatingLayout()
|
||||
} else {
|
||||
self.addPostUpdateToInterfaceOrientationBlock(f: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.setNeedsLayout()
|
||||
strongSelf.hostView.view.setNeedsLayout()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Window.addPostDeviceOrientationDidChange({ [weak self] in
|
||||
UIWindow.addPostDeviceOrientationDidChange({ [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.setNeedsLayout()
|
||||
strongSelf.hostView.view.setNeedsLayout()
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -362,11 +389,7 @@ public class Window: UIWindow {
|
||||
|
||||
var postUpdateToInterfaceOrientationBlocks: [(Void) -> Void] = []
|
||||
|
||||
override public func _update(toInterfaceOrientation arg1: Int32, duration arg2: Double, force arg3: Bool) {
|
||||
self.isUpdatingOrientationLayout = true
|
||||
super._update(toInterfaceOrientation: arg1, duration: arg2, force: arg3)
|
||||
self.isUpdatingOrientationLayout = false
|
||||
|
||||
private func updateToInterfaceOrientation() {
|
||||
let blocks = self.postUpdateToInterfaceOrientationBlocks
|
||||
self.postUpdateToInterfaceOrientationBlocks = []
|
||||
for f in blocks {
|
||||
@ -383,14 +406,14 @@ public class Window: UIWindow {
|
||||
self.updatingLayout = UpdatingLayout(layout: self.windowLayout, transition: .immediate)
|
||||
}
|
||||
update(&self.updatingLayout!)
|
||||
self.setNeedsLayout()
|
||||
self.hostView.view.setNeedsLayout()
|
||||
}
|
||||
|
||||
private func commitUpdatingLayout() {
|
||||
if let updatingLayout = self.updatingLayout {
|
||||
self.updatingLayout = nil
|
||||
if updatingLayout.layout != self.windowLayout {
|
||||
var statusBarHeight: CGFloat
|
||||
var statusBarHeight: CGFloat?
|
||||
if let statusBarHost = self.statusBarHost {
|
||||
statusBarHeight = statusBarHost.statusBarFrame.size.height
|
||||
} else {
|
||||
@ -398,14 +421,14 @@ public class Window: UIWindow {
|
||||
}
|
||||
let statusBarWasHidden = self.statusBarHidden
|
||||
if statusBarHiddenInLandscape && updatingLayout.layout.size.width > updatingLayout.layout.size.height {
|
||||
statusBarHeight = 0.0
|
||||
statusBarHeight = nil
|
||||
self.statusBarHidden = true
|
||||
} else {
|
||||
self.statusBarHidden = false
|
||||
}
|
||||
if self.statusBarHidden != statusBarWasHidden {
|
||||
self.tracingStatusBarsInvalidated = true
|
||||
self.setNeedsLayout()
|
||||
self.hostView.view.setNeedsLayout()
|
||||
}
|
||||
self.windowLayout = WindowLayout(size: updatingLayout.layout.size, statusBarHeight: statusBarHeight, inputHeight: updatingLayout.layout.inputHeight, inputMinimized: updatingLayout.layout.inputMinimized)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user