Splitting an image into a grid in Swift - arrays

I am attempting to split an image into 10'000 smaller ones, by dividing the image into a grid of 100x100. When I run the code, I receive an Error_Bad_Instruction.
import Cocoa
import CoreImage
public class Image {
public var image: CGImage?
public func divide() -> [CGImage] {
var tiles: [CGImage] = []
for x in 0...100 {
for y in 0...100 {
let tile = image?.cropping(to: CGRect(x: (image?.width)!/x, y: (image?.height)!/y, width: (image?.width)! / 100, height: (image?.height)! / 100 ))
tiles.append(tile!)
}
}
return tiles
}
public init(image: CGImage) {
self.image = image
}
}
var img = Image(image: (NSImage(named: "aus-regions.jpg")?.cgImage(forProposedRect: nil, context: nil, hints: nil))!)
var a = img.divide()

There are some problems with the code that you posted:
forced wrapping the image
loops go including 100 - you just need 99. Or as a comment suggests: for x in 0..<100
you divide by 0 - in the first step
your algorithm, is not forming a grid.
I've updated to obtain a grid, using an image that I had on my computer, and with widths and heights just of 10, because it using 100 was too long.
import Cocoa
import CoreImage
let times = 10
public class Image {
public var image: CGImage?
public func divide() -> [CGImage] {
var tiles: [CGImage] = []
for x in 0..<times {
for y in 0..<times {
let tile = image?.cropping(to: CGRect(x: x * (image?.width)!/times, y: y * (image?.height)!/times, width: (image?.width)! / times, height: (image?.height)! / times ))
tiles.append(tile!)
}
}
return tiles
}
public init(image: CGImage) {
self.image = image
}
}
if let image = NSImage(named: "bg.jpg")?.cgImage(forProposedRect: nil, context: nil, hints: nil) {
var img = Image(image: image)
var a = img.divide()
}

Related

How to show Firebase images in SwiftUI using an ImageSlider or something similar

I have been trying to do this for some time, but have not been able to achieve my goal. I would like to show images stored at Firebase in an Image Slider or carousel in SwiftUI. I am able to fetch and show a single image (that's a different thing). However, unable to load images from firebase to slider. The first part is to get url of images which I am not getting how to do it.
Update
This loads images but cant auto scroll or auto iterate, images load one after another than stops:
func downImage(){
let imageRef = Storage.storage()
let loc = imageRef.reference().child("Images")
loc.listAll{(result,error) in
if error != nil{
print("Ooops : ", error)
return
}
for item in result!.items{
let count = result?.items.count ?? 5
//provides actual count ie 3
print("number of Images: ", self.count)
print("images url : " , item)
item.getData(maxSize: 15 * 1024 * 1024) { data, error in
if let error = error {
print("\(error)")
}
guard let data = data else { return }
DispatchQueue.main.async {
self.count = count
// self.actualImage.append(self.image)
self.image = UIImage(data: data as Data)!
}}}}}}
In Slider, images are shown but stops after loading third image:
struct ImageSlider: View {
#StateObject var imageList = FireImageModel()
/* private let timer = Timer.publish(every: 3, on: .main, in: .common)
.autoconnect() */
var body: some View {
TabView(){
/* ForEach(imageList.actualImage, id: \.self) { item in
if foreach loop is used with actualImage appending Images no
image is shown or Index out of range error */
Image(uiImage: imageList.image)
.resizable()
.scaledToFill()
}
.tabViewStyle(PageTabViewStyle())
.animation(.easeInOut(duration: 3))
.onAppear(){
imageList.downImage()
}
}
If I apply the timer (commented out) with other settings it keeps updating selected image 0, selected image 1 but image never changes. Need images to auto scroll in this slider.
As I understand your code, you are downloading the string url from Firebase into
#Published var imageURL: [String] = []. This does not download the image data, just the url string.
In your loadImageURL, try this, to store all the url strings into your imageURL array:
DispatchQueue.main.async {
if let linq = link {
self.imageURL.append(linq)
print("url link : ", self.imageURL)
}
In your ImageSlider, use something like this, instead of your Image(item):
AsyncImage(url: URL(string: item))
also, change #ObservedObject var imageList = FireImageModel() to #StateObject var imageList = FireImageModel()
and, do not use !, anywhere in your code, it is a recipe for disaster.
EDIT-1:
Given your new code, try this simple example code, to try to find where your problem is:
struct ImageSlider: View {
#StateObject var imageList = FireImageModel()
var body: some View {
ScrollView {
ForEach(imageList.actualImage, id: \.self) { item in
Image(uiImage: item)
.resizable()
.frame(width: 222, height: 222)
}
.onAppear {
imageList.downImage()
}
}
}
}
Also, do not use ! anywhere in your code.
If that works, try this:
struct ImageSlider: View {
#StateObject var imageList = FireImageModel()
var body: some View {
VStack {
if imageList.actualImage.count > 0 {
TabView {
ForEach(imageList.actualImage, id: \.self) { item in
Image(uiImage: item)
.resizable()
.scaledToFill()
}
}
.tabViewStyle(PageTabViewStyle())
} else {
ProgressView()
}
}
.onAppear {
imageList.downImage()
}
}
}
You may also want to clean your FireImageModel, such as:
class FireImageModel: ObservableObject {
#Published var actualImage: [UIImage] = []
func downImage() {
let imageRef = Storage.storage()
let loc = imageRef.reference().child("Images")
loc.listAll{(result,error) in
if error != nil{
print("Ooops : ", error)
return
}
if let images = result {
print("number of Images: ", images.items.count)
for item in images.items {
print("images url : " , item)
item.getData(maxSize: 15 * 1024 * 1024) { data, error in
if let error = error {
print("\(error)")
}
DispatchQueue.main.async {
if let theData = data, let image = UIImage(data: theData) {
self.actualImage.append(image)
}
}
}
}
}
}
}
}
I was able to display images in slider final prob was auto iteration. ForEach was throwing index out of range error(known prob) when directly using an array having image data so ans by #workingdogUkraine helped to resolve the error by putting check before entering the foreach. Auto iteration is done. Look into updated clean FireImageModel model by WorkingDog, below is the slider struct:
struct ImageSlider: View {
#StateObject var imageList = FireImageModel()
#State private var currentIndex = 0
#State var zoomed = false
private let timer = Timer.publish(every: 3, on: .main, in: .common)
.autoconnect()
var body: some View {
// 2
VStack {
if imageList.actualImage.count > 0 {
GeometryReader{ proxy in
TabView(selection: $currentIndex){
ForEach(imageList.actualImage.indices, id: \.self) {
item in
let sot = imageList.actualImage[item]
Image(uiImage: sot)
.resizable()
.scaledToFill()
.onTapGesture {
self.zoomed.toggle()
}
.scaleEffect(self.zoomed ? 1.73 : 1)
}
.onReceive(timer, perform: {_ in
withAnimation(.spring(response: 1,
dampingFraction: 1, blendDuration: 1)){
currentIndex = currentIndex <
imageList.actualImage.count ? currentIndex + 1 : 0
}
})
}
.tabViewStyle(PageTabViewStyle())
.frame(width: proxy.size.width, height: proxy.size.height/3)
}
}else {
ProgressView()
}
}.background(.ultraThinMaterial)
.onAppear {
imageList.downImage()
}
}}
Images appear with non noticeable animation :), will like to add some cool animation, after every 3 seconds image changes

ImageView inside of ScrollView showing wrong picture

I have a scroll view that has an image view embedded into it. The user clicks the "Take Photo" button, takes a picture, and then those photos are stored in an array and then displayed on the scrollable imageView. My issue is that after the didFinishSelectingMedia is called and the camera closes, the imageView always shows the first image in the array. I want the imageView to show the most recent image added to the array and scroll backward through the images until the user comes to the first image in the array. How do I make this happen?
Btw: Idk if this is even plausible, but I tried reversing the for loop and that didn't work.
I'm a new programmer, so please explain your answers.
Here's my code:
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet weak var scrollImageView: UIScrollView!
var imageTaken: [UIImage] = []
var imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
scrollImageView.frame = scrollImageView.frame
}
#IBAction func takePhotoButton(_ sender: Any) {
imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .camera
present(imagePicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
imageTaken.append((info[.originalImage] as? UIImage)!)
imagePicker.dismiss(animated: true, completion: nil)
for i in 0..<imageTaken.count {
let imageView = UIImageView()
imageView.image = imageTaken[i]
imageView.contentMode = .scaleAspectFit
let xPosition = self.scrollImageView.frame.width * CGFloat(i)
imageView.frame = CGRect(x: xPosition, y: 0, width: self.scrollImageView.frame.width, height: self.scrollImageView.frame.height)
scrollImageView.contentSize.width = scrollImageView.frame.width * CGFloat(i + 1)
scrollImageView.addSubview(imageView)
}
}
}
Two options I guess you can take which ever one is seems easier.
OOP — Create an array of objects example:
struct TakePhotos {
var image : UIImage
var timestamp : Date
}
Then sort the array by the timestamp.
Use PHAsset
import Photos
Using PHAsset you should be able to retrieve the images taken from the user's album. Set a count when the user takes a new image.
Then you can run a query and sort the images in reverse order, they will already have the timestamp.
let photoOptions = PHFetchOptions()
photoOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
This is what I did in my application.
// FIXME: - Query only happens once this needs to be refactored so it happens when the album is updated on the device.
// Query only happens one time.
if imageArray.isEmpty {
let photoOptions = PHFetchOptions()
photoOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let allPhotos = PHAsset.fetchAssets(with: photoOptions)
let fetchOptions = PHImageRequestOptions()
fetchOptions.deliveryMode = .highQualityFormat
fetchOptions.resizeMode = .exact
fetchOptions.normalizedCropRect = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.width)
// Must be a synchronous call otherwise the view loads before images are retrieved (result is an empty view).
fetchOptions.isSynchronous = true
let imageSize : CGSize = CGSize(width: view.frame.width, height: view.frame.height)
//Retrieves Images using assets.
allPhotos.enumerateObjects { (assets, index, pointer) in
DispatchQueue.global(qos: .background).sync {
PHImageManager.default().requestImage(for: assets, targetSize: imageSize, contentMode: .aspectFit, options: fetchOptions) { (image, hashDictionary) in
guard let image = image else {return}
self.imageArray.append(image)
self.albumImages.append(ImageAlbum(images: image))
}
}
}
selectedImage.image = imageArray.first
selectedImage.contentMode = .scaleAspectFill
}

SwiftUI + Scenekit + Combine + GeometryReader ==> Scrollable List of 3D Models + Rotation of 3D Models By Scrolling the List

I want to create a scrollable list of 3D models(here: default ship model of Xcode which you can find in a Game project of Xcode).
For that I've created 3 View structures:
one for viewing the 3D model (called ScenekitView)
one for a CardView (called MyCardView)
one for the scrollable list of 3D models(called MyCardsListView)
Here are the Structures:
ScenekitView
(btw I added the art.scnassets folder of Xcode's default Game project to my Single View App project)
import SwiftUI
import SceneKit
struct ScenekitView : UIViewRepresentable {
#Binding var yRotation : Float
let scene = SCNScene(named: "art.scnassets/ship.scn")!
func makeUIView(context: Context) -> SCNView {
// create and add a camera to the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
// place the camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
// create and add a light to the scene
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = .omni
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)
// create and add an ambient light to the scene
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = .ambient
ambientLightNode.light!.color = UIColor.darkGray
scene.rootNode.addChildNode(ambientLightNode)
// retrieve the ship node
let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!
// animate the 3d object
ship.eulerAngles = SCNVector3(x: 0, y: self.yRotation * Float((Double.pi)/180.0) , z: 0)
// retrieve the SCNView
let scnView = SCNView()
return scnView
}
func updateUIView(_ scnView: SCNView, context: Context) {
scnView.scene = scene
// allows the user to manipulate the camera
scnView.allowsCameraControl = true
// show statistics such as fps and timing information
scnView.showsStatistics = true
// configure the view
scnView.backgroundColor = UIColor.black
}
}
#if DEBUG
struct ScenekitView_Previews : PreviewProvider {
static var previews: some View {
ScenekitView(yRotation: .constant(0))
}
}
#endif
MyCardView
import SwiftUI
struct MyCardView : View {
#Binding var yRotation: Float
var body: some View {
VStack(spacing: 0) {
ZStack {
Rectangle()
.foregroundColor(.black)
.opacity(0.8)
.frame(width:250, height: 70)
Text( "Ship \( self.yRotation )" )
.foregroundColor(.white)
.font(.title)
.fontWeight(.bold)
}
ScenekitView(yRotation: self.$yRotation )
.frame(width: 250, height: 250)
}
.cornerRadius(35)
.frame(width: 250,height: 320)
}
}
#if DEBUG
struct MyCardView_Previews : PreviewProvider {
static var previews: some View {
MyCardView(yRotation: .constant(90))
}
}
#endif
MyCardsListView
import SwiftUI
struct MyCardsListView: View {
#State var yRotation: Float = 0
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
ForEach(0...2) { number in
GeometryReader { geometry in
MyCardView(yRotation: self.$yRotation )
.rotation3DEffect(Angle(degrees: Double(geometry.frame(in: .global).minX) / -20), axis: (x: 0, y: 10, z: 0))
}
.padding()
.frame(width: 250, height: 350)
}
}
}
}
}
#if DEBUG
struct Product3DView_Previews: PreviewProvider {
static var previews: some View {
MyCardsListView()
}
}
#endif
In MyCardsListView I'm getting each Card's minimum X position using GeometryReader
My goal is:
while I'm scrolling the Cards in MyCardsListView, every ship model should rotate with respect to its Card's minimum X position (all of them shouldn't rotate with same rotation angle at the same time)
(I know I might made lots of mistakes in using Bindings, States and etc, Sorry about that)
There are many problems in your code:
You never assign a value to yRotation.
You only have one yRotation variable in MyCardsListView, but because you want to have different rotation values, depending on the view's position, you need one yRotation variable for each view.
The following line:
ship.eulerAngles = SCNVector3(x: 0, y: self.yRotation * Float((Double.pi)/180.0) , z: 0)
is inside the makeUIView() method, but that method is only executed once (when your view is created). You want the value to update continously, so you should assign the value in updateUIView() instead.
You should definitely watch WWDC sessions 226: Data Flow in SwiftUI and 231: Integrating SwiftUI.

UIImage isn't showing up in UIScrollView

Im grabbing the photos from library using a cocoa pod called BSImagePicker. The Images are then stored into a photoArray. I want to take those images from the array and present them in an image view that's held inside the scrollView. However the images aren't showing up at all.
This is what I've currently tried.
This is the code that the cocoa pod uses to store images into the array:
func openImagePicker() {
let vc = BSImagePickerViewController()
self.bs_presentImagePickerController(
vc,
animated: true,
select: { (assest: PHAsset) -> Void in },
deselect: { (assest: PHAsset) -> Void in },
cancel: { (assest: [PHAsset]) -> Void in },
finish: { (assest: [PHAsset]) -> Void in
for i in 0..<assest.count {
self.SelectedAssets.append(assest[i])
}
self.convertAssetToImages()
},
completion: nil
)
print(photoArray)
setupImages(photoArray)
}
this is the code that converts asset to image
extension addImages {
func convertAssetToImages() -> Void {
if SelectedAssets.count != 0{
for i in 0..<SelectedAssets.count{
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.isSynchronous = true
let width = scrollView.frame.width
let height = scrollView.frame.height
manager.requestImage(for: SelectedAssets[i], targetSize: CGSize(width: width, height: height), contentMode: .aspectFill, options: option, resultHandler: {(result,info) -> Void in
thumbnail = result!
})
let data = thumbnail.jpegData(compressionQuality: 1)
let newImage = UIImage(data: data! as Data)
self.photoArray.append(newImage! as UIImage)
}
}
}
}
this code takes image from photo array into scrollview
func setupImages(_ images: [UIImage]){
for a in 0..<photoArray.count {
let theImage = UIImageView()
theImage.image = self.photoArray[a]
let xPosition = UIScreen.main.bounds.width * CGFloat(a)
self.scrollView.frame = CGRect(x: xPosition, y: 0, width: self.scrollView.frame.width, height: self.scrollView.frame.height)
theImage.contentMode = .scaleAspectFit
self.scrollView.contentSize.width = self.scrollView.frame.width * CGFloat(a + 1)
self.scrollView.addSubview(theImage)
}
}
I have no idea why it isn't working.

Generate imageView based off array.count - Swift

I am trying to generate a section of image views which will sit at the top of an application and update as the user progresses through the quiz.
My array variable is Qs, the code I have to generate an imageView is as follows:
var imageView: UIImageView!
var i = 0
var total = Int(Qs.capacity) // Just testing what .capacity does.
for i in 0..<(Qs.count-1){
imageView - UIImageView(frame: CGRect(x: 0, y: 75, width: 50, height: 50))
imageView.image = UIImage(named:"InfoIcon")
imageView.contentMode = .scaleAspectFit
self.view.addSubView(imageView)
}
I already have a variable which tracks the users progress through the quiz with just an integer if that would be any help, its declared along with the quiz functionality.
Here is a fantastically draw visual of what I am trying to accomplish:
Any help is appreciated,
thanks
Lets assume you have a container view attached at the top, then you can use this method to add any number of imageView's.
func addProgressImages(to view: UIView, count: Int) {
let screenSize = UIScreen.main.bounds.size
for i in 0..<count {
let spacing: CGFloat = 2
let tag = i + 888
let width: CGFloat = screenSize.width/CGFloat(count)
let height: CGFloat = width
if let view = view.viewWithTag(tag) {
view.removeFromSuperview()
}
let x = spacing + CGFloat(i) * width
let imageView = UIImageView(frame: CGRect(x: x, y: 100, width: width - spacing, height: height))
imageView.image = UIImage(named:"InfoIcon")
imageView.tag = tag
imageView.contentMode = .scaleAspectFit
view.addSubview(imageView)
}
}
You can play with y value for vertical alignment.

Resources