mirror of
https://github.com/confirmedcode/Lockdown-iOS.git
synced 2025-12-21 12:14:02 +01:00
[KB-2177, KB-2193]: Creating a list and View the Lists, delete the list
This commit is contained in:
@@ -237,7 +237,7 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="17" y="591"/>
|
||||
</scene>
|
||||
<!--Protect-->
|
||||
<!--Home-->
|
||||
<scene sceneID="Js3-kv-Qbd">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="homeViewController" id="NZN-Gg-FQX" customClass="HomeViewController" customModule="Lockdown" customModuleProvider="target" sceneMemberID="viewController">
|
||||
@@ -967,7 +967,7 @@
|
||||
</constraint>
|
||||
</constraints>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Protect" image="lock.shield.fill" catalog="system" id="O5a-jC-Mj1"/>
|
||||
<tabBarItem key="tabBarItem" title="Home" image="lock.shield.fill" catalog="system" id="O5a-jC-Mj1"/>
|
||||
<connections>
|
||||
<outlet property="allTimeMetrics" destination="C0f-ye-V9U" id="pgQ-k9-Agg"/>
|
||||
<outlet property="dailyMetrics" destination="XSr-oZ-hkd" id="41Q-JX-CQw"/>
|
||||
@@ -1165,7 +1165,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="CQN-ao-cuu">
|
||||
<rect key="frame" x="0.0" y="54" width="375" height="50"/>
|
||||
<rect key="frame" x="0.0" y="54" width="375" height="48"/>
|
||||
<subviews>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="PO4-d5-rZJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="8"/>
|
||||
@@ -1186,10 +1186,10 @@
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SRb-dA-tWF">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="48"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Blocking Enabled" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Kd7-nB-tAb">
|
||||
<rect key="frame" x="20" y="6.5" width="144" height="30"/>
|
||||
<rect key="frame" x="20" y="5.5" width="144" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="ZgL-en-Q3V"/>
|
||||
</constraints>
|
||||
@@ -1197,14 +1197,14 @@
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MMI-Pp-XI4">
|
||||
<rect key="frame" x="306" y="5" width="51" height="33"/>
|
||||
<rect key="frame" x="306" y="5" width="51" height="31"/>
|
||||
<color key="onTintColor" red="0.0" green="0.67751116069999995" blue="0.90461090690000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<action selector="toggleLockdownWithSender:" destination="lbX-da-1v3" eventType="valueChanged" id="5KZ-iG-w1a"/>
|
||||
</connections>
|
||||
</switch>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pH0-q3-pY2">
|
||||
<rect key="frame" x="0.0" y="49" width="375" height="1"/>
|
||||
<rect key="frame" x="0.0" y="47" width="375" height="1"/>
|
||||
<color key="backgroundColor" systemColor="separatorColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="cle-g9-fSo"/>
|
||||
@@ -1227,7 +1227,7 @@
|
||||
</subviews>
|
||||
</stackView>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="45" sectionFooterHeight="1" translatesAutoresizingMaskIntoConstraints="NO" id="Gjd-0I-N7k">
|
||||
<rect key="frame" x="0.0" y="104" width="375" height="563"/>
|
||||
<rect key="frame" x="0.0" y="102" width="375" height="565"/>
|
||||
<color key="backgroundColor" systemColor="groupTableViewBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="BlockListGroupCell" rowHeight="72" id="c8H-rF-8EP" customClass="BlockListGroupCell" customModule="Lockdown" customModuleProvider="target">
|
||||
@@ -3426,7 +3426,7 @@
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="kzx-wt-LeJ" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-6443" y="1966"/>
|
||||
<point key="canvasLocation" x="-5738" y="2693"/>
|
||||
</scene>
|
||||
<!--Account-->
|
||||
<scene sceneID="xBZ-K8-q5G">
|
||||
@@ -3536,8 +3536,8 @@
|
||||
</designable>
|
||||
</designables>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="XMa-OM-jKs"/>
|
||||
<segue reference="QzY-Hs-CoL"/>
|
||||
<segue reference="f2L-nH-Ive"/>
|
||||
<segue reference="qGh-4l-Gst"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
<resources>
|
||||
<image name="blue_circle" width="156" height="156"/>
|
||||
|
||||
@@ -85,3 +85,14 @@ class BlockListView: UIView {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension BlockListView.Contents {
|
||||
|
||||
static func listsBlocked(listName: String, isEnabled: Bool) -> Self {
|
||||
let image = UIImage(named: "icn_list_lock")
|
||||
let status = isEnabled ?
|
||||
NSLocalizedString("On", comment: "") :
|
||||
NSLocalizedString("Off", comment: "")
|
||||
return Self(image: image, title: listName, status: status)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ class BlockListGroupViewController: BaseViewController, UITableViewDelegate, UIT
|
||||
}
|
||||
|
||||
@IBAction func dismiss() {
|
||||
blockListVC?.reloadBlockLists()
|
||||
blockListVC?.reloadCuratedBlockDomains()
|
||||
self.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,126 +8,251 @@
|
||||
import UIKit
|
||||
import CocoaLumberjackSwift
|
||||
|
||||
class BlockListViewController: BaseViewController {
|
||||
final class BlockListViewController: BaseViewController {
|
||||
|
||||
// MARK: - Properties
|
||||
var didMakeChange = false
|
||||
|
||||
var lockdownBlockLists: [LockdownGroup] = []
|
||||
var userBlockedDomains: [(String, Bool)] = []
|
||||
var customBlockedDomains: [(String, Bool)] = []
|
||||
|
||||
// TODO: - change data structure [[userListBlockedDomains], Bool]
|
||||
var customBlockedLists: [(String, Bool)] = []
|
||||
|
||||
let blockListsTableView = StaticTableView()
|
||||
let customBlocksTableView = StaticTableView()
|
||||
let curatedBlockedDomainsTableView = StaticTableView()
|
||||
let customBlockedListsTableView = StaticTableView()
|
||||
let customBlockedDomainsTableView = StaticTableView()
|
||||
|
||||
private lazy var listsSubmenuView: ListsSubmenuView = {
|
||||
let view = ListsSubmenuView()
|
||||
view.createNewListButton.addTarget(self, action: #selector(addList), for: .touchUpInside)
|
||||
view.importBlockListButton.addTarget(self, action: #selector(importBlockList), for: .touchUpInside)
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var customNavigationView: CustomNavigationView = {
|
||||
let view = CustomNavigationView()
|
||||
view.title = NSLocalizedString("Configure Blocking", comment: "")
|
||||
view.buttonTitle = NSLocalizedString("CLOSE", comment: "")
|
||||
view.onButtonPressed { [unowned self] in
|
||||
self.close()
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
enum Page: CaseIterable {
|
||||
case blockLists
|
||||
case curated
|
||||
case custom
|
||||
|
||||
var localizedTitle: String {
|
||||
switch self {
|
||||
case .blockLists:
|
||||
return NSLocalizedString("Block Lists", comment: "")
|
||||
case .curated:
|
||||
return NSLocalizedString("Curated", comment: "")
|
||||
case .custom:
|
||||
return NSLocalizedString("Custom", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let segmented = UISegmentedControl(items: Page.allCases.map(\.localizedTitle))
|
||||
let explanationLabel = UILabel()
|
||||
|
||||
let blockListAddView = BlockListAddView()
|
||||
var addDomainTextField: UITextField {
|
||||
return blockListAddView.textField
|
||||
}
|
||||
private lazy var segmented: UISegmentedControl = {
|
||||
let view = UISegmentedControl(items: Page.allCases.map(\.localizedTitle))
|
||||
view.selectedSegmentIndex = 0
|
||||
view.setTitleTextAttributes([.font: fontMedium14], for: .normal)
|
||||
view.selectedSegmentTintColor = .tunnelsBlue
|
||||
view.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
|
||||
view.addTarget(self, action: #selector(segmentedControlDidChangeValue), for: .valueChanged)
|
||||
return view
|
||||
}()
|
||||
|
||||
@objc func dismissKeyboard() {
|
||||
self.view.endEditing(true)
|
||||
}
|
||||
private let paragraphLabel: UILabel = {
|
||||
let view = UILabel()
|
||||
view.font = fontRegular14
|
||||
view.numberOfLines = 0
|
||||
view.text = NSLocalizedString("Block all your apps from connecting to the domains and sites below. For your convenience, Lockdown also has pre-configured suggestions.", comment: "")
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var addNewListButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .bold, scale: .large)
|
||||
button.tintColor = .tunnelsBlue
|
||||
button.setImage(UIImage(systemName: "plus", withConfiguration: symbolConfig), for: .normal)
|
||||
button.addTarget(self, action: #selector(showSubmenu), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var listsLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.text = NSLocalizedString("Lists", comment: "")
|
||||
label.textColor = .label
|
||||
label.font = fontBold18
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var emptyListsView: EmptyListsView = {
|
||||
let view = EmptyListsView()
|
||||
view.descriptionLabel.text = NSLocalizedString("No lists yet", comment: "")
|
||||
view.addButton.setTitle(NSLocalizedString("Create a list", comment: ""), for: .normal)
|
||||
view.addButton.addTarget(self, action: #selector(addList), for: .touchUpInside)
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var domainsLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.text = NSLocalizedString("Domains", comment: "")
|
||||
label.textColor = .label
|
||||
label.font = fontBold18
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var addNewDomainButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .bold, scale: .large)
|
||||
button.tintColor = .tunnelsBlue
|
||||
button.setImage(UIImage(systemName: "plus", withConfiguration: symbolConfig), for: .normal)
|
||||
button.addTarget(self, action: #selector(addDomain), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var editDomainButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .bold, scale: .large)
|
||||
button.tintColor = .tunnelsBlue
|
||||
button.setImage(UIImage(named: "icn_edit"), for: .normal)
|
||||
button.addTarget(self, action: #selector(editDomains), for: .touchUpInside)
|
||||
button.isHidden = true
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var emptyDomainsView: EmptyListsView = {
|
||||
let view = EmptyListsView()
|
||||
view.descriptionLabel.text = NSLocalizedString("No custom domains yet", comment: "")
|
||||
view.addButton.setTitle(NSLocalizedString("Add a domain", comment: ""), for: .normal)
|
||||
view.addButton.addTarget(self, action: #selector(addDomain), for: .touchUpInside)
|
||||
return view
|
||||
}()
|
||||
|
||||
// MARK: - Lifecycle
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
let customNavigationView = CustomNavigationView()
|
||||
customNavigationView.title = NSLocalizedString("Configure Blocking", comment: "")
|
||||
customNavigationView.buttonTitle = NSLocalizedString("SAVE", comment: "")
|
||||
customNavigationView.onButtonPressed { [unowned self] in
|
||||
self.save()
|
||||
}
|
||||
view.backgroundColor = .secondarySystemBackground
|
||||
|
||||
configure()
|
||||
configureCuratedBlockedDomainsTableView()
|
||||
configureCustomBlockedListsTableView()
|
||||
configureCustomBlockedDomainsTableView()
|
||||
}
|
||||
|
||||
private func configure() {
|
||||
view.addSubview(customNavigationView)
|
||||
customNavigationView.anchors.leading.pin()
|
||||
customNavigationView.anchors.trailing.pin()
|
||||
customNavigationView.anchors.top.safeAreaPin()
|
||||
|
||||
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
|
||||
tap.cancelsTouchesInView = false
|
||||
view.addGestureRecognizer(tap)
|
||||
view.addSubview(paragraphLabel)
|
||||
paragraphLabel.anchors.top.spacing(0, to: customNavigationView.anchors.bottom)
|
||||
paragraphLabel.anchors.leading.readableContentPin(inset: 3)
|
||||
paragraphLabel.anchors.trailing.readableContentPin(inset: 3)
|
||||
|
||||
do {
|
||||
explanationLabel.font = fontRegular14
|
||||
explanationLabel.numberOfLines = 0
|
||||
explanationLabel.text = NSLocalizedString("Block all your apps from connecting to the domains and sites below. For your convenience, Lockdown also has pre-configured suggestions.", comment: "")
|
||||
|
||||
view.addSubview(explanationLabel)
|
||||
explanationLabel.anchors.top.spacing(0, to: customNavigationView.anchors.bottom)
|
||||
explanationLabel.anchors.leading.readableContentPin(inset: 3)
|
||||
explanationLabel.anchors.trailing.readableContentPin(inset: 3)
|
||||
}
|
||||
|
||||
do {
|
||||
view.addSubview(segmented)
|
||||
segmented.selectedSegmentIndex = 0
|
||||
segmented.anchors.top.spacing(12, to: explanationLabel.anchors.bottom)
|
||||
segmented.anchors.leading.readableContentPin()
|
||||
segmented.anchors.trailing.readableContentPin()
|
||||
segmented.setTitleTextAttributes([.font: fontMedium14], for: .normal)
|
||||
if #available(iOS 13.0, *) {
|
||||
segmented.selectedSegmentTintColor = .tunnelsBlue
|
||||
segmented.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
|
||||
}
|
||||
|
||||
segmented.addTarget(self, action: #selector(segmentedControlDidChangeValue), for: .valueChanged)
|
||||
}
|
||||
|
||||
do {
|
||||
addDomainTextField.addTarget(self, action: #selector(textFieldDidEndOnExit), for: .editingDidEndOnExit)
|
||||
}
|
||||
|
||||
do {
|
||||
addTableView(blockListsTableView, layout: { tableView in
|
||||
tableView.anchors.top.spacing(8, to: segmented.anchors.bottom)
|
||||
tableView.anchors.leading.pin()
|
||||
tableView.anchors.trailing.pin()
|
||||
tableView.anchors.bottom.pin()
|
||||
})
|
||||
reloadBlockLists()
|
||||
|
||||
addTableView(customBlocksTableView, layout: { tableView in
|
||||
tableView.anchors.top.spacing(8, to: segmented.anchors.bottom)
|
||||
tableView.anchors.leading.pin()
|
||||
tableView.anchors.trailing.pin()
|
||||
tableView.anchors.bottom.pin()
|
||||
})
|
||||
customBlocksTableView.deselectsCellsAutomatically = true
|
||||
reloadUserBlockedDomains()
|
||||
|
||||
transition(toPage: .blockLists)
|
||||
}
|
||||
view.addSubview(segmented)
|
||||
segmented.anchors.top.spacing(12, to: paragraphLabel.anchors.bottom)
|
||||
segmented.anchors.leading.readableContentPin()
|
||||
segmented.anchors.trailing.readableContentPin()
|
||||
}
|
||||
|
||||
func reloadBlockLists() {
|
||||
blockListsTableView.clear()
|
||||
private func configureCuratedBlockedDomainsTableView() {
|
||||
addTableView(curatedBlockedDomainsTableView, layout: { tableView in
|
||||
tableView.anchors.top.spacing(8, to: segmented.anchors.bottom)
|
||||
tableView.anchors.leading.pin()
|
||||
tableView.anchors.trailing.pin()
|
||||
})
|
||||
|
||||
reloadCuratedBlockDomains()
|
||||
transition(toPage: .curated)
|
||||
}
|
||||
|
||||
private func configureCustomBlockedListsTableView() {
|
||||
|
||||
view.addSubview(listsLabel)
|
||||
listsLabel.anchors.top.spacing(24, to: segmented.anchors.bottom)
|
||||
listsLabel.anchors.leading.marginsPin()
|
||||
|
||||
view.addSubview(addNewListButton)
|
||||
addNewListButton.anchors.centerY.equal(listsLabel.anchors.centerY)
|
||||
addNewListButton.anchors.trailing.marginsPin()
|
||||
|
||||
addTableView(customBlockedListsTableView, layout: { tableView in
|
||||
tableView.anchors.top.spacing(0, to: listsLabel.anchors.bottom)
|
||||
tableView.anchors.leading.pin()
|
||||
tableView.anchors.trailing.pin()
|
||||
})
|
||||
|
||||
view.addSubview(listsSubmenuView)
|
||||
listsSubmenuView.anchors.trailing.marginsPin()
|
||||
listsSubmenuView.anchors.top.spacing(60, to: paragraphLabel.anchors.bottom)
|
||||
|
||||
customBlockedListsTableView.deselectsCellsAutomatically = true
|
||||
|
||||
reloadCustomBlockedLists()
|
||||
}
|
||||
|
||||
private func configureCustomBlockedDomainsTableView() {
|
||||
|
||||
view.addSubview(domainsLabel)
|
||||
domainsLabel.anchors.top.spacing(16, to: customBlockedListsTableView.anchors.bottom)
|
||||
domainsLabel.anchors.leading.marginsPin()
|
||||
|
||||
view.addSubview(addNewDomainButton)
|
||||
addNewDomainButton.anchors.centerY.equal(domainsLabel.anchors.centerY)
|
||||
addNewDomainButton.anchors.trailing.marginsPin()
|
||||
|
||||
view.addSubview(editDomainButton)
|
||||
editDomainButton.anchors.centerY.equal(domainsLabel.anchors.centerY)
|
||||
editDomainButton.anchors.trailing.spacing(16, to: addNewDomainButton.anchors.leading)
|
||||
|
||||
addTableView(customBlockedDomainsTableView, layout: { tableView in
|
||||
tableView.anchors.top.spacing(0, to: domainsLabel.anchors.bottom)
|
||||
tableView.anchors.leading.pin()
|
||||
tableView.anchors.trailing.pin()
|
||||
})
|
||||
|
||||
customBlockedDomainsTableView.deselectsCellsAutomatically = true
|
||||
|
||||
reloadCustomBlockedDomains()
|
||||
}
|
||||
|
||||
// Curated lists
|
||||
func reloadCuratedBlockDomains() {
|
||||
curatedBlockedDomainsTableView.clear()
|
||||
lockdownBlockLists = {
|
||||
let domains = getLockdownBlockedDomains().lockdownDefaults
|
||||
let sorted = domains.sorted(by: { $0.key < $1.key })
|
||||
return Array(sorted.map(\.value))
|
||||
}()
|
||||
createBlockListsRows()
|
||||
blockListsTableView.reloadData()
|
||||
createCuratedBlockedDomainsRows()
|
||||
curatedBlockedDomainsTableView.reloadData()
|
||||
}
|
||||
|
||||
func reloadUserBlockedDomains() {
|
||||
customBlocksTableView.clear()
|
||||
userBlockedDomains = {
|
||||
func reloadCustomBlockedLists() {
|
||||
customBlockedListsTableView.clear()
|
||||
customBlockedLists = {
|
||||
let lists = getUserBlockedList()
|
||||
return lists.sorted(by: { $0.key < $1.key }).map { (key, value) -> (String, Bool) in
|
||||
if let status = value as? NSNumber {
|
||||
return (key, status.boolValue)
|
||||
} else {
|
||||
return (key, false)
|
||||
}
|
||||
}
|
||||
}()
|
||||
createCustomBlockedListsRows()
|
||||
customBlockedListsTableView.reloadData()
|
||||
}
|
||||
|
||||
func reloadCustomBlockedDomains() {
|
||||
customBlockedDomainsTableView.clear()
|
||||
customBlockedDomains = {
|
||||
let domains = getUserBlockedDomains()
|
||||
return domains.sorted(by: { $0.key < $1.key }).map { (key, value) -> (String, Bool) in
|
||||
if let status = value as? NSNumber {
|
||||
@@ -137,12 +262,13 @@ class BlockListViewController: BaseViewController {
|
||||
}
|
||||
}
|
||||
}()
|
||||
createCustomBlocksRows()
|
||||
customBlocksTableView.reloadData()
|
||||
createCustomBlockedDomainsRows()
|
||||
customBlockedDomainsTableView.reloadData()
|
||||
}
|
||||
|
||||
func createBlockListsRows() {
|
||||
let tableView = blockListsTableView
|
||||
// Curated Lists
|
||||
func createCuratedBlockedDomainsRows() {
|
||||
let tableView = curatedBlockedDomainsTableView
|
||||
|
||||
for lockdownGroup in lockdownBlockLists {
|
||||
|
||||
@@ -163,15 +289,120 @@ class BlockListViewController: BaseViewController {
|
||||
}
|
||||
}
|
||||
|
||||
func createCustomBlocksRows() {
|
||||
let tableView = customBlocksTableView
|
||||
@objc
|
||||
func segmentedControlDidChangeValue() {
|
||||
let page = Page.allCases[segmented.selectedSegmentIndex]
|
||||
transition(toPage: page)
|
||||
}
|
||||
|
||||
func transition(toPage page: Page) {
|
||||
|
||||
tableView.addRow { (contentView) in
|
||||
contentView.addSubview(blockListAddView)
|
||||
blockListAddView.anchors.edges.pin()
|
||||
switch page {
|
||||
case .curated:
|
||||
customBlockedDomainsTableView.isHidden = true
|
||||
customBlockedListsTableView.isHidden = true
|
||||
curatedBlockedDomainsTableView.isHidden = false
|
||||
listsLabel.isHidden = true
|
||||
addNewListButton.isHidden = true
|
||||
listsSubmenuView.isHidden = true
|
||||
addNewDomainButton.isHidden = true
|
||||
domainsLabel.isHidden = true
|
||||
editDomainButton.isHidden = true
|
||||
case .custom:
|
||||
customBlockedListsTableView.isHidden = false
|
||||
customBlockedDomainsTableView.isHidden = false
|
||||
curatedBlockedDomainsTableView.isHidden = true
|
||||
listsLabel.isHidden = false
|
||||
addNewListButton.isHidden = false
|
||||
addNewDomainButton.isHidden = false
|
||||
domainsLabel.isHidden = false
|
||||
editDomainButton.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
func saveNewList(userEnteredListName: String) {
|
||||
DDLogInfo("Adding custom list - \(userEnteredListName)")
|
||||
addUserBlockedList(list: userEnteredListName.lowercased())
|
||||
reloadCustomBlockedLists()
|
||||
}
|
||||
|
||||
func saveNewDomain(userEnteredDomainName: String) {
|
||||
let validation = DomainNameValidator.validate(userEnteredDomainName)
|
||||
|
||||
switch validation {
|
||||
case .valid:
|
||||
didMakeChange = true
|
||||
|
||||
DDLogInfo("Adding custom domain - \(userEnteredDomainName)")
|
||||
addUserBlockedDomain(domain: userEnteredDomainName.lowercased())
|
||||
reloadCustomBlockedDomains()
|
||||
case .notValid(let reason):
|
||||
DDLogWarn("Custom domain is not valid - \(userEnteredDomainName), reason - \(reason)")
|
||||
showPopupDialog(
|
||||
title: NSLocalizedString("Invalid domain", comment: ""),
|
||||
message: "\"\(userEnteredDomainName)\"" + NSLocalizedString(" is not a valid entry. Please only enter the host of the domain you want to block. For example, \"google.com\" without \"https://\"", comment: ""),
|
||||
acceptButton: NSLocalizedString("Okay", comment: "")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// MARK: - Functions
|
||||
extension BlockListViewController {
|
||||
|
||||
func createCustomBlockedListsRows() {
|
||||
let tableView = customBlockedListsTableView
|
||||
let emptyList = emptyListsView
|
||||
|
||||
if customBlockedLists.count == 0 {
|
||||
tableView.addRow { (contentView) in
|
||||
contentView.addSubview(emptyList)
|
||||
emptyListsView.anchors.edges.pin()
|
||||
}.onSelect {
|
||||
self.addList()
|
||||
}
|
||||
}
|
||||
|
||||
for (domain, isEnabled) in userBlockedDomains {
|
||||
for (list, status) in customBlockedLists {
|
||||
// var currentEnabledStatus = status
|
||||
let blockListView = BlockListView()
|
||||
blockListView.contents = .listsBlocked(listName: list, isEnabled: status)
|
||||
|
||||
let cell = tableView.addRow { (contentView) in
|
||||
contentView.addSubview(blockListView)
|
||||
blockListView.anchors.edges.pin()
|
||||
}.onSelect { [unowned blockListView, unowned self] in
|
||||
self.didMakeChange = true
|
||||
let storyboard = UIStoryboard.main
|
||||
// let target = storyboard.instantiate(BlockListGroupViewController.self)
|
||||
// target.lockdownGroup = lockdownGroup
|
||||
// target.blockListVC = self
|
||||
// self.navigationController?.pushViewController(target, animated: true)
|
||||
// currentEnabledStatus.toggle()
|
||||
// blockListView.contents = .listsBlocked(listName: list, isEnabled: currentEnabledStatus)
|
||||
// setUserBlockedList(list: list, enabled: currentEnabledStatus)
|
||||
}.onSwipeToDelete { [unowned self] in
|
||||
self.didMakeChange = true
|
||||
deleteList(list: list)
|
||||
DDLogInfo("Deleting custom list - \(list)")
|
||||
}
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
}
|
||||
}
|
||||
|
||||
// Custom Domains
|
||||
func createCustomBlockedDomainsRows() {
|
||||
let tableView = customBlockedDomainsTableView
|
||||
let emptyDomains = emptyDomainsView
|
||||
if customBlockedDomains.count == 0 {
|
||||
tableView.addRow { (contentView) in
|
||||
contentView.addSubview(emptyDomains)
|
||||
emptyDomains.anchors.edges.pin()
|
||||
}.onSelect { [unowned self] in
|
||||
self.addDomain()
|
||||
}
|
||||
}
|
||||
|
||||
for (domain, isEnabled) in customBlockedDomains {
|
||||
var currentEnabledStatus = isEnabled
|
||||
let blockListView = BlockListView()
|
||||
blockListView.contents = .userBlocked(domain: domain, isEnabled: isEnabled)
|
||||
@@ -192,62 +423,9 @@ class BlockListViewController: BaseViewController {
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func segmentedControlDidChangeValue() {
|
||||
let page = Page.allCases[segmented.selectedSegmentIndex]
|
||||
transition(toPage: page)
|
||||
}
|
||||
|
||||
func transition(toPage page: Page) {
|
||||
dismissKeyboard()
|
||||
|
||||
switch page {
|
||||
case .blockLists:
|
||||
customBlocksTableView.isHidden = true
|
||||
blockListsTableView.isHidden = false
|
||||
case .custom:
|
||||
customBlocksTableView.isHidden = false
|
||||
blockListsTableView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func textFieldDidEndOnExit(textField: UITextField) {
|
||||
dismissKeyboard()
|
||||
|
||||
guard let text = textField.text else {
|
||||
DDLogError("Text is empty on add domain text field")
|
||||
return
|
||||
}
|
||||
|
||||
saveNewDomain(userEnteredDomainName: text)
|
||||
}
|
||||
|
||||
func saveNewDomain(userEnteredDomainName: String) {
|
||||
let validation = DomainNameValidator.validate(userEnteredDomainName)
|
||||
|
||||
switch validation {
|
||||
case .valid:
|
||||
didMakeChange = true
|
||||
|
||||
DDLogInfo("Adding custom domain - \(userEnteredDomainName)")
|
||||
addUserBlockedDomain(domain: userEnteredDomainName.lowercased())
|
||||
addDomainTextField.text = ""
|
||||
reloadUserBlockedDomains()
|
||||
case .notValid(let reason):
|
||||
DDLogWarn("Custom domain is not valid - \(userEnteredDomainName), reason - \(reason)")
|
||||
showPopupDialog(
|
||||
title: NSLocalizedString("Invalid domain", comment: ""),
|
||||
message: "\"\(userEnteredDomainName)\"" + NSLocalizedString(" is not a valid entry. Please only enter the host of the domain you want to block. For example, \"google.com\" without \"https://\"", comment: ""),
|
||||
acceptButton: NSLocalizedString("Okay", comment: "")
|
||||
) {
|
||||
self.addDomainTextField.becomeFirstResponder()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func save() {
|
||||
self.dismiss(animated: true, completion: {
|
||||
func close() {
|
||||
dismiss(animated: true, completion: { [weak self] in
|
||||
guard let self else { return }
|
||||
if (self.didMakeChange == true) {
|
||||
if getIsCombinedBlockListEmpty() {
|
||||
FirewallController.shared.setEnabled(false, isUserExplicitToggle: true)
|
||||
@@ -257,4 +435,96 @@ class BlockListViewController: BaseViewController {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@objc func addList() {
|
||||
let tableView = customBlockedListsTableView
|
||||
let alertController = UIAlertController(title: "Create New List", message: nil, preferredStyle: .alert)
|
||||
let saveAction = UIAlertAction(title: "Save", style: .default) { [weak self] (_) in
|
||||
if let txtField = alertController.textFields?.first, let text = txtField.text {
|
||||
guard let self else { return }
|
||||
self.saveNewList(userEnteredListName: text)
|
||||
if !getUserBlockedList().isEmpty {
|
||||
tableView.clear()
|
||||
}
|
||||
self.reloadCustomBlockedLists()
|
||||
self.listsSubmenuView.isHidden = true
|
||||
}
|
||||
}
|
||||
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { [weak self] (_) in
|
||||
guard let self else { return }
|
||||
self.listsSubmenuView.isHidden = true
|
||||
}
|
||||
alertController.addTextField { (textField) in
|
||||
textField.placeholder = NSLocalizedString("List Name", comment: "")
|
||||
}
|
||||
alertController.addAction(saveAction)
|
||||
alertController.addAction(cancelAction)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func deleteList(list: String) {
|
||||
print("deleteDomains btn pressed ....")
|
||||
let alert = UIAlertController(title: NSLocalizedString("Delete List?", comment: ""),
|
||||
message: NSLocalizedString("Are you sure you want to remove this list?", comment: ""),
|
||||
preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("No, Return", comment: ""),
|
||||
style: UIAlertAction.Style.default,
|
||||
handler: { [weak self] (_) in
|
||||
guard let self else { return }
|
||||
self.reloadCustomBlockedLists()
|
||||
print("Return")
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("Yes, Delete", comment: ""),
|
||||
style: UIAlertAction.Style.destructive,
|
||||
handler: { [weak self] (_) in
|
||||
guard let self else { return }
|
||||
deleteUserBlockedList(list: list)
|
||||
self.customBlockedListsTableView.clear()
|
||||
}))
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc func showSubmenu() {
|
||||
listsSubmenuView.isHidden = false
|
||||
}
|
||||
|
||||
@objc func dismissView() {
|
||||
listsSubmenuView.isHidden = true
|
||||
}
|
||||
|
||||
@objc func importBlockList() {
|
||||
listsSubmenuView.isHidden = true
|
||||
print("importBlockList ....")
|
||||
}
|
||||
|
||||
@objc func addDomain() {
|
||||
print("Alert is on")
|
||||
|
||||
let tableView = customBlockedDomainsTableView
|
||||
|
||||
let alertController = UIAlertController(title: "Add a Domain to Block", message: nil, preferredStyle: .alert)
|
||||
let saveAction = UIAlertAction(title: "Save", style: .default) { [weak self] (_) in
|
||||
if let txtField = alertController.textFields?.first, let text = txtField.text {
|
||||
guard let self else { return }
|
||||
self.saveNewDomain(userEnteredDomainName: text)
|
||||
if !getUserBlockedDomains().isEmpty {
|
||||
tableView.clear()
|
||||
}
|
||||
|
||||
self.reloadCustomBlockedDomains()
|
||||
print("Domain==>" + text + "added")
|
||||
}
|
||||
}
|
||||
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
|
||||
alertController.addTextField { (textField) in
|
||||
textField.placeholder = "domain-to-block URL"
|
||||
}
|
||||
alertController.addAction(saveAction)
|
||||
alertController.addAction(cancelAction)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc func editDomains() {
|
||||
print("editDomains .....")
|
||||
}
|
||||
}
|
||||
|
||||
111
LockdowniOS/CustomBlockedTableHeader.swift
Normal file
111
LockdowniOS/CustomBlockedTableHeader.swift
Normal file
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// CustomTableHeader.swift
|
||||
// LockdownSandbox
|
||||
//
|
||||
// Created by Aliaksandr Dvoineu on 24.03.23.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
enum Section: Int, CaseIterable, CustomStringConvertible {
|
||||
|
||||
case lists
|
||||
case domains
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .lists: return "Lists"
|
||||
case .domains: return "Domains"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CustomBlockedTableHeader: UITableViewHeaderFooterView {
|
||||
static let id = "CustomBlockedTableHeader"
|
||||
|
||||
private(set) var addButtonCallback: () -> () = { }
|
||||
private(set) var editButtonCallback: () -> () = { }
|
||||
|
||||
lazy var listsTitleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = .label
|
||||
label.textAlignment = .center
|
||||
label.font = fontBold18
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var addButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .bold, scale: .large)
|
||||
button.setImage(UIImage(systemName: "plus", withConfiguration: symbolConfig), for: .normal)
|
||||
button.tintColor = .tunnelsBlue
|
||||
button.addTarget(self, action: #selector(addButtonDidPress), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
||||
lazy var editButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .bold, scale: .large)
|
||||
button.setImage(UIImage(named: "icn_edit"), for: .normal)
|
||||
button.tintColor = .tunnelsBlue
|
||||
button.addTarget(self, action: #selector(editButtonDidPress), for: .touchUpInside)
|
||||
button.isHidden = true
|
||||
return button
|
||||
}()
|
||||
|
||||
var category: Section = .lists {
|
||||
didSet {
|
||||
switch category {
|
||||
case .lists:
|
||||
listsTitleLabel.text = category.description
|
||||
|
||||
case .domains:
|
||||
listsTitleLabel.text = category.description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override init(reuseIdentifier: String?) {
|
||||
super.init(reuseIdentifier: reuseIdentifier)
|
||||
configure()
|
||||
}
|
||||
|
||||
func configure() {
|
||||
contentView.addSubview(listsTitleLabel)
|
||||
contentView.addSubview(addButton)
|
||||
contentView.addSubview(editButton)
|
||||
|
||||
listsTitleLabel.anchors.leading.pin()
|
||||
listsTitleLabel.anchors.bottom.marginsPin()
|
||||
|
||||
addButton.anchors.trailing.pin()
|
||||
addButton.anchors.bottom.marginsPin()
|
||||
|
||||
editButton.anchors.trailing.spacing(12, to: addButton.anchors.leading)
|
||||
editButton.anchors.bottom.marginsPin()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func onAddButtonPressed(_ callback: @escaping () -> ()) -> Self {
|
||||
addButtonCallback = callback
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func onEditButtonPressed(_ callback: @escaping () -> ()) -> Self {
|
||||
editButtonCallback = callback
|
||||
return self
|
||||
}
|
||||
|
||||
@objc func addButtonDidPress() {
|
||||
addButtonCallback()
|
||||
}
|
||||
|
||||
@objc func editButtonDidPress() {
|
||||
editButtonCallback()
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ final class CustomNavigationView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
var buttonTitle: String = NSLocalizedString("SAVE", comment: "") {
|
||||
var buttonTitle: String = NSLocalizedString("CLOSE", comment: "") {
|
||||
didSet {
|
||||
button.setTitle(buttonTitle, for: .normal)
|
||||
}
|
||||
@@ -45,26 +45,22 @@ final class CustomNavigationView: UIView {
|
||||
|
||||
func didLoad() {
|
||||
addSubview(titleView)
|
||||
titleView.textAlignment = .left
|
||||
titleView.textAlignment = .center
|
||||
titleView.text = title
|
||||
titleView.font = fontMedium17
|
||||
|
||||
titleView.anchors.leading.marginsPin(inset: 20)
|
||||
titleView.anchors.width.greaterThanOrEqual(220)
|
||||
titleView.anchors.height.equal(24)
|
||||
titleView.anchors.centerX.align()
|
||||
titleView.anchors.top.pin(inset: 18)
|
||||
|
||||
addSubview(button)
|
||||
button.titleLabel?.font = fontBold13
|
||||
button.contentHorizontalAlignment = .trailing
|
||||
button.contentHorizontalAlignment = .leading
|
||||
button.tintColor = .confirmedBlue
|
||||
button.setTitle(buttonTitle, for: .normal)
|
||||
|
||||
button.anchors.centerY.equal(titleView.anchors.centerY)
|
||||
button.anchors.trailing.marginsPin(inset: 8)
|
||||
button.anchors.leading.marginsPin(inset: 8)
|
||||
button.anchors.bottom.marginsPin()
|
||||
button.anchors.height.equal(39)
|
||||
button.anchors.width.greaterThanOrEqual(60)
|
||||
|
||||
button.addTarget(self, action: #selector(buttonDidPress), for: .touchUpInside)
|
||||
}
|
||||
|
||||
94
LockdowniOS/EmptyListsView.swift
Normal file
94
LockdowniOS/EmptyListsView.swift
Normal file
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// NothingBlockedView.swift
|
||||
// Lockdown
|
||||
//
|
||||
// Created by Aliaksandr Dvoineu on 21.03.23.
|
||||
// Copyright © 2023 Confirmed Inc. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class EmptyListsView: UIView {
|
||||
|
||||
var descriptionText: String = "" {
|
||||
didSet {
|
||||
descriptionLabel.text = descriptionText
|
||||
}
|
||||
}
|
||||
|
||||
var buttonTitle: String = "" {
|
||||
didSet {
|
||||
addButton.setTitle(buttonTitle, for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
private(set) var buttonCallback: () -> () = { }
|
||||
|
||||
@discardableResult
|
||||
func onButtonPressed(_ callback: @escaping () -> ()) -> Self {
|
||||
buttonCallback = callback
|
||||
return self
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
lazy var stackView: UIStackView = {
|
||||
let stackView = UIStackView()
|
||||
stackView.addArrangedSubview(descriptionLabel)
|
||||
stackView.addArrangedSubview(addButton)
|
||||
stackView.axis = .vertical
|
||||
stackView.distribution = .fillEqually
|
||||
stackView.alignment = .center
|
||||
stackView.spacing = 4
|
||||
return stackView
|
||||
}()
|
||||
|
||||
lazy var descriptionLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = .lightGray
|
||||
label.textAlignment = .center
|
||||
label.font = fontBold13
|
||||
label.textAlignment = .center
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var addButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
button.tintColor = .tunnelsBlue
|
||||
button.backgroundColor = .tunnelsLightBlue
|
||||
button.titleLabel?.font = fontBold13
|
||||
button.layer.cornerRadius = 8
|
||||
button.titleEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
|
||||
return button
|
||||
}()
|
||||
|
||||
// MARK: - Initializer
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
configure()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: Configure UI
|
||||
|
||||
func configure() {
|
||||
addSubview(addButton)
|
||||
addButton.anchors.width.greaterThanOrEqual(120)
|
||||
|
||||
addSubview(stackView)
|
||||
stackView.anchors.top.marginsPin()
|
||||
stackView.anchors.bottom.marginsPin()
|
||||
stackView.anchors.leading.marginsPin()
|
||||
stackView.anchors.trailing.marginsPin()
|
||||
}
|
||||
|
||||
// - MARK: Functions
|
||||
|
||||
@objc func buttonDidPress() {
|
||||
buttonCallback()
|
||||
}
|
||||
}
|
||||
79
LockdowniOS/ListsSubmenuView.swift
Normal file
79
LockdowniOS/ListsSubmenuView.swift
Normal file
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// ListsSubmenuView.swift
|
||||
// LockdownSandbox
|
||||
//
|
||||
// Created by Aliaksandr Dvoineu on 23.03.23.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class ListsSubmenuView: UIView {
|
||||
|
||||
private(set) var buttonCallback: () -> () = { }
|
||||
|
||||
@discardableResult
|
||||
func onButtonPressed(_ callback: @escaping () -> ()) -> Self {
|
||||
buttonCallback = callback
|
||||
return self
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
lazy var createNewListButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
button.tintColor = .tunnelsBlue
|
||||
button.setTitle("Create New List...", for: .normal)
|
||||
button.setImage(UIImage(named: "icn_create_list"), for: .normal)
|
||||
button.setTitleColor(.label, for: .normal)
|
||||
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -10, bottom: 0, right: 0)
|
||||
return button
|
||||
}()
|
||||
|
||||
lazy var importBlockListButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
button.tintColor = .tunnelsBlue
|
||||
button.setTitle("Import Block List...", for: .normal)
|
||||
button.setImage(UIImage(named: "icn_import_list"), for: .normal)
|
||||
button.setTitleColor(.label, for: .normal)
|
||||
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -10, bottom: 0, right: 0)
|
||||
return button
|
||||
}()
|
||||
|
||||
lazy var stackView: UIStackView = {
|
||||
let stackView = UIStackView()
|
||||
stackView.addArrangedSubview(createNewListButton)
|
||||
stackView.addArrangedSubview(importBlockListButton)
|
||||
stackView.axis = .vertical
|
||||
stackView.distribution = .fillEqually
|
||||
stackView.alignment = .leading
|
||||
stackView.spacing = 12
|
||||
return stackView
|
||||
}()
|
||||
|
||||
// MARK: - Initializer
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
configure()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
func configure() {
|
||||
backgroundColor = .systemBackground
|
||||
|
||||
addSubview(stackView)
|
||||
stackView.anchors.top.marginsPin()
|
||||
stackView.anchors.bottom.marginsPin()
|
||||
stackView.anchors.leading.marginsPin(inset: 10)
|
||||
stackView.anchors.trailing.marginsPin(inset: 16)
|
||||
}
|
||||
|
||||
@objc func buttonDidPress() {
|
||||
buttonCallback()
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,23 @@ import UIKit
|
||||
|
||||
final class StaticTableView: UITableView {
|
||||
|
||||
// Resizing UITableView to fit content
|
||||
override var contentSize: CGSize {
|
||||
didSet {
|
||||
invalidateIntrinsicContentSize()
|
||||
}
|
||||
}
|
||||
|
||||
override var intrinsicContentSize: CGSize {
|
||||
layoutIfNeeded()
|
||||
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
|
||||
}
|
||||
|
||||
var rows: [SelectableTableViewCell] = []
|
||||
var deselectsCellsAutomatically: Bool = false
|
||||
|
||||
override init(frame: CGRect, style: UITableView.Style) {
|
||||
super.init(frame: frame, style: style)
|
||||
super.init(frame: frame, style: .insetGrouped)
|
||||
setup()
|
||||
}
|
||||
|
||||
@@ -126,6 +138,10 @@ extension StaticTableView: UITableViewDataSource {
|
||||
return 1
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
return 0
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return rows.count
|
||||
}
|
||||
|
||||
@@ -145,8 +145,8 @@
|
||||
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "O4a-yi-uRp"; */
|
||||
"O4a-yi-uRp.normalTitle" = "Button";
|
||||
|
||||
/* Class = "UITabBarItem"; title = "Protect"; ObjectID = "O5a-jC-Mj1"; */
|
||||
"O5a-jC-Mj1.title" = "Protect";
|
||||
/* Class = "UITabBarItem"; title = "Home"; ObjectID = "O5a-jC-Mj1"; */
|
||||
"O5a-jC-Mj1.title" = "Home";
|
||||
|
||||
/* Class = "UILabel"; text = "Location: 🇺🇸"; ObjectID = "O6b-GR-ijA"; */
|
||||
"O6b-GR-ijA.text" = "Location: 🇺🇸";
|
||||
|
||||
Reference in New Issue
Block a user