mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Temporary chat list status activity implementation
This commit is contained in:
@@ -312,6 +312,7 @@ public final class ChatListHeaderComponent: Component {
|
||||
|
||||
var contentOffsetFraction: CGFloat = 0.0
|
||||
private(set) var centerContentWidth: CGFloat = 0.0
|
||||
private(set) var centerContentLeftInset: CGFloat = 0.0
|
||||
private(set) var centerContentRightInset: CGFloat = 0.0
|
||||
|
||||
private(set) var centerContentOffsetX: CGFloat = 0.0
|
||||
@@ -639,8 +640,11 @@ public final class ChatListHeaderComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
var centerContentLeftInset: CGFloat = 0.0
|
||||
centerContentLeftInset = leftOffset - 4.0
|
||||
|
||||
var centerContentRightInset: CGFloat = 0.0
|
||||
centerContentRightInset = size.width - rightOffset + 16.0
|
||||
centerContentRightInset = size.width - rightOffset - 8.0
|
||||
|
||||
var centerContentWidth: CGFloat = 0.0
|
||||
var centerContentOffsetX: CGFloat = 0.0
|
||||
@@ -705,6 +709,7 @@ public final class ChatListHeaderComponent: Component {
|
||||
self.centerContentOffsetX = centerContentOffsetX
|
||||
self.centerContentOrigin = centerContentOrigin
|
||||
self.centerContentRightInset = centerContentRightInset
|
||||
self.centerContentLeftInset = centerContentLeftInset
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,7 +820,7 @@ public final class ChatListHeaderComponent: Component {
|
||||
let previousComponent = self.component
|
||||
self.component = component
|
||||
|
||||
if let primaryContent = component.primaryContent {
|
||||
if var primaryContent = component.primaryContent {
|
||||
var primaryContentTransition = transition
|
||||
let primaryContentView: ContentView
|
||||
if let current = self.primaryContentView {
|
||||
@@ -848,6 +853,19 @@ public final class ChatListHeaderComponent: Component {
|
||||
|
||||
let sideContentWidth: CGFloat = 0.0
|
||||
|
||||
if component.storySubscriptions != nil {
|
||||
primaryContent = Content(
|
||||
title: "",
|
||||
navigationBackTitle: primaryContent.navigationBackTitle,
|
||||
titleComponent: nil,
|
||||
chatListTitle: nil,
|
||||
leftButton: primaryContent.leftButton,
|
||||
rightButtons: primaryContent.rightButtons,
|
||||
backTitle: primaryContent.backTitle,
|
||||
backPressed: primaryContent.backPressed
|
||||
)
|
||||
}
|
||||
|
||||
primaryContentView.update(context: component.context, theme: component.theme, strings: component.strings, content: primaryContent, backTitle: primaryContent.backTitle, sideInset: component.sideInset, sideContentWidth: sideContentWidth, sideContentFraction: (1.0 - component.storiesFraction), size: availableSize, transition: primaryContentTransition)
|
||||
primaryContentTransition.setFrame(view: primaryContentView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
|
||||
@@ -868,6 +886,28 @@ public final class ChatListHeaderComponent: Component {
|
||||
self.storyPeerList = storyPeerList
|
||||
}
|
||||
|
||||
var primaryTitle = ""
|
||||
var primaryTitleHasLock = false
|
||||
var primaryTitleHasActivity = false
|
||||
var primaryTitlePeerStatus: StoryPeerListComponent.PeerStatus?
|
||||
if let primaryContent = component.primaryContent {
|
||||
if let chatListTitle = primaryContent.chatListTitle {
|
||||
primaryTitle = chatListTitle.text
|
||||
primaryTitleHasLock = chatListTitle.isPasscodeSet
|
||||
primaryTitleHasActivity = chatListTitle.activity
|
||||
if let peerStatus = chatListTitle.peerStatus {
|
||||
switch peerStatus {
|
||||
case .premium:
|
||||
primaryTitlePeerStatus = .premium
|
||||
case let .emoji(status):
|
||||
primaryTitlePeerStatus = .emoji(status)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
primaryTitle = primaryContent.title
|
||||
}
|
||||
}
|
||||
|
||||
let _ = storyPeerList.update(
|
||||
transition: storyListTransition,
|
||||
component: AnyComponent(StoryPeerListComponent(
|
||||
@@ -876,7 +916,11 @@ public final class ChatListHeaderComponent: Component {
|
||||
theme: component.theme,
|
||||
strings: component.strings,
|
||||
sideInset: component.sideInset,
|
||||
titleContentWidth: self.primaryContentView?.centerContentWidth ?? 0.0,
|
||||
title: primaryTitle,
|
||||
titleHasLock: primaryTitleHasLock,
|
||||
titleHasActivity: primaryTitleHasActivity,
|
||||
titlePeerStatus: primaryTitlePeerStatus,
|
||||
minTitleX: self.primaryContentView?.centerContentLeftInset ?? 0.0,
|
||||
maxTitleX: availableSize.width - (self.primaryContentView?.centerContentRightInset ?? 0.0),
|
||||
useHiddenList: component.storiesIncludeHidden,
|
||||
storySubscriptions: storySubscriptions,
|
||||
@@ -895,14 +939,11 @@ public final class ChatListHeaderComponent: Component {
|
||||
}
|
||||
self.storyContextPeerAction?(sourceNode, gesture, peer)
|
||||
},
|
||||
updateTitleContentOffset: { [weak self] offset, transition in
|
||||
guard let self, let primaryContentView = self.primaryContentView else {
|
||||
openStatusSetup: { [weak self] sourceView in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let chatListTitleView = primaryContentView.chatListTitleView else {
|
||||
return
|
||||
}
|
||||
transition.setSublayerTransform(view: chatListTitleView, transform: CATransform3DMakeTranslation(offset, 0.0, 0.0))
|
||||
self.component?.openStatusSetup(sourceView)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
@@ -1002,12 +1043,7 @@ public final class ChatListHeaderComponent: Component {
|
||||
|
||||
storyListTransition.setFrame(view: storyPeerListComponentView, frame: CGRect(origin: CGPoint(x: -1.0 * availableSize.width * component.secondaryTransition + 0.0, y: storyPeerListMaxOffset), size: CGSize(width: availableSize.width, height: 79.0)))
|
||||
|
||||
var storyListNormalAlpha: CGFloat = 1.0
|
||||
if let chatListTitle = component.primaryContent?.chatListTitle {
|
||||
if chatListTitle.activity {
|
||||
storyListNormalAlpha = component.storiesFraction
|
||||
}
|
||||
}
|
||||
let storyListNormalAlpha: CGFloat = 1.0
|
||||
|
||||
let storyListAlpha: CGFloat = (1.0 - component.secondaryTransition) * storyListNormalAlpha
|
||||
storyListTransition.setAlpha(view: storyPeerListComponentView, alpha: storyListAlpha)
|
||||
|
||||
@@ -22,6 +22,8 @@ swift_library(
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
||||
"//submodules/Components/MultilineTextComponent",
|
||||
"//submodules/ActivityIndicator",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@@ -10,6 +10,7 @@ import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import StoryContainerScreen
|
||||
import EmojiStatusComponent
|
||||
|
||||
public func shouldDisplayStoriesInChatListHeader(storySubscriptions: EngineStorySubscriptions) -> Bool {
|
||||
if !storySubscriptions.items.isEmpty {
|
||||
@@ -48,6 +49,11 @@ private let modelSpringAnimation: CABasicAnimation = {
|
||||
}()
|
||||
|
||||
public final class StoryPeerListComponent: Component {
|
||||
public enum PeerStatus: Equatable {
|
||||
case premium
|
||||
case emoji(PeerEmojiStatus)
|
||||
}
|
||||
|
||||
public final class ExternalState {
|
||||
public fileprivate(set) var collapsedWidth: CGFloat = 0.0
|
||||
|
||||
@@ -74,7 +80,11 @@ public final class StoryPeerListComponent: Component {
|
||||
public let theme: PresentationTheme
|
||||
public let strings: PresentationStrings
|
||||
public let sideInset: CGFloat
|
||||
public let titleContentWidth: CGFloat
|
||||
public let title: String
|
||||
public let titleHasLock: Bool
|
||||
public let titleHasActivity: Bool
|
||||
public let titlePeerStatus: PeerStatus?
|
||||
public let minTitleX: CGFloat
|
||||
public let maxTitleX: CGFloat
|
||||
public let useHiddenList: Bool
|
||||
public let storySubscriptions: EngineStorySubscriptions?
|
||||
@@ -83,7 +93,7 @@ public final class StoryPeerListComponent: Component {
|
||||
public let uploadProgress: Float?
|
||||
public let peerAction: (EnginePeer?) -> Void
|
||||
public let contextPeerAction: (ContextExtractedContentContainingNode, ContextGesture, EnginePeer) -> Void
|
||||
public let updateTitleContentOffset: (CGFloat, Transition) -> Void
|
||||
public let openStatusSetup: (UIView) -> Void
|
||||
|
||||
public init(
|
||||
externalState: ExternalState,
|
||||
@@ -91,7 +101,11 @@ public final class StoryPeerListComponent: Component {
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
sideInset: CGFloat,
|
||||
titleContentWidth: CGFloat,
|
||||
title: String,
|
||||
titleHasLock: Bool,
|
||||
titleHasActivity: Bool,
|
||||
titlePeerStatus: PeerStatus?,
|
||||
minTitleX: CGFloat,
|
||||
maxTitleX: CGFloat,
|
||||
useHiddenList: Bool,
|
||||
storySubscriptions: EngineStorySubscriptions?,
|
||||
@@ -100,14 +114,18 @@ public final class StoryPeerListComponent: Component {
|
||||
uploadProgress: Float?,
|
||||
peerAction: @escaping (EnginePeer?) -> Void,
|
||||
contextPeerAction: @escaping (ContextExtractedContentContainingNode, ContextGesture, EnginePeer) -> Void,
|
||||
updateTitleContentOffset: @escaping (CGFloat, Transition) -> Void
|
||||
openStatusSetup: @escaping (UIView) -> Void
|
||||
) {
|
||||
self.externalState = externalState
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.sideInset = sideInset
|
||||
self.titleContentWidth = titleContentWidth
|
||||
self.title = title
|
||||
self.titleHasLock = titleHasLock
|
||||
self.titleHasActivity = titleHasActivity
|
||||
self.titlePeerStatus = titlePeerStatus
|
||||
self.minTitleX = minTitleX
|
||||
self.maxTitleX = maxTitleX
|
||||
self.useHiddenList = useHiddenList
|
||||
self.storySubscriptions = storySubscriptions
|
||||
@@ -116,7 +134,7 @@ public final class StoryPeerListComponent: Component {
|
||||
self.uploadProgress = uploadProgress
|
||||
self.peerAction = peerAction
|
||||
self.contextPeerAction = contextPeerAction
|
||||
self.updateTitleContentOffset = updateTitleContentOffset
|
||||
self.openStatusSetup = openStatusSetup
|
||||
}
|
||||
|
||||
public static func ==(lhs: StoryPeerListComponent, rhs: StoryPeerListComponent) -> Bool {
|
||||
@@ -132,7 +150,19 @@ public final class StoryPeerListComponent: Component {
|
||||
if lhs.sideInset != rhs.sideInset {
|
||||
return false
|
||||
}
|
||||
if lhs.titleContentWidth != rhs.titleContentWidth {
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.titleHasLock != rhs.titleHasLock {
|
||||
return false
|
||||
}
|
||||
if lhs.titleHasActivity != rhs.titleHasActivity {
|
||||
return false
|
||||
}
|
||||
if lhs.titlePeerStatus != rhs.titlePeerStatus {
|
||||
return false
|
||||
}
|
||||
if lhs.minTitleX != rhs.minTitleX {
|
||||
return false
|
||||
}
|
||||
if lhs.maxTitleX != rhs.maxTitleX {
|
||||
@@ -261,6 +291,10 @@ public final class StoryPeerListComponent: Component {
|
||||
private var visibleItems: [EnginePeer.Id: VisibleItem] = [:]
|
||||
private var visibleCollapsableItems: [EnginePeer.Id: VisibleItem] = [:]
|
||||
|
||||
private var titleIndicatorView: ComponentView<Empty>?
|
||||
private let titleView = ComponentView<Empty>()
|
||||
private var titleIconView: ComponentView<Empty>?
|
||||
|
||||
private var component: StoryPeerListComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
@@ -401,11 +435,107 @@ public final class StoryPeerListComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
var hasStories: Bool = false
|
||||
if let storySubscriptions = component.storySubscriptions, shouldDisplayStoriesInChatListHeader(storySubscriptions: storySubscriptions) {
|
||||
hasStories = true
|
||||
let titleIconSpacing: CGFloat = 4.0
|
||||
let titleIndicatorSpacing: CGFloat = 8.0
|
||||
|
||||
var titleContentWidth: CGFloat = 0.0
|
||||
|
||||
var titleIndicatorSize: CGSize?
|
||||
if component.titleHasActivity {
|
||||
let titleIndicatorView: ComponentView<Empty>
|
||||
if let current = self.titleIndicatorView {
|
||||
titleIndicatorView = current
|
||||
} else {
|
||||
titleIndicatorView = ComponentView()
|
||||
self.titleIndicatorView = titleIndicatorView
|
||||
}
|
||||
let titleIndicatorSizeValue = titleIndicatorView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(TitleActivityIndicatorComponent(
|
||||
color: component.theme.rootController.navigationBar.accentTextColor
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 22.0, height: 22.0)
|
||||
)
|
||||
titleIndicatorSize = titleIndicatorSizeValue
|
||||
titleContentWidth += titleIndicatorSizeValue.width + titleIndicatorSpacing
|
||||
} else {
|
||||
if let titleIndicatorView = self.titleIndicatorView {
|
||||
self.titleIndicatorView = nil
|
||||
titleIndicatorView.view?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
let titleSize = self.titleView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(Text(text: component.title, font: Font.semibold(17.0), color: component.theme.rootController.navigationBar.primaryTextColor)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 200.0, height: 100.0)
|
||||
)
|
||||
titleContentWidth += titleSize.width
|
||||
|
||||
var titleIconSize: CGSize?
|
||||
if let peerStatus = component.titlePeerStatus {
|
||||
let statusContent: EmojiStatusComponent.Content
|
||||
switch peerStatus {
|
||||
case .premium:
|
||||
statusContent = .premium(color: component.theme.list.itemAccentColor)
|
||||
case let .emoji(emoji):
|
||||
statusContent = .animation(content: .customEmoji(fileId: emoji.fileId), size: CGSize(width: 22.0, height: 22.0), placeholderColor: component.theme.list.mediaPlaceholderColor, themeColor: component.theme.list.itemAccentColor, loopMode: .count(2))
|
||||
}
|
||||
|
||||
var animateStatusTransition = false
|
||||
|
||||
let titleIconView: ComponentView<Empty>
|
||||
if let current = self.titleIconView {
|
||||
animateStatusTransition = true
|
||||
titleIconView = current
|
||||
} else {
|
||||
titleIconView = ComponentView()
|
||||
self.titleIconView = titleIconView
|
||||
}
|
||||
|
||||
var titleIconTransition: Transition
|
||||
if animateStatusTransition {
|
||||
titleIconTransition = Transition(animation: .curve(duration: 0.2, curve: .easeInOut))
|
||||
} else {
|
||||
titleIconTransition = .immediate
|
||||
}
|
||||
|
||||
let titleIconSizeValue = titleIconView.update(
|
||||
transition: titleIconTransition,
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: component.context,
|
||||
animationCache: component.context.animationCache,
|
||||
animationRenderer: component.context.animationRenderer,
|
||||
content: statusContent,
|
||||
isVisibleForAnimations: true,
|
||||
action: { [weak self] in
|
||||
guard let self, let component = self.component, let titleIconView = self.titleIconView?.view else {
|
||||
return
|
||||
}
|
||||
component.openStatusSetup(titleIconView)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 22.0, height: 22.0)
|
||||
)
|
||||
|
||||
titleIconSize = titleIconSizeValue
|
||||
|
||||
if let titleIconComponentView = titleIconView.view {
|
||||
titleIconComponentView.isHidden = component.titleHasActivity
|
||||
}
|
||||
|
||||
if !component.titleHasActivity {
|
||||
titleContentWidth += titleIconSpacing + titleIconSizeValue.width
|
||||
}
|
||||
} else {
|
||||
if let titleIconView = self.titleIconView {
|
||||
self.titleIconView = nil
|
||||
titleIconView.view?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
let _ = hasStories
|
||||
|
||||
let collapseStartIndex: Int
|
||||
if component.useHiddenList {
|
||||
@@ -434,16 +564,26 @@ public final class StoryPeerListComponent: Component {
|
||||
let collapsedItemOffsetY: CGFloat
|
||||
|
||||
let titleContentSpacing: CGFloat = 8.0
|
||||
var combinedTitleContentWidth = component.titleContentWidth
|
||||
var combinedTitleContentWidth = titleContentWidth
|
||||
if !combinedTitleContentWidth.isZero {
|
||||
combinedTitleContentWidth += titleContentSpacing
|
||||
}
|
||||
let centralContentWidth: CGFloat = collapsedContentWidth + combinedTitleContentWidth
|
||||
|
||||
let centralContentWidth: CGFloat
|
||||
centralContentWidth = collapsedContentWidth + combinedTitleContentWidth
|
||||
|
||||
collapsedContentOrigin = floor((itemLayout.containerSize.width - centralContentWidth) * 0.5)
|
||||
|
||||
if component.titleHasActivity {
|
||||
collapsedContentOrigin -= (collapsedContentWidth + titleContentSpacing) * 0.5
|
||||
}
|
||||
|
||||
collapsedContentOrigin = min(collapsedContentOrigin, component.maxTitleX - centralContentWidth - 4.0)
|
||||
|
||||
var collapsedContentOriginOffset: CGFloat = 0.0
|
||||
|
||||
if itemLayout.itemCount == 1 && collapsedContentWidth <= 0.1 {
|
||||
collapsedContentOriginOffset = 4.0
|
||||
collapsedContentOriginOffset += 4.0
|
||||
}
|
||||
collapsedContentOrigin -= collapsedContentOriginOffset
|
||||
collapsedItemOffsetY = -59.0
|
||||
@@ -703,7 +843,7 @@ public final class StoryPeerListComponent: Component {
|
||||
rightItemFrame = calculateItem(i + 1).itemFrame
|
||||
}
|
||||
|
||||
if effectiveFirstVisibleIndex == 0 {
|
||||
if effectiveFirstVisibleIndex == 0 && !component.titleHasActivity {
|
||||
itemAlpha = 1.0
|
||||
} else {
|
||||
itemAlpha = collapsedState.sideAlphaFraction
|
||||
@@ -854,6 +994,10 @@ public final class StoryPeerListComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
if component.titleHasActivity {
|
||||
itemAlpha = 0.0
|
||||
}
|
||||
|
||||
var leftNeighborDistance: CGPoint?
|
||||
var rightNeighborDistance: CGPoint?
|
||||
|
||||
@@ -943,9 +1087,9 @@ public final class StoryPeerListComponent: Component {
|
||||
self.visibleCollapsableItems.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.collapsedButton, frame: CGRect(origin: CGPoint(x: collapsedContentOrigin - 4.0, y: 6.0 - 59.0), size: CGSize(width: collapsedContentWidth + 4.0, height: 44.0)))
|
||||
transition.setFrame(view: self.collapsedButton, frame: CGRect(origin: CGPoint(x: component.minTitleX, y: 6.0 - 59.0), size: CGSize(width: max(0.0, component.maxTitleX - component.minTitleX), height: 44.0)))
|
||||
|
||||
let defaultCollapsedTitleOffset = floor((itemLayout.containerSize.width - component.titleContentWidth) * 0.5)
|
||||
let defaultCollapsedTitleOffset: CGFloat = 0.0
|
||||
|
||||
var targetCollapsedTitleOffset: CGFloat = collapsedContentOrigin + collapsedContentOriginOffset + collapsedContentWidth + titleContentSpacing
|
||||
if itemLayout.itemCount == 1 && collapsedContentWidth <= 0.1 {
|
||||
@@ -956,15 +1100,50 @@ public final class StoryPeerListComponent: Component {
|
||||
let collapsedTitleOffset = targetCollapsedTitleOffset - defaultCollapsedTitleOffset
|
||||
|
||||
let titleMinContentOffset: CGFloat = collapsedTitleOffset.interpolate(to: collapsedTitleOffset + 12.0, amount: collapsedState.minFraction)
|
||||
let titleContentOffset: CGFloat = titleMinContentOffset.interpolate(to: 0.0 as CGFloat, amount: collapsedState.maxFraction)
|
||||
var titleContentOffset: CGFloat = titleMinContentOffset.interpolate(to: floor((itemLayout.containerSize.width - titleContentWidth) * 0.5) as CGFloat, amount: collapsedState.maxFraction)
|
||||
|
||||
component.updateTitleContentOffset(titleContentOffset, transition)
|
||||
if let titleIndicatorSize, let titleIndicatorView = self.titleIndicatorView?.view {
|
||||
let titleIndicatorFrame = CGRect(origin: CGPoint(x: titleContentOffset, y: collapsedItemOffsetY + 1.0 + floor((56.0 - titleIndicatorSize.height) * 0.5)), size: titleIndicatorSize)
|
||||
if titleIndicatorView.superview == nil {
|
||||
self.addSubview(titleIndicatorView)
|
||||
}
|
||||
titleIndicatorView.frame = titleIndicatorFrame
|
||||
titleContentOffset += titleIndicatorSize.width + titleIndicatorSpacing
|
||||
}
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: titleContentOffset, y: collapsedItemOffsetY + 1.0 + floor((56.0 - titleSize.height) * 0.5)), size: titleSize)
|
||||
if let titleComponentView = self.titleView.view {
|
||||
if titleComponentView.superview == nil {
|
||||
titleComponentView.isUserInteractionEnabled = false
|
||||
self.addSubview(titleComponentView)
|
||||
}
|
||||
titleComponentView.frame = titleFrame
|
||||
}
|
||||
titleContentOffset += titleSize.width
|
||||
|
||||
if let titleIconSize, let titleIconView = self.titleIconView?.view {
|
||||
titleContentOffset += titleIconSpacing
|
||||
|
||||
let titleIconFrame = CGRect(origin: CGPoint(x: titleContentOffset, y: collapsedItemOffsetY + 1.0 + floor((56.0 - titleIconSize.height) * 0.5)), size: titleIconSize)
|
||||
|
||||
if titleIconView.superview == nil {
|
||||
self.addSubview(titleIconView)
|
||||
}
|
||||
titleIconView.frame = titleIconFrame
|
||||
}
|
||||
}
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.alpha.isZero {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let titleIconView = self.titleIconView?.view {
|
||||
if let result = titleIconView.hitTest(self.convert(point, to: titleIconView), with: event) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
var result: UIView?
|
||||
for view in self.subviews.reversed() {
|
||||
if let resultValue = view.hitTest(self.convert(point, to: view), with: event), resultValue.isUserInteractionEnabled {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import ActivityIndicator
|
||||
|
||||
public final class TitleActivityIndicatorComponent: Component {
|
||||
let color: UIColor
|
||||
|
||||
public init(
|
||||
color: UIColor
|
||||
) {
|
||||
self.color = color
|
||||
}
|
||||
|
||||
public static func ==(lhs: TitleActivityIndicatorComponent, rhs: TitleActivityIndicatorComponent) -> Bool {
|
||||
if lhs.color != rhs.color {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private var activityIndicator: ActivityIndicator?
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
func update(component: TitleActivityIndicatorComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
let activityIndicator: ActivityIndicator
|
||||
if let current = self.activityIndicator {
|
||||
activityIndicator = current
|
||||
} else {
|
||||
activityIndicator = ActivityIndicator(type: .custom(component.color, availableSize.width, 2.0, true))
|
||||
self.activityIndicator = activityIndicator
|
||||
self.addSubview(activityIndicator.view)
|
||||
}
|
||||
|
||||
activityIndicator.frame = CGRect(origin: CGPoint(), size: availableSize)
|
||||
activityIndicator.isHidden = false
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
public func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user