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 */; };
|
D55C468F29E761C000EA6DE9 /* AnyFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55C468E29E761C000EA6DE9 /* AnyFeature.swift */; };
|
||||||
D55C469129E7631000EA6DE9 /* AnyOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55C469029E7631000EA6DE9 /* AnyOption.swift */; };
|
D55C469129E7631000EA6DE9 /* AnyOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55C469029E7631000EA6DE9 /* AnyOption.swift */; };
|
||||||
D560BD8629EDC45600289847 /* ExternalDisplaySceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D560BD8529EDC45600289847 /* ExternalDisplaySceneDelegate.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 */; };
|
D57D795629F300E100BB2CF8 /* CustomTintColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A817AF29DF4E6E00904AFE /* CustomTintColor.swift */; };
|
||||||
D57D795F29F315F700BB2CF8 /* FeatureDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54A4BB229E4D27E004C7D57 /* FeatureDetailView.swift */; };
|
D57D795F29F315F700BB2CF8 /* FeatureDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54A4BB229E4D27E004C7D57 /* FeatureDetailView.swift */; };
|
||||||
D57D796029F315F700BB2CF8 /* ExperimentalFeaturesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9C00229DDED6D00A8D610 /* ExperimentalFeaturesView.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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
D5864977297756CE0081477E /* CheatBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatBaseView.swift; sourceTree = "<group>"; };
|
||||||
@ -787,6 +789,7 @@
|
|||||||
BFFA4C081E8A24D600D87934 /* GameTableViewCell.swift */,
|
BFFA4C081E8A24D600D87934 /* GameTableViewCell.swift */,
|
||||||
BF71CF891FE904B1001F1613 /* GameTableViewCell.xib */,
|
BF71CF891FE904B1001F1613 /* GameTableViewCell.xib */,
|
||||||
BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */,
|
BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */,
|
||||||
|
D56F7ABB2B05988700490ACB /* AttributedHeaderFooterView.swift */,
|
||||||
);
|
);
|
||||||
path = "Table View";
|
path = "Table View";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1663,6 +1666,7 @@
|
|||||||
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */,
|
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */,
|
||||||
BF713C0822499ED4004A1A2B /* PreviousHarmony.xcdatamodeld in Sources */,
|
BF713C0822499ED4004A1A2B /* PreviousHarmony.xcdatamodeld in Sources */,
|
||||||
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */,
|
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */,
|
||||||
|
D56F7ABC2B05988700490ACB /* AttributedHeaderFooterView.swift in Sources */,
|
||||||
BFAB9F7D219A43380080EC7D /* SyncManager.swift in Sources */,
|
BFAB9F7D219A43380080EC7D /* SyncManager.swift in Sources */,
|
||||||
BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */,
|
BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */,
|
||||||
BF1173501DA32CF600047DF8 /* ControllersSettingsViewController.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
|
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)
|
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: "")
|
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
|
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
|
||||||
@ -481,14 +521,24 @@ extension MelonDSCoreSettingsViewController
|
|||||||
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
|
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
|
||||||
{
|
{
|
||||||
let section = Section(rawValue: section)!
|
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: "")
|
self.versionLabel.text = NSLocalizedString("Delta", comment: "")
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if #available(iOS 15, *)
|
||||||
|
{
|
||||||
|
self.tableView.register(AttributedHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: AttributedHeaderFooterView.reuseIdentifier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool)
|
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?
|
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String?
|
||||||
{
|
{
|
||||||
let section = Section(rawValue: section)!
|
let section = Section(rawValue: section)!
|
||||||
@ -594,7 +626,7 @@ extension SettingsViewController
|
|||||||
#if !BETA
|
#if !BETA
|
||||||
case .advanced: return nil
|
case .advanced: return nil
|
||||||
#endif
|
#endif
|
||||||
|
case .controllerSkins: return nil
|
||||||
default: return super.tableView(tableView, titleForFooterInSection: section.rawValue)
|
default: return super.tableView(tableView, titleForFooterInSection: section.rawValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -616,14 +648,24 @@ extension SettingsViewController
|
|||||||
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
|
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
|
||||||
{
|
{
|
||||||
let section = Section(rawValue: section)!
|
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