Adds FAQ links underneath Controller Skins and BIOS settings
This commit is contained in:
parent
47b96d1cf6
commit
c5333ad118
@ -191,6 +191,7 @@
|
||||
D55C468F29E761C000EA6DE9 /* AnyFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55C468E29E761C000EA6DE9 /* AnyFeature.swift */; };
|
||||
D55C469129E7631000EA6DE9 /* AnyOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55C469029E7631000EA6DE9 /* AnyOption.swift */; };
|
||||
D560BD8629EDC45600289847 /* ExternalDisplaySceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D560BD8529EDC45600289847 /* ExternalDisplaySceneDelegate.swift */; };
|
||||
D56F7ABC2B05988700490ACB /* AttributedHeaderFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D56F7ABB2B05988700490ACB /* AttributedHeaderFooterView.swift */; };
|
||||
D57D795629F300E100BB2CF8 /* CustomTintColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A817AF29DF4E6E00904AFE /* CustomTintColor.swift */; };
|
||||
D57D795F29F315F700BB2CF8 /* FeatureDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54A4BB229E4D27E004C7D57 /* FeatureDetailView.swift */; };
|
||||
D57D796029F315F700BB2CF8 /* ExperimentalFeaturesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9C00229DDED6D00A8D610 /* ExperimentalFeaturesView.swift */; };
|
||||
@ -464,6 +465,7 @@
|
||||
D55C468E29E761C000EA6DE9 /* AnyFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyFeature.swift; sourceTree = "<group>"; };
|
||||
D55C469029E7631000EA6DE9 /* AnyOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyOption.swift; sourceTree = "<group>"; };
|
||||
D560BD8529EDC45600289847 /* ExternalDisplaySceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalDisplaySceneDelegate.swift; sourceTree = "<group>"; };
|
||||
D56F7ABB2B05988700490ACB /* AttributedHeaderFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedHeaderFooterView.swift; sourceTree = "<group>"; };
|
||||
D586496F297734280081477E /* CheatMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatMetadata.swift; sourceTree = "<group>"; };
|
||||
D586497129774ABD0081477E /* CheatBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatBase.swift; sourceTree = "<group>"; };
|
||||
D5864977297756CE0081477E /* CheatBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatBaseView.swift; sourceTree = "<group>"; };
|
||||
@ -787,6 +789,7 @@
|
||||
BFFA4C081E8A24D600D87934 /* GameTableViewCell.swift */,
|
||||
BF71CF891FE904B1001F1613 /* GameTableViewCell.xib */,
|
||||
BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */,
|
||||
D56F7ABB2B05988700490ACB /* AttributedHeaderFooterView.swift */,
|
||||
);
|
||||
path = "Table View";
|
||||
sourceTree = "<group>";
|
||||
@ -1663,6 +1666,7 @@
|
||||
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */,
|
||||
BF713C0822499ED4004A1A2B /* PreviousHarmony.xcdatamodeld in Sources */,
|
||||
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */,
|
||||
D56F7ABC2B05988700490ACB /* AttributedHeaderFooterView.swift in Sources */,
|
||||
BFAB9F7D219A43380080EC7D /* SyncManager.swift in Sources */,
|
||||
BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */,
|
||||
BF1173501DA32CF600047DF8 /* ControllersSettingsViewController.swift in Sources */,
|
||||
|
||||
75
Delta/Components/Table View/AttributedHeaderFooterView.swift
Normal file
75
Delta/Components/Table View/AttributedHeaderFooterView.swift
Normal file
@ -0,0 +1,75 @@
|
||||
//
|
||||
// AttributedHeaderFooterView.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 11/15/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@available(iOS 15, *)
|
||||
final class AttributedHeaderFooterView: UITableViewHeaderFooterView
|
||||
{
|
||||
static let reuseIdentifier: String = "TextViewHeaderFooterView"
|
||||
|
||||
var attributedText: AttributedString? {
|
||||
get {
|
||||
guard let attributedText = self.textView.attributedText else { return nil }
|
||||
return AttributedString(attributedText)
|
||||
}
|
||||
set {
|
||||
guard var attributedText = newValue else {
|
||||
self.textView.attributedText = nil
|
||||
return
|
||||
}
|
||||
|
||||
var attributes = AttributeContainer()
|
||||
attributes.foregroundColor = UIColor.secondaryLabel
|
||||
attributes.font = self.textLabel?.font ?? UIFont.preferredFont(forTextStyle: .footnote)
|
||||
|
||||
attributedText.mergeAttributes(attributes, mergePolicy: .keepCurrent)
|
||||
self.textView.attributedText = NSAttributedString(attributedText)
|
||||
}
|
||||
}
|
||||
|
||||
private let textView: UITextView
|
||||
|
||||
override init(reuseIdentifier: String?)
|
||||
{
|
||||
self.textView = UITextView(frame: .zero)
|
||||
self.textView.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.textView.textContainer.lineFragmentPadding = 0
|
||||
self.textView.textContainerInset = .zero
|
||||
self.textView.isSelectable = true // Must be true to open links
|
||||
self.textView.isEditable = false
|
||||
self.textView.isScrollEnabled = false
|
||||
self.textView.backgroundColor = nil
|
||||
self.textView.textContainer.lineBreakMode = .byWordWrapping
|
||||
|
||||
super.init(reuseIdentifier: reuseIdentifier)
|
||||
|
||||
self.textView.delegate = self
|
||||
self.contentView.addSubview(self.textView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
self.textView.topAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.topAnchor),
|
||||
self.textView.bottomAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.bottomAnchor),
|
||||
self.textView.leadingAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.leadingAnchor),
|
||||
self.textView.trailingAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.trailingAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 15, *)
|
||||
extension AttributedHeaderFooterView: UITextViewDelegate
|
||||
{
|
||||
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool
|
||||
{
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -120,6 +120,11 @@ class MelonDSCoreSettingsViewController: UITableViewController
|
||||
self.navigationItem.rightBarButtonItem = nil
|
||||
}
|
||||
|
||||
if #available(iOS 15, *)
|
||||
{
|
||||
self.tableView.register(AttributedHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: AttributedHeaderFooterView.reuseIdentifier)
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(MelonDSCoreSettingsViewController.willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
|
||||
}
|
||||
|
||||
@ -460,8 +465,43 @@ extension MelonDSCoreSettingsViewController
|
||||
case (false, .horizontal): return NSLocalizedString("When AirPlaying DS games, both screens will be placed side-by-side on the external display.", comment: "")
|
||||
}
|
||||
|
||||
default: return super.tableView(tableView, titleForFooterInSection: section.rawValue)
|
||||
case .dsBIOS, .dsiBIOS:
|
||||
guard #available(iOS 15, *) else { break }
|
||||
return nil
|
||||
|
||||
default: break
|
||||
}
|
||||
|
||||
return super.tableView(tableView, titleForFooterInSection: section.rawValue)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView?
|
||||
{
|
||||
let section = Section(rawValue: section)!
|
||||
guard !isSectionHidden(section) else { return nil }
|
||||
|
||||
switch section
|
||||
{
|
||||
case .dsBIOS, .dsiBIOS:
|
||||
guard #available(iOS 15, *), let footerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: AttributedHeaderFooterView.reuseIdentifier) as? AttributedHeaderFooterView else { break }
|
||||
|
||||
let systemName = (section == .dsiBIOS) ? String(localized: "DSi") : String(localized: "DS")
|
||||
|
||||
var attributedText = AttributedString(localized: "Delta requires these BIOS files in order to play Nintendo \(systemName) games.")
|
||||
attributedText += " "
|
||||
|
||||
var learnMore = AttributedString(localized: "Learn more…")
|
||||
learnMore.link = URL(string: "https://faq.deltaemulator.com/getting-started/nintendo-ds-bios-files")
|
||||
attributedText += learnMore
|
||||
|
||||
footerView.attributedText = attributedText
|
||||
|
||||
return footerView
|
||||
|
||||
default: break
|
||||
}
|
||||
|
||||
return super.tableView(tableView, viewForFooterInSection: section.rawValue)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
|
||||
@ -481,14 +521,24 @@ extension MelonDSCoreSettingsViewController
|
||||
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
|
||||
{
|
||||
let section = Section(rawValue: section)!
|
||||
guard !isSectionHidden(section) else { return 1 }
|
||||
|
||||
if isSectionHidden(section)
|
||||
switch section
|
||||
{
|
||||
return 1
|
||||
case .dsBIOS, .dsiBIOS: return UITableView.automaticDimension
|
||||
default: return super.tableView(tableView, heightForFooterInSection: section.rawValue)
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat
|
||||
{
|
||||
let section = Section(rawValue: section)!
|
||||
guard !isSectionHidden(section) else { return 1 }
|
||||
|
||||
switch section
|
||||
{
|
||||
return super.tableView(tableView, heightForFooterInSection: section.rawValue)
|
||||
case .dsBIOS, .dsiBIOS: return 30
|
||||
default: return UITableView.automaticDimension
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,6 +115,11 @@ class SettingsViewController: UITableViewController
|
||||
self.versionLabel.text = NSLocalizedString("Delta", comment: "")
|
||||
#endif
|
||||
}
|
||||
|
||||
if #available(iOS 15, *)
|
||||
{
|
||||
self.tableView.register(AttributedHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: AttributedHeaderFooterView.reuseIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool)
|
||||
@ -584,6 +589,33 @@ extension SettingsViewController
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView?
|
||||
{
|
||||
let section = Section(rawValue: section)!
|
||||
guard !isSectionHidden(section) else { return nil }
|
||||
|
||||
switch section
|
||||
{
|
||||
case .controllerSkins:
|
||||
guard #available(iOS 15, *), let footerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: AttributedHeaderFooterView.reuseIdentifier) as? AttributedHeaderFooterView else { break }
|
||||
|
||||
var attributedText = AttributedString(localized: "Customize the appearance of each system.")
|
||||
attributedText += " "
|
||||
|
||||
var learnMore = AttributedString(localized: "Learn more…")
|
||||
learnMore.link = URL(string: "https://faq.deltaemulator.com/using-delta/controller-skins")
|
||||
attributedText += learnMore
|
||||
|
||||
footerView.attributedText = attributedText
|
||||
|
||||
return footerView
|
||||
|
||||
default: break
|
||||
}
|
||||
|
||||
return super.tableView(tableView, viewForFooterInSection: section.rawValue)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String?
|
||||
{
|
||||
let section = Section(rawValue: section)!
|
||||
@ -594,7 +626,7 @@ extension SettingsViewController
|
||||
#if !BETA
|
||||
case .advanced: return nil
|
||||
#endif
|
||||
|
||||
case .controllerSkins: return nil
|
||||
default: return super.tableView(tableView, titleForFooterInSection: section.rawValue)
|
||||
}
|
||||
}
|
||||
@ -616,14 +648,24 @@ extension SettingsViewController
|
||||
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
|
||||
{
|
||||
let section = Section(rawValue: section)!
|
||||
guard !isSectionHidden(section) else { return 1 }
|
||||
|
||||
if isSectionHidden(section)
|
||||
switch section
|
||||
{
|
||||
return 1
|
||||
case .controllerSkins: return UITableView.automaticDimension
|
||||
default: return super.tableView(tableView, heightForFooterInSection: section.rawValue)
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat
|
||||
{
|
||||
let section = Section(rawValue: section)!
|
||||
guard !isSectionHidden(section) else { return 1 }
|
||||
|
||||
switch section
|
||||
{
|
||||
return super.tableView(tableView, heightForFooterInSection: section.rawValue)
|
||||
case .controllerSkins: return 30
|
||||
default: return UITableView.automaticDimension
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user