add bar, gestures for switching between images
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,4 @@
|
||||
/build
|
||||
/.nvim
|
||||
.DS_Store
|
||||
buildServer.json
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 77;
|
||||
objectVersion = 90;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -63,24 +63,18 @@
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
22FB3B892E18135A00A9B407 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
22FB3B9F2E18135B00A9B407 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
22FB3BA92E18135B00A9B407 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
@@ -118,14 +112,10 @@
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
22FB3B8E2E18135A00A9B407 /* ImageViewer */,
|
||||
);
|
||||
name = ImageViewer;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = ImageViewer;
|
||||
productReference = 22FB3B8C2E18135A00A9B407 /* ImageViewer.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
@@ -147,8 +137,6 @@
|
||||
22FB3BA52E18135B00A9B407 /* ImageViewerTests */,
|
||||
);
|
||||
name = ImageViewerTests;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = ImageViewerTests;
|
||||
productReference = 22FB3BA22E18135B00A9B407 /* ImageViewerTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
@@ -170,8 +158,6 @@
|
||||
22FB3BAF2E18135B00A9B407 /* ImageViewerUITests */,
|
||||
);
|
||||
name = ImageViewerUITests;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = ImageViewerUITests;
|
||||
productReference = 22FB3BAC2E18135B00A9B407 /* ImageViewerUITests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
@@ -208,7 +194,7 @@
|
||||
);
|
||||
mainGroup = 22FB3B832E18135A00A9B407;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
preferredProjectObjectVersion = 77;
|
||||
preferredProjectObjectVersion = 90;
|
||||
productRefGroup = 22FB3B8D2E18135A00A9B407 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
@@ -223,48 +209,36 @@
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
22FB3B8A2E18135A00A9B407 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
22FB3BA02E18135B00A9B407 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
22FB3BAA2E18135B00A9B407 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
22FB3B882E18135A00A9B407 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
22FB3B9E2E18135B00A9B407 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
22FB3BA82E18135B00A9B407 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
@@ -282,7 +256,7 @@
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
22FB3BB62E18135B00A9B407 /* Debug */ = {
|
||||
22FB3BB62E18135B00A9B407 /* Debug configuration for PBXNativeTarget "ImageViewer" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
@@ -310,7 +284,7 @@
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
22FB3BB72E18135B00A9B407 /* Release */ = {
|
||||
22FB3BB72E18135B00A9B407 /* Release configuration for PBXNativeTarget "ImageViewer" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
@@ -338,7 +312,7 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
22FB3BB82E18135B00A9B407 /* Debug */ = {
|
||||
22FB3BB82E18135B00A9B407 /* Debug configuration for PBXProject "ImageViewer" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
@@ -401,7 +375,7 @@
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
22FB3BB92E18135B00A9B407 /* Release */ = {
|
||||
22FB3BB92E18135B00A9B407 /* Release configuration for PBXProject "ImageViewer" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
@@ -457,7 +431,7 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
22FB3BBB2E18135B00A9B407 /* Debug */ = {
|
||||
22FB3BBB2E18135B00A9B407 /* Debug configuration for PBXNativeTarget "ImageViewerTests" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
@@ -475,7 +449,7 @@
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
22FB3BBC2E18135B00A9B407 /* Release */ = {
|
||||
22FB3BBC2E18135B00A9B407 /* Release configuration for PBXNativeTarget "ImageViewerTests" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
@@ -493,7 +467,7 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
22FB3BBE2E18135B00A9B407 /* Debug */ = {
|
||||
22FB3BBE2E18135B00A9B407 /* Debug configuration for PBXNativeTarget "ImageViewerUITests" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@@ -509,7 +483,7 @@
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
22FB3BBF2E18135B00A9B407 /* Release */ = {
|
||||
22FB3BBF2E18135B00A9B407 /* Release configuration for PBXNativeTarget "ImageViewerUITests" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@@ -531,37 +505,33 @@
|
||||
22FB3B872E18135A00A9B407 /* Build configuration list for PBXProject "ImageViewer" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
22FB3BB82E18135B00A9B407 /* Debug */,
|
||||
22FB3BB92E18135B00A9B407 /* Release */,
|
||||
22FB3BB82E18135B00A9B407 /* Debug configuration for PBXProject "ImageViewer" */,
|
||||
22FB3BB92E18135B00A9B407 /* Release configuration for PBXProject "ImageViewer" */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
22FB3BB52E18135B00A9B407 /* Build configuration list for PBXNativeTarget "ImageViewer" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
22FB3BB62E18135B00A9B407 /* Debug */,
|
||||
22FB3BB72E18135B00A9B407 /* Release */,
|
||||
22FB3BB62E18135B00A9B407 /* Debug configuration for PBXNativeTarget "ImageViewer" */,
|
||||
22FB3BB72E18135B00A9B407 /* Release configuration for PBXNativeTarget "ImageViewer" */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
22FB3BBA2E18135B00A9B407 /* Build configuration list for PBXNativeTarget "ImageViewerTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
22FB3BBB2E18135B00A9B407 /* Debug */,
|
||||
22FB3BBC2E18135B00A9B407 /* Release */,
|
||||
22FB3BBB2E18135B00A9B407 /* Debug configuration for PBXNativeTarget "ImageViewerTests" */,
|
||||
22FB3BBC2E18135B00A9B407 /* Release configuration for PBXNativeTarget "ImageViewerTests" */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
22FB3BBD2E18135B00A9B407 /* Build configuration list for PBXNativeTarget "ImageViewerUITests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
22FB3BBE2E18135B00A9B407 /* Debug */,
|
||||
22FB3BBF2E18135B00A9B407 /* Release */,
|
||||
22FB3BBE2E18135B00A9B407 /* Debug configuration for PBXNativeTarget "ImageViewerUITests" */,
|
||||
22FB3BBF2E18135B00A9B407 /* Release configuration for PBXNativeTarget "ImageViewerUITests" */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
<dict>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,14 +1,245 @@
|
||||
import UIKit
|
||||
|
||||
class ViewController: UIViewController {
|
||||
enum PageTurn {
|
||||
case next
|
||||
case previous
|
||||
}
|
||||
|
||||
enum PageTurnMode {
|
||||
case leftToRight
|
||||
case rightToLeft
|
||||
case scroll
|
||||
}
|
||||
|
||||
class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||
|
||||
var imageView = UIImageView()
|
||||
var images: [UIImage] = []
|
||||
var page = 0
|
||||
var mode = PageTurnMode.leftToRight
|
||||
var leftTap: UITapGestureRecognizer!
|
||||
var rightTap: UITapGestureRecognizer!
|
||||
var topTap: UITapGestureRecognizer!
|
||||
let topBarView = UIView()
|
||||
var isTopBarVisible = false
|
||||
var topBarHeightConstraint: NSLayoutConstraint!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .white
|
||||
let label = UILabel()
|
||||
label.text = "Hello, UIKit!"
|
||||
label.textAlignment = .center
|
||||
label.frame = CGRect(x: 50, y: 150, width: 300, height: 50)
|
||||
view.addSubview(label)
|
||||
|
||||
view.backgroundColor = .red
|
||||
|
||||
setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
setupImageView()
|
||||
setupGestures()
|
||||
setupTopBar()
|
||||
}
|
||||
|
||||
func setupGestures() {
|
||||
let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
|
||||
swipeLeft.direction = .left
|
||||
view.addGestureRecognizer(swipeLeft)
|
||||
|
||||
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
|
||||
swipeRight.direction = .right
|
||||
view.addGestureRecognizer(swipeRight)
|
||||
|
||||
setupTapZones()
|
||||
}
|
||||
|
||||
func setupTapZones() {
|
||||
let leftView = UIView()
|
||||
let rightView = UIView()
|
||||
let topView = UIView()
|
||||
|
||||
leftView.translatesAutoresizingMaskIntoConstraints = false
|
||||
rightView.translatesAutoresizingMaskIntoConstraints = false
|
||||
topView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
view.addSubview(leftView)
|
||||
view.addSubview(rightView)
|
||||
view.addSubview(topView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
leftView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
leftView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
leftView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
leftView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
|
||||
|
||||
rightView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
rightView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
rightView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
rightView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
|
||||
|
||||
topView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
topView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
topView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
topView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2),
|
||||
])
|
||||
|
||||
leftView.backgroundColor = .clear
|
||||
rightView.backgroundColor = .clear
|
||||
topView.backgroundColor = .clear
|
||||
|
||||
leftTap = UITapGestureRecognizer(target: self, action: #selector(handleLeftTap))
|
||||
rightTap = UITapGestureRecognizer(target: self, action: #selector(handleRightTap))
|
||||
topTap = UITapGestureRecognizer(target: self, action: #selector(handleTopTap))
|
||||
|
||||
leftTap.delegate = self
|
||||
rightTap.delegate = self
|
||||
topTap.delegate = self
|
||||
|
||||
leftView.addGestureRecognizer(leftTap)
|
||||
rightView.addGestureRecognizer(rightTap)
|
||||
topView.addGestureRecognizer(topTap)
|
||||
}
|
||||
|
||||
func setupTopBar() {
|
||||
topBarView.translatesAutoresizingMaskIntoConstraints = false
|
||||
topBarView.backgroundColor = UIColor.black.withAlphaComponent(0.8) // Or any style
|
||||
view.addSubview(topBarView)
|
||||
|
||||
topBarHeightConstraint = topBarView.heightAnchor.constraint(equalToConstant: 0)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
topBarView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
|
||||
topBarView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
topBarView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
topBarHeightConstraint,
|
||||
])
|
||||
}
|
||||
|
||||
func gestureRecognizer(
|
||||
_ gestureRecognizer: UIGestureRecognizer,
|
||||
shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer
|
||||
) -> Bool {
|
||||
if gestureRecognizer == leftTap || gestureRecognizer == rightTap,
|
||||
otherGestureRecognizer == topTap
|
||||
{
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func toggleTopBar() {
|
||||
isTopBarVisible.toggle()
|
||||
|
||||
topBarHeightConstraint.constant = isTopBarVisible ? 60 : 0 // You can adjust height
|
||||
}
|
||||
|
||||
@objc func handleTopTap() {
|
||||
toggleTopBar()
|
||||
}
|
||||
|
||||
@objc func handleLeftTap() {
|
||||
switch mode {
|
||||
case .rightToLeft: changeImage(turn: .next)
|
||||
case .leftToRight: changeImage(turn: .previous)
|
||||
case .scroll: break
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleRightTap() {
|
||||
switch mode {
|
||||
case .rightToLeft: changeImage(turn: .previous)
|
||||
case .leftToRight: changeImage(turn: .next)
|
||||
case .scroll: break
|
||||
}
|
||||
}
|
||||
|
||||
@objc func handleSwipe(_ gesture: UISwipeGestureRecognizer) {
|
||||
switch gesture.direction {
|
||||
case .left:
|
||||
switch mode {
|
||||
case .rightToLeft: changeImage(turn: .previous)
|
||||
case .leftToRight: changeImage(turn: .next)
|
||||
case .scroll: break
|
||||
}
|
||||
case .right:
|
||||
switch mode {
|
||||
case .rightToLeft: changeImage(turn: .next)
|
||||
case .leftToRight: changeImage(turn: .previous)
|
||||
case .scroll: break
|
||||
}
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
func setupImageView() {
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
imageView.contentMode = UIView.ContentMode.scaleAspectFit
|
||||
|
||||
imageView.clipsToBounds = true
|
||||
|
||||
view.addSubview(imageView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
imageView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
])
|
||||
images = getImages()
|
||||
imageView.image = images[0]
|
||||
}
|
||||
|
||||
func changeImage(turn: PageTurn) {
|
||||
if images.count == 0 {
|
||||
return
|
||||
}
|
||||
switch turn {
|
||||
case .next: page = min(images.count - 1, page + 1)
|
||||
case .previous: page = max(0, page - 1)
|
||||
}
|
||||
imageView.image = images[page]
|
||||
}
|
||||
|
||||
func getImages() -> [UIImage] {
|
||||
let fileManager = FileManager.default
|
||||
let supportedExtensions = ["png", "jpg", "jpeg"]
|
||||
|
||||
guard
|
||||
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first
|
||||
else {
|
||||
print("Documents directory not found.")
|
||||
return []
|
||||
}
|
||||
|
||||
do {
|
||||
let contents = try fileManager.contentsOfDirectory(
|
||||
at: documentsURL, includingPropertiesForKeys: nil)
|
||||
|
||||
var images: [UIImage] = []
|
||||
|
||||
for file in contents {
|
||||
if supportedExtensions.contains(file.pathExtension.lowercased()) {
|
||||
print("Loading image: \(file.lastPathComponent)")
|
||||
if let image = UIImage(contentsOfFile: file.path) {
|
||||
images.append(image)
|
||||
} else {
|
||||
print("Failed to load image.")
|
||||
}
|
||||
} else {
|
||||
print("No image files found in Documents directory.")
|
||||
}
|
||||
}
|
||||
return images
|
||||
} catch {
|
||||
print("Error reading contents of Documents directory: \(error)")
|
||||
}
|
||||
return []
|
||||
}
|
||||
func createTestFile() {
|
||||
if let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
|
||||
.first
|
||||
{
|
||||
let fileURL = documentsURL.appendingPathComponent("dummy.txt")
|
||||
let data = ".".data(using: .utf8)!
|
||||
try? data.write(to: fileURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
run.sh
4
run.sh
@@ -7,6 +7,8 @@ DERIVED_DATA="./build"
|
||||
SIMULATOR='iOS Simulator'
|
||||
COMPANY='ImageViewer'
|
||||
|
||||
sudo bash -c "echo '127.0.0.1 developerservices2.apple.com' >>/etc/hosts"
|
||||
|
||||
# Boot and launch simulator
|
||||
xcrun simctl boot "$SIM_DEVICE"
|
||||
open -a Simulator
|
||||
@@ -18,3 +20,5 @@ xcodebuild -scheme "$SCHEME" -derivedDataPath "$DERIVED_DATA" -destination "plat
|
||||
APP_PATH="$DERIVED_DATA/Build/Products/Debug-iphonesimulator/$APP_NAME.app"
|
||||
xcrun simctl install booted "$APP_PATH"
|
||||
xcrun simctl launch booted "$COMPANY.$APP_NAME"
|
||||
|
||||
sudo sed -i '' '/developerservices2\.apple\.com/d' /etc/hosts
|
||||
|
||||
Reference in New Issue
Block a user