This commit is contained in:
HiBit 2025-11-26 09:17:04 +08:00
parent 8e142ebca7
commit 967534d926
71 changed files with 1800 additions and 52 deletions

View File

@ -108,6 +108,21 @@
370D2F092ED44A6D00571E77 /* SRListMenuCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F082ED44A6D00571E77 /* SRListMenuCell.swift */; };
370D2F0C2ED44ACB00571E77 /* SRListMenuDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F0B2ED44ACB00571E77 /* SRListMenuDataSource.swift */; };
370D2F102ED4534500571E77 /* SRUserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F0F2ED4534500571E77 /* SRUserViewController.swift */; };
370D2F152ED457F000571E77 /* SRUserTopCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F142ED457F000571E77 /* SRUserTopCell.swift */; };
370D2F182ED45B3000571E77 /* SRUserSettingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F172ED45B3000571E77 /* SRUserSettingModel.swift */; };
370D2F1A2ED45CCA00571E77 /* SRUserSettingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F192ED45CCA00571E77 /* SRUserSettingCell.swift */; };
370D2F1C2ED4770800571E77 /* SRUserInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F1B2ED4770800571E77 /* SRUserInfoModel.swift */; };
370D2F1E2ED54C7F00571E77 /* SRHelpCenterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F1D2ED54C7F00571E77 /* SRHelpCenterController.swift */; };
370D2F202ED54C8F00571E77 /* SRAboutUsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F1F2ED54C8F00571E77 /* SRAboutUsController.swift */; };
370D2F222ED54CA400571E77 /* SRPrivacyController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F212ED54CA400571E77 /* SRPrivacyController.swift */; };
370D2F252ED5807600571E77 /* SRAboutHeaderVIew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F242ED5807600571E77 /* SRAboutHeaderVIew.swift */; };
370D2F272ED581BB00571E77 /* SRAboutCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F262ED581BB00571E77 /* SRAboutCell.swift */; };
370D2F292ED58EC400571E77 /* SRTopChartsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F282ED58EC400571E77 /* SRTopChartsViewController.swift */; };
370D2F2B2ED597F700571E77 /* SRTopChartsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F2A2ED597F700571E77 /* SRTopChartsCell.swift */; };
370D2F2D2ED5AA9700571E77 /* SRViralHitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F2C2ED5AA9700571E77 /* SRViralHitCell.swift */; };
370D2F2F2ED5AB2500571E77 /* SRViralHitController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D2F2E2ED5AB2500571E77 /* SRViralHitController.swift */; };
3754ACBD2ED5B6B6009EBCAD /* UINavigationBar+SRAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754ACBC2ED5B6B6009EBCAD /* UINavigationBar+SRAdd.swift */; };
3754ACBF2ED5B839009EBCAD /* SRNavgationTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754ACBE2ED5B839009EBCAD /* SRNavgationTitleView.swift */; };
3779D0612ECF1CB8006B1698 /* SRShortHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3779D0602ECF1CB8006B1698 /* SRShortHeaderView.swift */; };
47BB39E2DD30787FA591F8EB /* Pods_SynthReel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9255BF4D4B1CFDDB5CFFB43 /* Pods_SynthReel.framework */; };
/* End PBXBuildFile section */
@ -215,6 +230,21 @@
370D2F082ED44A6D00571E77 /* SRListMenuCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRListMenuCell.swift; sourceTree = "<group>"; };
370D2F0B2ED44ACB00571E77 /* SRListMenuDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRListMenuDataSource.swift; sourceTree = "<group>"; };
370D2F0F2ED4534500571E77 /* SRUserViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserViewController.swift; sourceTree = "<group>"; };
370D2F142ED457F000571E77 /* SRUserTopCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserTopCell.swift; sourceTree = "<group>"; };
370D2F172ED45B3000571E77 /* SRUserSettingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserSettingModel.swift; sourceTree = "<group>"; };
370D2F192ED45CCA00571E77 /* SRUserSettingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserSettingCell.swift; sourceTree = "<group>"; };
370D2F1B2ED4770800571E77 /* SRUserInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserInfoModel.swift; sourceTree = "<group>"; };
370D2F1D2ED54C7F00571E77 /* SRHelpCenterController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRHelpCenterController.swift; sourceTree = "<group>"; };
370D2F1F2ED54C8F00571E77 /* SRAboutUsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRAboutUsController.swift; sourceTree = "<group>"; };
370D2F212ED54CA400571E77 /* SRPrivacyController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRPrivacyController.swift; sourceTree = "<group>"; };
370D2F242ED5807600571E77 /* SRAboutHeaderVIew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRAboutHeaderVIew.swift; sourceTree = "<group>"; };
370D2F262ED581BB00571E77 /* SRAboutCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRAboutCell.swift; sourceTree = "<group>"; };
370D2F282ED58EC400571E77 /* SRTopChartsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRTopChartsViewController.swift; sourceTree = "<group>"; };
370D2F2A2ED597F700571E77 /* SRTopChartsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRTopChartsCell.swift; sourceTree = "<group>"; };
370D2F2C2ED5AA9700571E77 /* SRViralHitCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRViralHitCell.swift; sourceTree = "<group>"; };
370D2F2E2ED5AB2500571E77 /* SRViralHitController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRViralHitController.swift; sourceTree = "<group>"; };
3754ACBC2ED5B6B6009EBCAD /* UINavigationBar+SRAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+SRAdd.swift"; sourceTree = "<group>"; };
3754ACBE2ED5B839009EBCAD /* SRNavgationTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRNavgationTitleView.swift; sourceTree = "<group>"; };
3779D0602ECF1CB8006B1698 /* SRShortHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRShortHeaderView.swift; sourceTree = "<group>"; };
59DC746604B26E9FF802D317 /* Pods-SynthReel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SynthReel.debug.xcconfig"; path = "Target Support Files/Pods-SynthReel/Pods-SynthReel.debug.xcconfig"; sourceTree = "<group>"; };
AA88214030574193B51DE563 /* Pods-SynthReel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SynthReel.release.xcconfig"; path = "Target Support Files/Pods-SynthReel/Pods-SynthReel.release.xcconfig"; sourceTree = "<group>"; };
@ -299,6 +329,8 @@
03B1A84F2EC5DB2E006C353F /* SRHomeViewController.swift */,
03B1A8D42EC6CF37006C353F /* SRHomeChildViewController.swift */,
03B1A92F2ECC10D1006C353F /* SRSearchViewController.swift */,
370D2F282ED58EC400571E77 /* SRTopChartsViewController.swift */,
370D2F2E2ED5AB2500571E77 /* SRViralHitController.swift */,
);
path = VC;
sourceTree = "<group>";
@ -321,6 +353,7 @@
03B1A93D2ECC4568006C353F /* SRTableView.swift */,
03B1A93F2ECC45BA006C353F /* SRTableViewCell.swift */,
03B1A94B2ECC7A2D006C353F /* SRPanModalContentView.swift */,
3754ACBE2ED5B839009EBCAD /* SRNavgationTitleView.swift */,
);
path = View;
sourceTree = "<group>";
@ -354,6 +387,8 @@
03B1A9412ECC4632006C353F /* SRHotSearchCell.swift */,
03B1A9432ECC4ED9006C353F /* SRSearchResultView.swift */,
03B1A9452ECC5679006C353F /* SRSearchResultCell.swift */,
370D2F2A2ED597F700571E77 /* SRTopChartsCell.swift */,
370D2F2C2ED5AA9700571E77 /* SRViralHitCell.swift */,
);
path = V;
sourceTree = "<group>";
@ -569,6 +604,7 @@
03E9A7F12EC4A8EC000D1067 /* UserDefaults+SRAdd.swift */,
03B1A91A2ECAFFCB006C353F /* UIView+SRAdd.swift */,
03B1A91C2ECB241B006C353F /* UIScrollView+SRAdd.swift */,
3754ACBC2ED5B6B6009EBCAD /* UINavigationBar+SRAdd.swift */,
);
path = Extension;
sourceTree = "<group>";
@ -594,6 +630,8 @@
370D2F0D2ED4532100571E77 /* User */ = {
isa = PBXGroup;
children = (
370D2F162ED45B1C00571E77 /* model */,
370D2F132ED457C900571E77 /* view */,
370D2F0E2ED4532A00571E77 /* VC */,
);
path = User;
@ -603,10 +641,33 @@
isa = PBXGroup;
children = (
370D2F0F2ED4534500571E77 /* SRUserViewController.swift */,
370D2F1D2ED54C7F00571E77 /* SRHelpCenterController.swift */,
370D2F1F2ED54C8F00571E77 /* SRAboutUsController.swift */,
370D2F212ED54CA400571E77 /* SRPrivacyController.swift */,
);
path = VC;
sourceTree = "<group>";
};
370D2F132ED457C900571E77 /* view */ = {
isa = PBXGroup;
children = (
370D2F142ED457F000571E77 /* SRUserTopCell.swift */,
370D2F192ED45CCA00571E77 /* SRUserSettingCell.swift */,
370D2F242ED5807600571E77 /* SRAboutHeaderVIew.swift */,
370D2F262ED581BB00571E77 /* SRAboutCell.swift */,
);
path = view;
sourceTree = "<group>";
};
370D2F162ED45B1C00571E77 /* model */ = {
isa = PBXGroup;
children = (
370D2F172ED45B3000571E77 /* SRUserSettingModel.swift */,
370D2F1B2ED4770800571E77 /* SRUserInfoModel.swift */,
);
path = model;
sourceTree = "<group>";
};
3779D05F2ECF1C8D006B1698 /* V */ = {
isa = PBXGroup;
children = (
@ -761,7 +822,9 @@
03B1A9402ECC45BA006C353F /* SRTableViewCell.swift in Sources */,
03E9A7EA2EC4995D000D1067 /* SRKeychain.swift in Sources */,
03E9A7D92EC47B90000D1067 /* SRNetworkReachableManager.swift in Sources */,
370D2F2D2ED5AA9700571E77 /* SRViralHitCell.swift in Sources */,
03B1A8532EC5E12E006C353F /* UIScreen+SRAdd.swift in Sources */,
370D2F182ED45B3000571E77 /* SRUserSettingModel.swift in Sources */,
03B1A8FB2EC818BE006C353F /* UIStackView+SRAdd.swift in Sources */,
03B1A8E12EC6D6D3006C353F /* SRHomeMenuDataSource.swift in Sources */,
03E9A7F82EC4AA54000D1067 /* SRUserDefaultsKey.swift in Sources */,
@ -775,16 +838,22 @@
03B1A9172ECAF14F006C353F /* SRHomeHotView.swift in Sources */,
03B1A8FD2EC81C62006C353F /* SRHomeBannerCell.swift in Sources */,
03E9A7DB2EC485BE000D1067 /* SRNetwork.swift in Sources */,
370D2F152ED457F000571E77 /* SRUserTopCell.swift in Sources */,
3754ACBD2ED5B6B6009EBCAD /* UINavigationBar+SRAdd.swift in Sources */,
03E9A7F42EC4A94D000D1067 /* SRAccountToken.swift in Sources */,
370D2F222ED54CA400571E77 /* SRPrivacyController.swift in Sources */,
370D2EF72ED000C400571E77 /* SRFavoritesViewController.swift in Sources */,
370D2F072ED3FEFA00571E77 /* SRFavoritesCell.swift in Sources */,
03B1A8E72EC7175D006C353F /* SRCategoryModel.swift in Sources */,
370D2F2F2ED5AB2500571E77 /* SRViralHitController.swift in Sources */,
03B1A94A2ECC79AB006C353F /* SREpSelectorView.swift in Sources */,
03B1A94E2ECD604B006C353F /* SREpSelectorCell.swift in Sources */,
03B1A9262ECBFF31006C353F /* SRShortPlayerViewModel.swift in Sources */,
370D2F202ED54C8F00571E77 /* SRAboutUsController.swift in Sources */,
03B1A9322ECC1167006C353F /* SRSearchHomeView.swift in Sources */,
03B1A8F32EC809C5006C353F /* SRHomeHeaderView.swift in Sources */,
03B1A9482ECC6669006C353F /* SRProgressView.swift in Sources */,
370D2F1A2ED45CCA00571E77 /* SRUserSettingCell.swift in Sources */,
03B1A91D2ECB2424006C353F /* UIScrollView+SRAdd.swift in Sources */,
03E9A7C92EC47177000D1067 /* AppDelegate.swift in Sources */,
03E9A7FA2EC56D03000D1067 /* String+SRAdd.swift in Sources */,
@ -797,7 +866,10 @@
03980F532ECEDEAB0006E317 /* SRRecommendPlayerControlView.swift in Sources */,
03B1A90F2ECAC768006C353F /* SRHomeBingeWorthyView.swift in Sources */,
03B1A9072EC86656006C353F /* SRGradientView.swift in Sources */,
370D2F292ED58EC400571E77 /* SRTopChartsViewController.swift in Sources */,
370D2F1E2ED54C7F00571E77 /* SRHelpCenterController.swift in Sources */,
03E9A7CA2EC47177000D1067 /* SceneDelegate.swift in Sources */,
370D2F252ED5807600571E77 /* SRAboutHeaderVIew.swift in Sources */,
03B1A8402EC5CA37006C353F /* AppDelegate+Config.swift in Sources */,
03B1A9152ECAEE63006C353F /* SRHomeViralHitsCell.swift in Sources */,
03B1A8472EC5CBCF006C353F /* SRViewController.swift in Sources */,
@ -808,6 +880,7 @@
03B1A90D2ECAC51A006C353F /* NSNumber+SRAdd.swift in Sources */,
03E9A7DD2EC485E1000D1067 /* SRNetworkModel.swift in Sources */,
03B1A9442ECC4ED9006C353F /* SRSearchResultView.swift in Sources */,
3754ACBF2ED5B839009EBCAD /* SRNavgationTitleView.swift in Sources */,
03B1A8FF2EC81C92006C353F /* SRImageView.swift in Sources */,
03B1A83B2EC5C8E0006C353F /* SRToast.swift in Sources */,
03B1A9302ECC10D1006C353F /* SRSearchViewController.swift in Sources */,
@ -817,6 +890,7 @@
370D2F102ED4534500571E77 /* SRUserViewController.swift in Sources */,
03B1A9112ECAC927006C353F /* SRHomeBingeWorthyCell.swift in Sources */,
03E9A7F22EC4A8F6000D1067 /* UserDefaults+SRAdd.swift in Sources */,
370D2F2B2ED597F700571E77 /* SRTopChartsCell.swift in Sources */,
03B1A93A2ECC3F54006C353F /* SRSearchViewModel.swift in Sources */,
03E9A7F62EC4A9B1000D1067 /* SRUserInfo.swift in Sources */,
03B1A8F92EC813BC006C353F /* SRScrollView.swift in Sources */,
@ -852,6 +926,8 @@
03980F4F2ECEB91C0006E317 /* SRRecommendPlayerViewModel.swift in Sources */,
03B1A9422ECC4632006C353F /* SRHotSearchCell.swift in Sources */,
03B1A9342ECC12D9006C353F /* SRSearchTextView.swift in Sources */,
370D2F272ED581BB00571E77 /* SRAboutCell.swift in Sources */,
370D2F1C2ED4770800571E77 /* SRUserInfoModel.swift in Sources */,
03B1A93C2ECC406E006C353F /* SRHotSearchView.swift in Sources */,
03B1A8E32EC6F577006C353F /* SRHomeMenuCell.swift in Sources */,
);

View File

@ -0,0 +1,35 @@
//
// UINavigationBar+SRAdd.swift
// SynthReel
//
// Created by CSGY on 2025/11/25.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
extension UINavigationBar {
static func uv_applyGlobalAppearance() {
let navBar = UINavigationBar.appearance()
navBar.tintColor = .white
navBar.titleTextAttributes = [
.foregroundColor: UIColor.white,
.font: UIFont.boldSystemFont(ofSize: 18)
]
//
if let backImage = UIImage(named: "arrow_left_icon_01")?.withRenderingMode(.alwaysOriginal) {
navBar.backIndicatorImage = backImage
navBar.backIndicatorTransitionMaskImage = backImage
}
//
UIBarButtonItem.appearance().setBackButtonTitlePositionAdjustment(
UIOffset(horizontal: -1000, vertical: 0),
for: .default
)
}
}

View File

@ -90,3 +90,66 @@ extension UIView {
self.layer.mask = shapeLayer
}
}
extension UIView {
/// +
/// - Parameters:
/// - cornerRadius:
/// - rotationAngle: π/6 = 30°
func applyHexagonMask(cornerRadius: CGFloat = 0, rotationAngle: CGFloat = 0) {
let width = bounds.width
let height = bounds.height
let center = CGPoint(x: width / 2, y: height / 2)
//
let radius = min(width, height) / 2
let path = UIBezierPath()
var points: [CGPoint] = []
// 6 angle
for i in 0..<6 {
let angle = CGFloat(i) * (.pi / 3) + rotationAngle
let x = center.x + radius * cos(angle)
let y = center.y + radius * sin(angle)
points.append(CGPoint(x: x, y: y))
}
//
for i in 0..<6 {
let prev = points[(i + 5) % 6]
let curr = points[i]
let next = points[(i + 1) % 6]
let v1 = CGPoint(x: curr.x - prev.x, y: curr.y - prev.y)
let v2 = CGPoint(x: curr.x - next.x, y: curr.y - next.y)
let l1 = sqrt(v1.x * v1.x + v1.y * v1.y)
let l2 = sqrt(v2.x * v2.x + v2.y * v2.y)
let u1 = CGPoint(x: v1.x / l1, y: v1.y / l1)
let u2 = CGPoint(x: v2.x / l2, y: v2.y / l2)
let p1 = CGPoint(x: curr.x - u1.x * cornerRadius,
y: curr.y - u1.y * cornerRadius)
let p2 = CGPoint(x: curr.x - u2.x * cornerRadius,
y: curr.y - u2.y * cornerRadius)
if i == 0 {
path.move(to: p1)
} else {
path.addLine(to: p1)
}
path.addQuadCurve(to: p2, controlPoint: curr)
}
path.close()
// mask
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
layer.mask = maskLayer
}
}

View File

@ -19,9 +19,12 @@ class SRLabel: UILabel {
override func layoutSubviews() {
super.layoutSubviews()
let size = self.bounds.size
if let text = self.text, text.count > 0, let colors = self.textColors, let startPoint = self.textStartPoint, let endPoine = self.textEndPoint {
self.textColor = UIColor(patternImage: UIImage.sr_getGradientImage(size: size, colors: colors, startPoint: startPoint, endPoint: endPoine))
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
let size = self.bounds.size
if let text = self.text, text.count > 0, let colors = self.textColors, let startPoint = self.textStartPoint, let endPoine = self.textEndPoint {
self.textColor = UIColor(patternImage: UIImage.sr_getGradientImage(size: size, colors: colors, startPoint: startPoint, endPoint: endPoine))
}
}
}

View File

@ -0,0 +1,89 @@
//
// SRNavgationTitleView.swift
// SynthReel
//
// Created by CSGY on 2025/11/25.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import UIKit
class SRNavgationTitleView: UIView {
private let leftImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "闪光 1"))
imageView.contentMode = .scaleAspectFit
return imageView
}()
private let rightImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "闪光 1"))
imageView.contentMode = .scaleAspectFit
return imageView
}()
private let titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .bold)
label.textAlignment = .center
label.textColor = .white
return label
}()
// MARK: - Init
init(title: String) {
super.init(frame: .zero)
titleLabel.text = title
addSubview(leftImageView)
addSubview(titleLabel)
addSubview(rightImageView)
sizeToFit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Layout & Size
override func sizeThatFits(_ size: CGSize) -> CGSize {
let padding: CGFloat = 6
let imageW: CGFloat = 20
let height: CGFloat = 30
titleLabel.sizeToFit()
let totalWidth = imageW + padding + titleLabel.bounds.width + padding + imageW
return CGSize(width: totalWidth, height: height)
}
override func layoutSubviews() {
super.layoutSubviews()
let padding: CGFloat = 10
let imageW: CGFloat = 20
let imageH: CGFloat = 20
let height = bounds.height
//
leftImageView.frame = CGRect(x: 0,
y: (height - imageH) / 2,
width: imageW,
height: imageH)
//
let titleX = leftImageView.frame.maxX + padding
titleLabel.frame = CGRect(x: titleX,
y: 0,
width: titleLabel.bounds.width,
height: height)
//
let rightX = titleLabel.frame.maxX + padding
rightImageView.frame = CGRect(x: rightX,
y: (height - imageH) / 2,
width: imageW,
height: imageH)
}
}

View File

@ -34,3 +34,28 @@ class SRNavigationController: UINavigationController {
}
}
extension UINavigationController {
func uv_setNavigationBarTransparent(_ transparent: Bool) {
if transparent {
self.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationBar.shadowImage = UIImage()
self.navigationBar.isTranslucent = true
} else {
self.navigationBar.setBackgroundImage(nil, for: .default)
self.navigationBar.shadowImage = nil
self.navigationBar.isTranslucent = false
}
}
}
extension UINavigationController {
func sr_setNavigationTitle(_ title: String?, for viewController: UIViewController) {
let titleText = title ?? ""
// +
let titleView = SRNavgationTitleView(title: titleText)
viewController.navigationItem.titleView = titleView
}
}

View File

@ -18,7 +18,7 @@ class SRTabBarController: ESTabBarController {
let nav1 = createNavigationView(SRHomeViewController(), image: UIImage(named: "tabbar_icon_01"), selectedImage: UIImage(named: "tabbar_icon_01_selected"))
let nav2 = createNavigationView(SRRecommendPlayerViewController(), image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected"))
let nav3 = createNavigationView(SRMyShortViewController(), image: UIImage(named: "tabbar_icon_03"), selectedImage: UIImage(named: "tabbar_icon_03_selected"))
let nav4 = createNavigationView(SRViewController(), image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected"))
let nav4 = createNavigationView(SRUserViewController(), image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected"))
viewControllers = [nav1, nav2, nav3, nav4]

View File

@ -52,14 +52,21 @@ class SRHomeHeaderView: UIView {
lazy var topChartsView: SRHomeTopChartsView = {
let view = SRHomeTopChartsView()
view.showIndicator(true)
view.didSelectedShort = { [weak self] model in
self?.pushShortDetail(model)
}
view.onButtonTapped = { [self] in
if let item = self.viewModel?.moduleArr.first(where: { $0.module_key == view.module_key }) {
pushTopCharts(item.list)
}
}
return view
}()
lazy var bingeWorthyView: SRHomeBingeWorthyView = {
let view = SRHomeBingeWorthyView()
view.showIndicator(false)
view.didSelectedShort = { [weak self] model in
self?.pushShortDetail(model)
}
@ -68,14 +75,22 @@ class SRHomeHeaderView: UIView {
lazy var viralHitsView: SRHomeViralHitsView = {
let view = SRHomeViralHitsView()
view.showIndicator(true)
view.didSelectedShort = { [weak self] model in
self?.pushShortDetail(model)
}
view.onButtonTapped = { [self] in
if let item = self.viewModel?.moduleArr.first(where: { $0.module_key == view.module_key }) {
pushViralHits(item.list)
}
}
return view
}()
lazy var premiereNowView: SRHomePremiereNowView = {
let view = SRHomePremiereNowView()
view.showIndicator(false)
view.didSelectedShort = { [weak self] model in
self?.pushShortDetail(model)
}
@ -126,6 +141,7 @@ class SRHomeHeaderView: UIView {
stackView.addArrangedSubview(youLikeView)
} else if $0.module_key == .popular {
popularArr = $0.list
topChartsView.module_key = $0.module_key
stackView.addArrangedSubview(topChartsView)
} else if $0.module_key == .updates {
updatesArr = $0.list
@ -135,6 +151,7 @@ class SRHomeHeaderView: UIView {
stackView.addArrangedSubview(bingeWorthyView)
} else if $0.module_key == .viralHits {
viralHitsView.dataArr = $0.list
viralHitsView.module_key = $0.module_key
stackView.addArrangedSubview(viralHitsView)
} else if $0.module_key == .premiereNow {
premiereNowView.dataArr = $0.list
@ -153,4 +170,16 @@ class SRHomeHeaderView: UIView {
vc.shortId = model?.short_play_id
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}
func pushTopCharts(_ models: [SRShortModel]?) {
let vc = SRTopChartsViewController()
vc.dataArr = models ?? []
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}
func pushViralHits(_ models: [SRShortModel]?) {
let vc = SRViralHitController()
vc.dataArr = models ?? []
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}
}

View File

@ -13,6 +13,10 @@ class SRHomeModuleView: UIView {
var didSelectedShort: ((_ model: SRShortModel?) -> Void)?
var onButtonTapped: (() -> Void)?
var module_key: SRHomeModuleItem.ModuleKey?
lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .bold)
@ -20,13 +24,14 @@ class SRHomeModuleView: UIView {
return label
}()
lazy var indicatorImageView = UIImageView(image: UIImage(named: "arrow_right_icon_01"))
// lazy var indicatorImageView = UIImageView(image: UIImage(named: "arrow_right_icon_01"))
private var indicatorImageView: UIImageView?
lazy var button: UIControl = {
let button = UIControl(frame: .zero, primaryAction: UIAction(handler: { [weak self] _ in
let button = UIButton(frame: .zero, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
onButtonTapped?()
}))
return button
}()
@ -34,10 +39,29 @@ class SRHomeModuleView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
// addSubview(button)
// button.addSubview(titleLabel)
// button.addSubview(indicatorImageView)
//
// button.snp.makeConstraints { make in
// make.left.equalToSuperview().offset(15)
// make.right.equalToSuperview().offset(-15)
// make.top.equalToSuperview()
// make.height.equalTo(40)
// }
//
// titleLabel.snp.makeConstraints { make in
// make.centerY.equalToSuperview()
// make.left.equalToSuperview()
// }
//
// indicatorImageView.snp.makeConstraints { make in
// make.centerY.equalToSuperview()
// make.right.equalToSuperview()
// }
//
addSubview(button)
button.addSubview(titleLabel)
button.addSubview(indicatorImageView)
button.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15)
@ -50,12 +74,6 @@ class SRHomeModuleView: UIView {
make.centerY.equalToSuperview()
make.left.equalToSuperview()
}
indicatorImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview()
}
}
required init?(coder: NSCoder) {
@ -63,3 +81,30 @@ class SRHomeModuleView: UIView {
}
}
// MARK: - Public API
extension SRHomeModuleView {
///
func showIndicator(_ show: Bool) {
if show {
//
if indicatorImageView != nil { return }
let arrow = UIImageView(image: UIImage(named: "arrow_right_icon_01"))
arrow.contentMode = .scaleAspectFit
indicatorImageView = arrow
button.addSubview(arrow)
arrow.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview()
}
} else {
//
indicatorImageView?.removeFromSuperview()
indicatorImageView = nil
}
}
}

View File

@ -20,7 +20,9 @@ class SRSearchHomeView: UIView {
self.hotView.dataArr = self.viewModel?.hotDataArr ?? []
self.recordView.dataArr = self.viewModel?.recordList ?? []
updateLayout()
DispatchQueue.main.async {
self.updateLayout()
}
}
}
@ -75,7 +77,10 @@ class SRSearchHomeView: UIView {
} else if keyPath == "recordList" {
self.recordView.dataArr = self.viewModel?.recordList ?? []
}
updateLayout()
DispatchQueue.main.async {
self.updateLayout()
}
}
func updateLayout() {

View File

@ -0,0 +1,150 @@
//
// SRTopChartsCell.swift
// SynthReel
//
// Created by CSGY on 2025/11/25.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRTopChartsCell: UICollectionViewCell {
var model : SRShortModel? {
didSet {
titlelabel.text = model?.name
coverImageView.sr_setImage(model?.image_url)
detailLabel.text = model?.sr_description
let count = model?.watch_total ?? 0
if count >= 1000 {
let value = Double(count) / 1000.0
let text = String(format: "%.1fk", value)
// 1.0k 1k
hotLabel.text = text.replacingOccurrences(of: ".0k", with: "k")
} else {
hotLabel.text = "\(count)"
}
}
}
lazy var coverImageView: SRImageView = {
let imageView = SRImageView()
imageView.layer.cornerRadius = 2
imageView.layer.masksToBounds = true
return imageView
}()
lazy var bgIconImage : UIImageView = {
let imageview = UIImageView()
imageview.image = .topChartBg
imageview.contentMode = .scaleToFill
imageview.isUserInteractionEnabled = true
return imageview
}()
lazy var leftIconImage = UIImageView.init(image: .rankLeftIcon)
lazy var rightIconImage = UIImageView.init(image: .rankRightIcon)
lazy var hotIconImage = UIImageView.init(image: .fire)
lazy var titlelabel : UILabel = {
let labe = UILabel()
labe.textColor = .white
labe.font = .font(ofSize: 14, weight: .regular)
return labe
}()
lazy var topLabel : UILabel = {
let labe = UILabel()
labe.textColor = UIColor.FFE_4_B_4
labe.font = .font(ofSize: 12, weight: .semibold)
return labe
}()
lazy var hotLabel : UILabel = {
let labe = UILabel()
labe.textColor = UIColor.FFE_4_B_4
labe.font = .font(ofSize: 10, weight: .regular)
return labe
}()
lazy var detailLabel : UILabel = {
let labe = UILabel()
labe.textColor = UIColor.A_6_A_6_A_6
labe.font = .font(ofSize: 11, weight: .regular)
labe.numberOfLines = 1
return labe
}()
override init(frame: CGRect) {
super.init(frame: frame)
sr_setui()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SRTopChartsCell {
func sr_setui (){
titlelabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
hotLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
contentView.addSubview(bgIconImage)
contentView.addSubview(coverImageView)
contentView.addSubview(leftIconImage)
contentView.addSubview(topLabel)
contentView.addSubview(rightIconImage)
contentView.addSubview(titlelabel)
contentView.addSubview(hotIconImage)
contentView.addSubview(hotLabel)
contentView.addSubview(detailLabel)
bgIconImage.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
coverImageView.snp.makeConstraints { make in
make.left.top.bottom.equalToSuperview().inset(4)
make.width.equalTo(90)
}
leftIconImage.snp.makeConstraints { make in
make.top.equalTo(10)
make.left.equalTo(coverImageView.snp.right).offset(10)
}
topLabel.snp.makeConstraints { make in
make.left.equalTo(leftIconImage.snp.right)
make.centerY.equalTo(leftIconImage)
}
rightIconImage.snp.makeConstraints { make in
make.left.equalTo(topLabel.snp.right)
make.centerY.equalTo(leftIconImage)
}
titlelabel.snp.makeConstraints { make in
make.left.equalTo(coverImageView.snp.right).offset(10)
make.top.equalTo(leftIconImage.snp.bottom).offset(10)
}
hotIconImage.snp.makeConstraints { make in
make.left.equalTo(titlelabel.snp.right).offset(10)
make.centerY.equalTo(titlelabel)
make.size.equalTo(CGSizeMake(10, 10))
}
hotLabel.snp.makeConstraints { make in
make.left.equalTo(hotIconImage.snp.right).offset(2)
make.centerY.equalTo(hotIconImage)
make.right.lessThanOrEqualToSuperview().inset(10)
}
detailLabel.snp.makeConstraints { make in
make.left.equalTo(titlelabel)
make.right.equalTo(-10)
make.top.equalTo(titlelabel.snp.bottom).offset(10)
}
}
}

View File

@ -0,0 +1,86 @@
//
// SRViralHitCell.swift
// SynthReel
//
// Created by CSGY on 2025/11/25.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRViralHitCell: UICollectionViewCell {
var model : SRShortModel? {
didSet {
coverImageView.sr_setImage(model?.image_url)
let count = model?.watch_total ?? 0
if count >= 1000 {
let value = Double(count) / 1000.0
let text = String(format: "%.1fk", value)
// 1.0k 1k
hotLabel.text = text.replacingOccurrences(of: ".0k", with: "k")
} else {
hotLabel.text = "\(count)"
}
}
}
lazy var bgImageView = UIImageView(image: UIImage(named: "home_viral_hits_cell_image"))
lazy var coverImageView: SRImageView = {
let imageView = SRImageView()
imageView.layer.cornerRadius = 2
imageView.layer.masksToBounds = true
return imageView
}()
lazy var hotIconImage = UIImageView.init(image: .hotIcon01)
lazy var hotLabel : UILabel = {
let labe = UILabel()
labe.textColor = .srBlue
labe.font = .font(ofSize: 10, weight: .regular)
return labe
}()
override init(frame: CGRect) {
super.init(frame: frame)
sr_setui()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SRViralHitCell {
func sr_setui (){
contentView.addSubview(bgImageView)
contentView.addSubview(coverImageView)
contentView.addSubview(hotIconImage)
contentView.addSubview(hotLabel)
bgImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
coverImageView.snp.makeConstraints { make in
make.edges.equalTo(UIEdgeInsets(top: 15, left: 8, bottom: 15, right: 8))
}
hotLabel.snp.makeConstraints { make in
make.right.equalTo(-10)
make.top.equalTo(18)
}
hotIconImage.snp.makeConstraints { make in
make.right.equalTo(hotLabel.snp.left).offset(-3)
make.centerY.equalTo(hotLabel)
make.size.equalTo(CGSizeMake(9, 10))
}
}
}

View File

@ -152,7 +152,7 @@ extension SRHomeViewController {
menuContentView.addSubview(menuView)
menuContentView.addSubview(moreButton)
moreButton.addSubview(moreTitleLabel)
moreButton.addSubview(moreIconImageView)
// moreButton.addSubview(moreIconImageView)
menuView.listContainer = pageView.listContainerView
@ -196,10 +196,10 @@ extension SRHomeViewController {
make.left.equalToSuperview()
}
moreIconImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview()
}
// moreIconImageView.snp.makeConstraints { make in
// make.centerY.equalToSuperview()
// make.right.equalToSuperview()
// }
}
}

View File

@ -0,0 +1,102 @@
//
// SRTopChartsViewController.swift
// SynthReel
//
// Created by CSGY on 2025/11/25.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRTopChartsViewController: SRViewController {
override func viewDidLoad() {
super.viewDidLoad()
sr_setui()
// Do any additional setup after loading the view.
}
lazy var topImageview = UIImageView.init(image: .topChartsTop)
var dataArr: [SRShortModel] = [] {
didSet {
self.collectionView.reloadData()
}
}
lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let itemWidth = floor((UIScreen.width - 30))
let itemHeight = 98.0
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 6
layout.minimumLineSpacing = 10
layout.itemSize = .init(width: itemWidth, height: itemHeight)
layout.sectionInset = .init(top: 10, left: 15, bottom: 10, right: 15)
return layout
}()
lazy var collectionView: SRCollectionView = {
let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.showsVerticalScrollIndicator = false
collectionView.showsHorizontalScrollIndicator = false
collectionView.register(SRTopChartsCell.self, forCellWithReuseIdentifier: "cell")
collectionView.sr_addRefreshFooter { [weak self] in
self?.handleFooterRefresh(nil)
}
return collectionView
}()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = false
navigationController?.sr_setNavigationTitle("Top Charts".localized, for: self)
}
}
extension SRTopChartsViewController {
func sr_setui() {
view.addSubview(topImageview)
topImageview.snp.makeConstraints { make in
make.top.equalTo(UIScreen.safeTop)
make.right.equalToSuperview()
}
view.addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.top.equalTo(UIScreen.navBarHeight)
make.left.right.bottom.equalToSuperview()
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension SRTopChartsViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRTopChartsCell
cell.model = self.dataArr[indexPath.item]
cell.topLabel.text = String(format: "Top.%ld", indexPath.item + 1)
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.dataArr.count
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.didScrollCallback?(scrollView)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = dataArr[indexPath.row]
let vc = SRDetailPlayerViewController()
vc.shortId = model.short_play_id
self.navigationController?.pushViewController(vc, animated: true)
}
}

View File

@ -0,0 +1,92 @@
//
// SRViralHitController.swift
// SynthReel
//
// Created by CSGY on 2025/11/25.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRViralHitController: SRViewController {
override func viewDidLoad() {
super.viewDidLoad()
sr_setui()
// Do any additional setup after loading the view.
}
var dataArr: [SRShortModel] = [] {
didSet {
self.collectionView.reloadData()
}
}
lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let itemWidth = floor((UIScreen.width - 30 - 12) / 3.0)
let itemHeight = 148.0 / 111 * itemWidth
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 6
layout.minimumLineSpacing = 10
layout.itemSize = .init(width: itemWidth, height: itemHeight)
layout.sectionInset = .init(top: 10, left: 15, bottom: 10, right: 15)
return layout
}()
lazy var collectionView: SRCollectionView = {
let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.showsVerticalScrollIndicator = false
collectionView.showsHorizontalScrollIndicator = false
collectionView.register(SRViralHitCell.self, forCellWithReuseIdentifier: "cell")
collectionView.sr_addRefreshFooter { [weak self] in
self?.handleFooterRefresh(nil)
}
return collectionView
}()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = false
navigationController?.sr_setNavigationTitle("Viral Hits".localized, for: self)
}
}
extension SRViralHitController {
func sr_setui() {
view.addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.top.equalTo(UIScreen.navBarHeight)
make.left.right.bottom.equalToSuperview()
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension SRViralHitController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRViralHitCell
cell.model = self.dataArr[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.dataArr.count
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.didScrollCallback?(scrollView)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = dataArr[indexPath.row]
let vc = SRDetailPlayerViewController()
vc.shortId = model.short_play_id
self.navigationController?.pushViewController(vc, animated: true)
}
}

View File

@ -35,12 +35,6 @@ class SRListMenuCell: JXSegmentedTitleCell/*JXSegmentedTitleCell*/ {
override func reloadData(itemModel: JXSegmentedBaseItemModel, selectedType: JXSegmentedViewItemSelectedType) {
super.reloadData(itemModel: itemModel, selectedType: selectedType)
// if itemModel.isSelected {
// bgImageView.image = UIImage(named: "FavoritesSeleted")
// } else {
// bgImageView.image = UIImage(named: "Favorites")
// }
if itemModel.index == 0 {
if itemModel.isSelected {
bgImageView.image = UIImage(named: "FavoritesSeleted")

View File

@ -0,0 +1,96 @@
//
// SRAboutUsController.swift
// SynthReel
//
// Created by CSGY on 2025/11/25.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRAboutUsController: SRViewController {
private lazy var dataArr: [SRUserSettingModel] = [
SRUserSettingModel(type: .visitWebsite, name: "synthreel_visit_website".localized),
]
private lazy var tableView: SRTableView = {
let tableView = SRTableView(frame: .zero, style: .plain)
tableView.tableHeaderView = self.headerView
tableView.delegate = self
tableView.dataSource = self
tableView.separatorInset = .init(top: 0, left: 32, bottom: 0, right: 32)
tableView.register(SRAboutCell.self, forCellReuseIdentifier: "cell")
tableView.backgroundColor = .clear
return tableView
}()
private lazy var headerView: SRAboutHeaderVIew = {
let view = SRAboutHeaderVIew(frame: .init(x: 0, y: 0, width: UIScreen.width, height: 186))
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "About".localized
sr_setupLayout()
}
}
extension SRAboutUsController {
private func sr_setupLayout() {
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
}
}
}
//MARK: UITableViewDelegate UITableViewDataSource
extension SRAboutUsController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SRAboutCell
cell.item = dataArr[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = dataArr[indexPath.row]
var urlStr: String? = nil
switch item.type {
case .privacyPolicy:
urlStr = SRWebBaseURL + "/private"
case .userAgreement:
urlStr = SRWebBaseURL + "/user_policy"
case .visitWebsite:
if let url = URL(string: SRWebBaseURL) {
UIApplication.shared.open(url)
}
default:
break
}
// if let urlStr = urlStr {
// let vc = FABaseWebViewController()
// vc.webUrl = urlStr
// self.navigationController?.pushViewController(vc, animated: true)
//
// }
}
}

View File

@ -0,0 +1,30 @@
//
// SRHelpCenterController.swift
// SynthReel
//
// Created by CSGY on 2025/11/25.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRHelpCenterController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}

View File

@ -0,0 +1,30 @@
//
// SRPrivacyController.swift
// SynthReel
//
// Created by CSGY on 2025/11/25.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRPrivacyController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}

View File

@ -10,11 +10,145 @@ import UIKit
class SRUserViewController: SRViewController {
private lazy var dataArr: [SRUserSettingModel] = []
lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
return layout
}()
lazy var collectionView: SRCollectionView = {
let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.showsVerticalScrollIndicator = false
collectionView.showsHorizontalScrollIndicator = false
collectionView.register(SRUserSettingCell.self, forCellWithReuseIdentifier: "cell")
collectionView.register(SRUserTopCell.self, forCellWithReuseIdentifier: "topcell")
collectionView.sr_addRefreshHeader { [weak self] in
Task {
await SRAccountManager.manager.updateUserInfo()
self?.collectionView.reloadData()
}
}
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
set_ui()
setDataArr()
// Do any additional setup after loading the view.
}
}
extension SRUserViewController {
func set_ui (){
view.addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension SRUserViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if(indexPath.section == 0){
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "topcell", for: indexPath) as! SRUserTopCell
// cell.model = self.dataArr[indexPath.row];
return cell
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRUserSettingCell
cell.model = self.dataArr[indexPath.row];
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return 1
}
return self.dataArr.count
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.didScrollCallback?(scrollView)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = dataArr[indexPath.row]
if (model.type == .about){
}
switch model.type {
case .about:
let aboutvc = SRAboutUsController ();
self.navigationController?.pushViewController(aboutvc, animated: true)
break
case .privacyPolicy:
if let url = URL(string: SRWebBaseURL + "/private") {
UIApplication.shared.open(url)
}
break
case .userAgreement:
if let url = URL(string: SRWebBaseURL + "/user_policy") {
UIApplication.shared.open(url)
}
break
default: break
}
}
}
extension SRUserViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.section == 0 { return CGSizeMake(UIScreen.width, 278) }
return CGSizeMake(UIScreen.width, 60)
}
}
extension SRFavoritesViewController {
private func requestDataArr(page: Int) async {
if let dataArr = await SRHomeApi.requestFavoritesData(page: page) {
if page == 1 {
self.dataArr.removeAll()
}
self.dataArr += dataArr
self.page = page
self.collectionView.reloadData()
}
}
}
extension SRUserViewController {
private func setDataArr() {
let arr = [
// SRUserSettingModel(type: .feedback, name: "synthreel_feedback".localized, icon: UIImage(named: "icon_feedback")),
SRUserSettingModel(type: .about, name: "synthreel_about_us".localized, icon: UIImage(named: "icon_about")),
SRUserSettingModel(type: .privacyPolicy, name: "synthreel_privacy_policy".localized, icon: UIImage(named: "icon_privacy")),
SRUserSettingModel(type: .userAgreement, name: "synthreel_user_agreement".localized, icon: UIImage(named: "icon_user")),
]
self.dataArr = arr
self.collectionView.reloadData()
}
}

View File

@ -0,0 +1,13 @@
//
// SRUserInfoModel.swift
// SynthReel
//
// Created by CSGY on 2025/11/24.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRUserInfoModel: NSObject {
}

View File

@ -0,0 +1,34 @@
//
// SRUserSettingModel.swift
// SynthReel
//
// Created by CSGY on 2025/11/24.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
struct SRUserSettingModel {
enum ItemType {
case feedback
case about
// case setting
case privacyPolicy
case userAgreement
case visitWebsite
// ///
// case consumptionRecords
// ///
// case purchaseRecords
// ///
// case rewardCoins
// case deleteAccount
// case language
}
var type: ItemType?
var name: String?
var icon: UIImage?
}

View File

@ -0,0 +1,47 @@
//
// SRAboutCell.swift
// SynthReel
//
// Created by CSGY on 2025/11/25.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRAboutCell: SRTableViewCell {
var item : SRUserSettingModel? {
didSet {
titleLabe.text = item?.name
}
}
lazy var titleLabe : UILabel = {
let label = UILabel ()
label.textColor = UIColor.white
label.font = .font(ofSize: 18, weight: .regular)
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(titleLabe)
titleLabe.snp.makeConstraints { make in
make.left.equalTo(15)
make.centerY.equalToSuperview()
}
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

View File

@ -0,0 +1,68 @@
//
// SRAboutHeaderVIew.swift
// SynthReel
//
// Created by CSGY on 2025/11/25.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRAboutHeaderVIew: UIView {
private lazy var appLogoView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "logo"))
imageView.layer.cornerRadius = 8
imageView.layer.masksToBounds = true
return imageView
}()
private lazy var nameLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .bold)
label.textColor = .white
label.text = kSRAPPName
return label
}()
private lazy var versionLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .regular)
label.textColor = .white
label.text = "Version \(kSRAPPVersion)"
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SRAboutHeaderVIew {
private func fa_setupLayout() {
addSubview(appLogoView)
addSubview(nameLabel)
addSubview(versionLabel)
appLogoView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(30)
make.width.height.equalTo(84)
}
nameLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(appLogoView.snp.bottom).offset(13)
}
versionLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(nameLabel.snp.bottom).offset(6)
}
}
}

View File

@ -0,0 +1,66 @@
//
// SRUserSettingCell.swift
// SynthReel
//
// Created by CSGY on 2025/11/24.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRUserSettingCell: UICollectionViewCell {
var model : SRUserSettingModel? {
didSet {
titleLabel.text = model?.name
iconImage.image = model?.icon
}
}
lazy var iconImage = UIImageView()
lazy var arrayImage = UIImageView.init(image: UIImage(named: "arrow_right_icon_01"))
lazy var titleLabel : UILabel = {
let label = UILabel()
label.textColor = UIColor.B_5_DAE_6
label.font = .font(ofSize: 14, weight: .regular)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
sr_setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SRUserSettingCell {
func sr_setupUI() {
contentView.addSubview(iconImage)
contentView.addSubview(titleLabel)
contentView.addSubview(arrayImage)
iconImage.snp.makeConstraints { make in
make.left.equalTo(10)
make.centerY.equalToSuperview()
make.size.equalTo(CGSizeMake(20, 20))
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(iconImage.snp_rightMargin).offset(10)
make.centerY.equalToSuperview()
}
arrayImage.snp.makeConstraints { make in
make.right.equalTo(-10)
make.centerY.equalToSuperview()
make.size.equalTo(CGSizeMake(14, 14))
}
}
}

View File

@ -0,0 +1,100 @@
//
// SRUserTopCell.swift
// SynthReel
//
// Created by CSGY on 2025/11/24.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRUserTopCell: UICollectionViewCell {
lazy var topImageView : UIImageView = {
let image = UIImageView(image: .bg)
image.contentMode = .scaleToFill
return image
}()
lazy var titleLabel : UILabel = {
let label = UILabel()
label.text = "Visitor"
label.textColor = UIColor._51_D_4_FF
label.font = .font(ofSize: 18, weight: .regular)
label.backgroundColor = UIColor.init(patternImage: .userNameBg)
label.textAlignment = .center
return label
}()
lazy var userId : UILabel = {
let label = UILabel()
label.textColor = UIColor.AAEAFF_0_5
label.font = .font(ofSize: 13, weight: .regular)
label.text = SRAccountManager.manager.userInfo?.customer_id
return label
}()
lazy var iconImageView: SRImageView = {
let imageView = SRImageView()
imageView.isUserInteractionEnabled = true
imageView.image = .logo
return imageView
}()
lazy var iconBgImageView = UIImageView(image: UIImage(named: "头像框"))
override init(frame: CGRect) {
super.init(frame: frame)
sr_setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
DispatchQueue.main.async {
self.iconImageView.applyHexagonMask(cornerRadius: 2, rotationAngle: .pi / 6)
}
}
}
extension SRUserTopCell {
func sr_setupUI (){
contentView.addSubview(topImageView)
contentView.addSubview(iconBgImageView)
iconBgImageView.addSubview(iconImageView)
contentView.addSubview(titleLabel)
contentView.addSubview(userId)
topImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
iconBgImageView.snp.makeConstraints { make in
make.top.equalTo(UIScreen.safeTop + 12);
make.centerX.equalToSuperview()
make.size.equalTo(CGSizeMake(86 , 96))
}
iconImageView.snp.makeConstraints { make in
make.edges.equalTo(UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))
}
titleLabel.snp.makeConstraints { make in
make.top.equalTo(204);
make.centerX.equalToSuperview()
make.size.equalTo(CGSizeMake(136, 26))
}
userId.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp_bottomMargin).offset(7)
make.centerX.equalToSuperview()
make.height.equalTo(26)
}
}
}

View File

@ -21,6 +21,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
window?.rootViewController = SRTabBarController()
window?.makeKeyAndVisible()
UINavigationBar.uv_applyGlobalAppearance()

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.600",
"blue" : "0xFF",
"green" : "0xEA",
"red" : "0xAA"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE6",
"green" : "0xDA",
"red" : "0xB5"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -5,27 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
"blue" : "0xC4",
"green" : "0xC4",
"red" : "0xC4"
}
},
"idiom" : "universal"

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xB4",
"green" : "0xE4",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "logo@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "logo@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "topChartsTop@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "topChartsTop@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "userNameBg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "userNameBg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "fire@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "fire@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_about@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_about@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_feedback@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_feedback@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_privacy@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_privacy@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_user@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_user@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "rankLeftIcon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "rankLeftIcon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "rankRightIcon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "rankRightIcon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "topChartBg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "topChartBg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "头像框@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "头像框@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "顶部bg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "顶部bg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

View File

@ -23,3 +23,10 @@
"Select Episode" = "Select Episode";
"all_episodes_text" = "(All ## episodes)";
"recommend_ep_text" = "Watch the complete series";
"synthreel_feedback" = "Help Center";
"synthreel_about_us" = "About Us";
"synthreel_privacy_policy" = "Privacy Policy";
"synthreel_user_agreement" = "User Agreement";
"synthreel_visit_website" = "visit website";
"Viral Hits" = "Viral Hits";