Posts

Post not yet marked as solved
11 Replies
4.7k Views
Hi guys, I hope you are doing fine. I am trying to achieve following thing: ) Fetch data array from database (async call) ) Iterate over fetched data array ) Fetch additional information about each object (async call) ) Create a new data array with all the information and return it back Currently, I have following approach self.dataAccessService.fetchRepliesByCommentId(completionHandler: { (commentReplyArray) in       for var i in 0..<commentReplyArray.count {         let commentReply = commentReplyArray[i]         let commentItem = CommentItem()                   self.fetchDetailsAboutCommentReply(commentReplyObject: commentReply) { (commentItem) in           commentItem.commentObject = commentReply                       dataSource.insert(commentItem, at: index + i + 1) -> APP CRASHES HERE, i is never 0 here           ips.append(IndexPath(row: index + i + 1 , section: 0))                                                                      if (i == commentReplyArray.count - 1) {             self.delegate?.didLoadReplies(dataSource: dataSource, ips: ips)           }         }       }     }, commentId: commentItem.commentObject.id) My fetchDetailsAboutCommentReply function: private func fetchDetailsAboutCommentReply(commentReplyObject:CommentReply, completionHandler:@escaping(CommentItem)->()) {      let group = DispatchGroup()      let commentItem = CommentItem()            group.enter()       self.dataAccessService.fetchUserById(completionHandler: { (userObject) in         commentItem.userObject = userObject         group.leave()      }, uid: commentReplyObject.userId)                   group.enter()       self.dataAccessService.fetchDownloadURLOfProfileImage(organizerId: commentReplyObject.userId) { (contentURL) in       commentItem.userObject.contentURL = contentURL       group.leave()      }           group.notify(queue: .main) {       completionHandler(commentItem)     }   } My question is how, I can change my code, so the loop basically "pauses" until I fetch every detail information of the iterated object, add it into the dataSource Array and then continues with the next one? Thanks and stay healthy!
Posted Last updated
.
Post not yet marked as solved
5 Replies
992 Views
So the use case I want to do is easy. ) Select a video from library ) Trim video and apply a CIFilter  ) Upload it to FireStore Here is my code: ) Select a video from library func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {     var image = UIImage()            let applyEffectController = STORYBOARD.instantiateViewController(withIdentifier: "applyEffects") as! ApplyEffectsViewController                 if let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String {       if mediaType == "public.image" {         let pickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage         image = pickedImage!       }                    if mediaType == "public.movie" {              let videoURL = info[.mediaURL] as! URL         image = AVUtil.createThumbnail(videoURL: videoURL)                   let avAsset = AVUtil.trimVideo(videoURL: videoURL)         let fileManager = FileManager.default         let documents = try! fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)                   try! avAsset.export(to: documents.appendingPathComponent(VIDEO_NAME))                   applyEffectController.avAsset = avAsset         applyEffectController.IS_VIDEO_SELECTED = true       }     }     self.dismiss(animated: true, completion: nil)                applyEffectController.challengeImage = image     self.navigationController?.pushViewController(applyEffectController, animated: true)   } ) Trim video and apply a CIFilter  func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {     var chosenFilter = filterImagesArray[indexPath.row]     chosenFilter = stripFileExtension(chosenFilter)           let contentFilter = CIFilter(name: chosenFilter)     let challengeCIImage = CIImage(image: challengeImage)               contentFilter!.setValue(challengeCIImage, forKey: kCIInputImageKey)     let editedChallengeImage = contentFilter!.value(forKey: kCIOutputImageKey) as! CIImage     challengeImageView.image = UIImage(ciImage: editedChallengeImage)          if (IS_VIDEO_SELECTED) {       playerItem?.videoComposition = AVVideoComposition(asset: avAsset!, applyingCIFiltersWithHandler: { (request) in         let source = request.sourceImage.clampedToExtent()         contentFilter?.setValue(source, forKey: kCIInputImageKey)                   _ = CMTimeGetSeconds(request.compositionTime)                   let output = contentFilter?.outputImage!.cropped(to: request.sourceImage.extent)                   request.finish(with: output!, context: nil)       })     }   } Confirm filter and redirect to the view controller where I do the upload @objc func confirmButton_clicked() {     let createChallengeController = STORYBOARD.instantiateViewController(withIdentifier: "createChallenge") as! CreateChallengeViewController     createChallengeController.IS_VIDEO_SELECTED = IS_VIDEO_SELECTED     createChallengeController.challengeImage = challengeImageView.image!     createChallengeController.challengeObject = challengeObject               if (IS_VIDEO_SELECTED) {       let fileManager = FileManager.default       let documents = try! fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)               if (fileManager.fileExists(atPath: documents.appendingPathComponent(EDITED_VIDEO_NAME).path)) {         DispatchQueue.main.async(execute: {           try! fileManager.removeItem(atPath: documents.appendingPathComponent(EDITED_VIDEO_NAME).path)         })       }               let videoExport = AVAssetExportSession(asset: (playerItem?.asset)!, presetName: AVAssetExportPresetMediumQuality)       videoExport?.outputFileType = .mov       videoExport?.outputURL = documents.appendingPathComponent(EDITED_VIDEO_NAME)       videoExport?.videoComposition = playerItem?.videoComposition       createChallengeController.challengeImage = self.challengeImageView.image!       print(videoExport?.outputURL)               let group = DispatchGroup()       group.enter()       self.view.addSubview(activityView)               videoExport?.exportAsynchronously(completionHandler: {         createChallengeController.videoURL = videoExport?.outputURL         group.leave()       })               group.notify(queue: .main) {         self.navigationController?.pushViewController(createChallengeController, animated: true)         self.activityView.removeFromSuperview()       }     }     else {       self.navigationController?.pushViewController(createChallengeController, animated: true)     }   } *3.) Upload it to FireStore var videoData = NSData() &#9;&#9;do { &#9;&#9;&#9;&#9;videoData = try NSData(contentsOf: self.videoURL!, options: .dataReadingMapped) &#9;&#9;} &#9;&#9;catch { &#9;&#9;&#9;&#9;cancelChallenge(challengeId: challengeId) // always goes here -> it fails &#9;&#9;&#9;&#9;return &#9;&#9;} The exception I get is: "The file “editedVideo.mov” couldn’t be opened because there is no such file." The approach above works with videos which are about 30-35 sec. (without trmming). If I upload a video which is about two minutes, then it always fails. I need your help. Thank you and stay healthy! *
Posted Last updated
.
Post not yet marked as solved
0 Replies
2k Views
Hello everyone, I have been struggling a lot with this issue: Using an UITableView, I want to use a custom UITableViewCell which contains labels and an image view. Depending on how long the text is, which I want to display on the labels, the height of the cell should be adjusted automatically. Currently, I have this approach in my viewDidLoad method:   activityTableView.translatesAutoresizingMaskIntoConstraints = false     self.view.addSubview(activityTableView)     activityTableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true     activityTableView.widthAnchor.constraint(equalToConstant: width).isActive = true     activityTableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: height * (60 / IPHONE8_SCREEN_HEIGHT)).isActive = true     activityTableView.heightAnchor.constraint(equalToConstant: height * (554 / IPHONE8_SCREEN_HEIGHT)).isActive = true     //activityTableView.frame = CGRect(x: 0, y: navigationBar.frame.maxY, width: width, height: height * (554 / IPHONE8_SCREEN_HEIGHT))     activityTableView.delegate = self     activityTableView.dataSource = self     activityTableView.register(ActivityTableViewCell.self, forCellReuseIdentifier: "activityCell")       activityTableView.estimatedRowHeight = 92     activityTableView.rowHeight = UITableView.automaticDimension       Furthermore, I have delegates: extension ActivityViewController:UITableViewDelegate {   func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {     return UITableView.automaticDimension   }   } My custom table view cell func initActivityLabel() {     activityLabel.translatesAutoresizingMaskIntoConstraints = false     self.contentView.addSubview(activityLabel)     activityLabel.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 81).isActive = true     activityLabel.widthAnchor.constraint(equalToConstant: cellWidth * (276 / cellWidth)).isActive = true     activityLabel.topAnchor.constraint(greaterThanOrEqualTo: self.contentView.topAnchor, constant: 13).isActive = true     activityLabel.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -38).isActive = true           activityLabel.setColorForLabelTitle()     activityLabel.font = .boldSystemFont(ofSize: LABEL_TEXT_SIZE)     activityLabel.numberOfLines = 0     activityLabel.lineBreakMode = .byWordWrapping     activityLabel.sizeToFit()   }       func initActivityButton() {     self.activityButton.translatesAutoresizingMaskIntoConstraints = false     self.contentView.addSubview(activityButton)           activityButton.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 250).isActive = true     activityButton.widthAnchor.constraint(equalToConstant: 131).isActive = true     activityButton.topAnchor.constraint(equalTo:self.activityLabel.bottomAnchor, constant: 0).isActive = true     activityButton.heightAnchor.constraint(equalToConstant: 40).isActive = true         activityButton.addTarget(self, action: #selector(activityButton_clicked), for: .touchDown)     activityButton.setColor()     activityButton.roundCorners()     self.layoutIfNeeded()   } Those init-functions are called within this method func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {     let activityCell = tableView.dequeueReusableCell(withIdentifier: "activityCell", for: indexPath) as! ActivityTableViewCell     activityCell.selectionStyle = .none     activityCell.sizeToFit()         activityCell.backgroundColor = DARK_BLUE_COLOR     let activityObject = activityArray[indexPath.row]       &#9;&#9; activityCell.initActivityLabel() .. &#9;&#9; As you can see, everything is done programmatically. I need to do it like this, so I was wondering if you could give me any hints. When I run the code, the table view doesn't show the label at all. I get a warning regarding a constraint violation. But why?
Posted Last updated
.
Post not yet marked as solved
1 Replies
1.1k Views
Hello everyone, I am currently working on having an application to display comments with respective replies. I can show all the comments easily in a table view having a simple array of comment objects. In case, one of the comment has any replies (in the database stored), I need to load those replies - which are represented as a different objects at the moment - and display them under the correct comment. For the table view, this means, new rows are going to be inserted. I tried to code this but somehow, this ends up in an endless loop. Maybe you guys can give me a hint? This is my function to load the comments first func fetchCommentsByParticipationId() {     dataAccessService.fetchCommentsByParticipationId(completionHandler: { (commentArray) in       self.commentArray = commentArray       self.commentTableView.reloadData()     }, participationId: participationObject.id)   } I have two arrays as data sources. One contains the comments and the other ones contains the comment replies  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {     return commentArray.count + commentReplyArray.count   } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {     let commentsTableViewCell = tableView.dequeueReusableCell(withIdentifier: "CommentsTableViewCell") as! CommentsTableViewCell     commentsTableViewCell.backgroundColor = DARK_BLUE_COLOR           let cellHeight = commentsTableViewCell.frame.height     let cellWidth = commentsTableViewCell.frame.width           let commentObject = commentArray[indexPath.row] // Comment(json: (hitsTableController.hitsSource?.hit(atIndex: indexPath.row))!)           commentsTableViewCell.initSenderLabel(cellWidth: cellWidth, cellHeight: cellHeight)     commentsTableViewCell.initReplyButton(cellWidth: cellWidth, cellHeight: cellHeight)     dataAccessService.fetchUserById(completionHandler: { (userObject) in       commentsTableViewCell.senderLabel.text = userObject.firstName + " " + userObject.lastName     }, uid: commentObject.userId)           commentsTableViewCell.initCommentLabel(cellWidth: cellWidth, cellHeight: cellHeight)     commentsTableViewCell.commentTextView.text = commentObject.comment           commentsTableViewCell.replyButtonAction = { [unowned self] in       self.commentReplyObject.commentId = commentObject.id       self.commentReplyObject.userId = commentObject.userId       var replyToUserName = commentsTableViewCell.senderLabel.text!       replyToUserName = replyToUserName.replacingOccurrences(of: " ", with: "")               self.replyToUser(replyToUserName: replyToUserName)     }           // fetch replies to comments     dataAccessService.fetchRepliesByCommentId(completionHandler: { (_commentReplyArray) in       for commentReply in _commentReplyArray {         self.commentReplyArray.append(commentReply)         self.commentTableView.insertRows(at: [           (NSIndexPath(row: self.commentArray.count-1, section: 0) as IndexPath)], with: .bottom)         commentsTableViewCell.initReplyViews(cellWidth: cellWidth, cellHeight: cellHeight)                   commentsTableViewCell.senderLabel.text = "Test"         commentsTableViewCell.commentTextView.text = commentReply.comment       }     }, commentId: commentObject.id)     return commentsTableViewCell   } ``` What would be the best idea to achieve what I want to do?
Posted Last updated
.
Post marked as solved
2 Replies
1.1k Views
I am using the sdk from OneSignal. Let's assume, I have following use case:User A use my app in German language. He triggers an action within the app e. g. a like on an image of User B. User B gets a push notification about the like. But User B uses the app in English language. How can I achieve that?I mean, I can send the notification message in a particular language but if User B decides to change the language of the mobile device to Turkish, then I have to be flexibel.Is there any way, I can access the incoming push notification on the target device?Currently, my code for sending the notification from the source device looks like this:public func notifyAboutSubscription(userObject:User, receiverArray:[String]) { var receiverArray = removeChallengeCreatorTokenFromArray(receiverArray: receiverArray) notificationTypeService.clearReceiverListForNotificationType(completionHandler: { (clearedReceiverArray) in receiverArray = clearedReceiverArray let source = self.determineUserType(userObject: userObject) OneSignal.postNotification(["contents": ["en": source + NSLocalizedString(FOLLOW_MESSAGE, comment: "")], "include_player_ids": receiverArray]) }, receiverList: receiverArray, notificationType: NotificationType.follow) }I am thankful for any hintsBest regards,Nazar Medeiros
Posted Last updated
.
Post not yet marked as solved
8 Replies
895 Views
Hello everyone,I am working with auto layouts for the first time, but I am a but confused. Let me explain you my problem by showing the code I havefunc setupView() { self.view.translatesAutoresizingMaskIntoConstraints = false var createdButton = UIButton() createdButton.setTitle(CREATED_BUTTON_TITLE, for: .normal) createdButton.setTitleColor(DARK_BLUE_COLOR, for: .normal) createdButton.backgroundColor = UIColor.white createdButton.addTarget(self, action: #selector(fetchMyChallenges), for: .touchDown) createdButton.layer.borderWidth = 1.5 createdButton.layer.borderColor = UIColor.white.cgColor self.view.addSubview(createdButton) createdButton.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true createdButton.widthAnchor.constraint(equalTo: self.view.widthAnchor, constant: self.view.frame.width / 3).isActive = true createdButton.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 60).isActive = true createdButton.heightAnchor.constraint(equalTo: self.view.heightAnchor, constant: 54).isActive = true ...So basically, I just want to show a button with some properties set on my view controller. The button appears correctly with the code above, however, the view (self.view) itself is black. I expect it to be black.My questions:1.) Do I always have to set translatesAutoresizingMaskIntoConstraints for super views? Like in this case for self.view?2.) What is actually the correct way of solving it? Is my approach correct? If yes, how I do avoid that black background color?Best regards,Nazar Medeiros
Posted Last updated
.
Post not yet marked as solved
7 Replies
494 Views
Hi everyone,so I haven't understood one thing:Basically, I don't use the storyboard to create the user interface since for complex UIs like having multiple subviews in one container view etc. using the storyboard is a pain.I am using the good old way with the frames. First I created a template for the iPhone 8 screen using Adobe CC, then I started coding the UI in Xcode by having a look at the frames. For example, I am creating a label like this: .. let screenSize = UIScreen.main.bounds let width = screenSize.width let height = screenSize.height let accountTypeLabel = UILabel() accountTypeLabel.frame = CGRect(x: width * (53 / width), y: height * (172 / height), width: width * (269 / width), height: height * (37 / height)) accountTypeLabel.text = ACCOUNT_TYPE_LABEL accountTypeLabel.textColor = UIColor.white accountTypeLabel.backgroundColor = DARK_BLUE_COLOR accountTypeLabel.textAlignment = .center let accountTypeLabelLabelHeight = accountTypeLabel.frame.size.height accountTypeLabel.font = UIFont(name: "Kefa", size: accountTypeLabelLabelHeight * (22 / accountTypeLabelLabelHeight)) ..Depending on the iPhone device, we have different widths and heights of course. So I just put them into variables and define width and height of my label relatively to the screensize of the corresponding iPhone.Well, this works great for iPhone 8. But I just tried to run the code for the iPhone 8 Plus and here, the label is a little bit more on the left part of the screen, so it doesn't look the same as for iPhone 8.Now my question, why is that the case? Since I am putting the height and width always relatively to the screensize, I would have expected to have some kind of an "autolayout" functionalityCan anyone help?Thanks!
Posted Last updated
.
Post not yet marked as solved
0 Replies
3.3k Views
Hi everyone,I bought myself a new notebook. Then I transfered all the data from my previous macbook to the new one by using themigration assistant. After that, I created a new certificate (Apple Push Services) in the Apple developer portal. Using the keychain, I created a certificate signing request on the new mac so this means, I revoked the previous certificate in the developer portal and tried to the enrollment from scratch.Now my actual problem: I downloaded the .cer file. Whenever I do a doubleclick on it, the certificate is not imported into the 'My certificates' section in my keychain tool. But I need to export the p12 file, so how can I do that? The 'My certificates' section is completely empty and I am struggling a lot with this issue.When running Xcode, I get this:CodeSign /Users/nazarmedeiros/Library/Developer/Xcode/DerivedData/Delgado-duyvfumilkktardkhfafnctzmbys/Build/Products/Debug-iphoneos/OneSignalNotificationServiceExtension.appex (in target 'OneSignalNotificationServiceExtension' from project 'Delgado') cd /Users/nazarmedeiros/Desktop/My\ IOS\ Apps/Delgado\ Project/Delgado export CODESIGN_ALLOCATE=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate Signing Identity: "Apple Development: Kaan Cayoglu (KG2HH59QBZ)"Provisioning Profile: "iOS Team Provisioning Profile: *" (35ceee17-9abb-4e81-9fc5-6c69a4f40949) /usr/bin/codesign --force --sign B3B2F1EEA1C3E630DEFF25637172AA73A2944277 --entitlements /Users/nazarmedeiros/Library/Developer/Xcode/DerivedData/Delgado-duyvfumilkktardkhfafnctzmbys/Build/Intermediates.noindex/Delgado.build/Debug-iphoneos/OneSignalNotificationServiceExtension.build/OneSignalNotificationServiceExtension.appex.xcent --timestamp=none /Users/nazarmedeiros/Library/Developer/Xcode/DerivedData/Delgado-duyvfumilkktardkhfafnctzmbys/Build/Products/Debug-iphoneos/OneSignalNotificationServiceExtension.appexI also tried to do couple of other things:- Delete all the files under Library/Keychains- Reset all the keychains- Install Catalina again without deleting existing files- Create a new admin user and try it with itOS version: Catalina, 10.15.2Not sure if issue might be caused because having the keychain in iCloud (just a suggestion)Can anyone help me, please?Thanks
Posted Last updated
.
Post not yet marked as solved
0 Replies
415 Views
Hi everyone,I bought myself a new notebook. Then I transfered all the data from my previous macbook to the new one by using themigration assistant. After that, I created a new certificate (Apple Push Services) in the Apple developer portal. Using the keychain, I ceated a certificate signing request.Now my actual problem: I downloaded the .cer file. Whenever I do a doubleclick on it, the certificate is not imported into the 'My certificates' section in my keychain tool. But I need to export the p12 file, so how can I do that? The 'My certificates' section is completely empty and I am struggling a lot with this issue.OS version: Catalina, 10.15.2Can anyone help me, please?Thanks
Posted Last updated
.
Post not yet marked as solved
8 Replies
4.1k Views
Hi everyone,I have an image view which I initialized using following code:... imageView.frame = CGRect(x: width * (20 / width), y: navigationBar.frame.maxY, width: width * (335 / width), height: width * (335 / width)) imageView.contentMode = .scaleToFill imageView.clipsToBounds = true imageView.isUserInteractionEnabled = true ...As you can see, currently the contentMode is set to .scaleToFill. If I use this setting, most of my image look strange though. The scale seems not to fit somehow. If I change the contentMode to .scaleAspectFill, then every image looks fine.However, for my use case, it is required to use .scaleToFill. What can I do to do that? And why is it acting strange using .scaleToFill?
Posted Last updated
.
Post not yet marked as solved
1 Replies
11k Views
Hi everyone,when trying to run pod install or pod update, I am getting following problem in the terminal:[!] Couldn't determine repo type for URL: `https://cdn.cocoapods.org/`: SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: tlsv1 alert protocol version [!] Smart quotes were detected and ignored in your Podfile. To avoid issues in the future, you should not use TextEdit for editing it. If you are not using TextEdit, you should turn off smart quotes in your editor of choice.My podfile looks like this:#source 'https://github.com/CocoaPods/Specs.git' # Uncomment the next line to define a global platform for your project platform :ios, ’10.0’ target 'Sommerabend' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! # Pods for Sommerabend pod 'Firebase/Auth‘ pod 'Firebase/Database’ pod 'Firebase/Storage' pod 'Firebase/Core' pod 'Firebase/Firestore' pod 'Firebase/Messaging' pod 'AZDropdownMenu' pod 'Fusuma' pod 'AlgoliaSearch-Client-Swift', '~&gt; 4.0‘ pod 'SwiftyJSON', '~&gt; 4.0' pod 'OneSignal', '&gt;= 2.6.2', '&lt; 3.0' pod 'InstantSearch', '~&gt; 4.0' target 'OneSignalNotificationServiceExtension' do pod 'OneSignal', '&gt;= 2.6.2', '&lt; 3.0' endWhat is the problem? Can anyone help me, please?
Posted Last updated
.
Post not yet marked as solved
3 Replies
1k Views
Hi everyone,I have a collection view which displays the content of an array that fetches the data from a database (asychronously). First, I only want to fetch 10 records, once the user scrolls down to the end of the collection view, another 10 records should be fetched.I have following code so far:// load challenges from database func loadChallenges() { self.view.addSubview(activityView) self.view.bringSubview(toFront: activityView) algoliaSearch.fetchChallengeInformationBySearchValue(completionHandler: { (challengeArray) in if(challengeArray.count == 0) { self.activityView.removeFromSuperview() return } // if challenge contains already data, clear the existing data and get the new content // search is done through the api of algolia. We search by using indexes. Each index contains 10 records // First, we load 10 objects using index = 0 which is set to 'PAGINATION' self.challengeArray.removeAll() self.challengeArray = challengeArray self.removeBlockedChallengesFromArray(_challengeArray: self.challengeArray) }, searchValue: EMPTY_MESSAGE, page: PAGINATION) } ... // method that should load the next 10 objects when collection view is scrolled down to the end func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { // if we reached the 10 objects already, we can increase the PAGINATION variable and fetch the next 10 records if (challengeArray.count &gt; (10 * i)) { PAGINATION = PAGINATION + 1 i = i + 1 } // if last row of the table view is reached, load the next items if indexPath.row == challengeArray.count - 1 { algoliaSearch.fetchChallengeInformationBySearchValue(completionHandler: { (challengeArray) in // if there is no data, we can return from the function if (challengeArray.count == 0) { return } for challengeObject in challengeArray { // check if data is already stored within the array, avoid duplicates /* The problem is, using algolia api, it is not possible to fetch data between indexes, so therefore we also fetch the first 10 records again. Any idea how to solve that as well? */ let results = self.challengeArray.filter { $0.id == challengeObject.id} if (results.count == 0) { self.challengeArray.append(challengeObject) } } self.mainCollectionView?.reloadData() }, searchValue: "", page: PAGINATION) } }I tried to explain my problem using comments in the code snippt above. Using the code snippets above, my problem is that data is fetched even if I haven't scrolled down in my collection view. I was wondering if anyone could help me? Thanks.
Posted Last updated
.
Post not yet marked as solved
12 Replies
2.1k Views
Hi guys,I want to replace colors of an image using color cube. The methods below work pretty well for any color besides black and white, which means, I can't replace black and white color. Can any one help me why this the case?func render() { let centerHueAngle: Float = defaultHue/360.0 let destCenterHueAngle: Float = replaceColorSlider.value let minHueAngle: Float = (defaultHue - toleranceSlider.value / 2.0) / 360 let maxHueAngle: Float = (defaultHue + toleranceSlider.value / 2.0) / 360 let hueAdjustment = centerHueAngle - destCenterHueAngle let size = 64 var cubeData = [Float](repeating: 0, count: size * size * size * 4) var rgb: [Float] = [0, 0, 0] var hsv: (h : Float, s : Float, v : Float) var newRGB: (r : Float, g : Float, b : Float) var offset = 0 for z in 0 ..&lt; size { rgb[2] = Float(z) / Float(size) // blue value for y in 0 ..&lt; size { rgb[1] = Float(y) / Float(size) // green value for x in 0 ..&lt; size { rgb[0] = Float(x) / Float(size) // red value hsv = RGBtoHSV(rgb[0], g: rgb[1], b: rgb[2]) if hsv.h &lt; minHueAngle || hsv.h &gt; maxHueAngle { newRGB.r = rgb[0] newRGB.g = rgb[1] newRGB.b = rgb[2] } else { hsv.h = destCenterHueAngle == 1 ? 0 : hsv.h - hueAdjustment //force red if slider angle is 360 newRGB = HSVtoRGB(hsv.h, s:hsv.s, v:hsv.v) } cubeData[offset] = newRGB.r cubeData[offset+1] = newRGB.g cubeData[offset+2] = newRGB.b cubeData[offset+3] = 1.0 offset += 4 } } } let b = cubeData.withUnsafeBufferPointer { Data(buffer: $0) } let data = b as NSData let colorCube = CIFilter(name: "CIColorCube")! colorCube.setValue(size, forKey: "inputCubeDimension") colorCube.setValue(data, forKey: "inputCubeData") colorCube.setValue(ciImage, forKey: kCIInputImageKey) if let outImage = colorCube.outputImage { let context = CIContext(options: nil) let outputImageRef = context.createCGImage(outImage, from: outImage.extent) let image = UIImage(cgImage: outputImageRef!, scale: 1.0, orientation: .up) cropImageView.image = image } } func RGBtoHSV(_ r : Float, g : Float, b : Float) -&gt; (h : Float, s : Float, v : Float) { var h : CGFloat = 0 var s : CGFloat = 0 var v : CGFloat = 0 let col = UIColor(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: 1.0) col.getHue(&amp;h, saturation: &amp;s, brightness: &amp;v, alpha: nil) return (Float(h), Float(s), Float(v)) } func HSVtoRGB(_ h : Float, s : Float, v : Float) -&gt; (r : Float, g : Float, b : Float) { var r : Float = 0 var g : Float = 0 var b : Float = 0 let C = s * v let HS = h * 6.0 let X = C * (1.0 - fabsf(fmodf(HS, 2.0) - 1.0)) if (HS &gt;= 0 &amp;&amp; HS &lt; 1) { r = C g = X b = 0 } else if (HS &gt;= 1 &amp;&amp; HS &lt; 2) { r = X g = C b = 0 } else if (HS &gt;= 2 &amp;&amp; HS &lt; 3) { r = 0 g = C b = X } else if (HS &gt;= 3 &amp;&amp; HS &lt; 4) { r = 0 g = X b = C } else if (HS &gt;= 4 &amp;&amp; HS &lt; 5) { r = X g = 0 b = C } else if (HS &gt;= 5 &amp;&amp; HS &lt; 6) { r = C g = 0 b = X } let m = v - C r += m g += m b += m return (r, g, b) }Thanks a lot in advance! I have been struggling a lot with this issue.Best regards,Nazar
Posted Last updated
.
Post not yet marked as solved
3 Replies
1.3k Views
Hi guys,I want to apply a CIFilter on just a selected part of an image. When working with CIFilter, you get an output image. This output imageis edited but is still only a part of the origin image. I want to merge it with the original image again.At the moment, I have following code:let colorCube = CIFilter(name: "CIColorCube")! colorCube.setValue(size, forKey: "inputCubeDimension") colorCube.setValue(data, forKey: "inputCubeData") // overlay is the selected the area, which should be edited let partOfImage = self.imageView.image?.crop(size: overlay.frame.size, offset: overlay.frame.origin) colorCube.setValue(partOfImage, forKey: kCIInputImageKey) if let outImage = colorCube.outputImage { let context = CIContext(options: nil) // when applying CIFilter, we get an output image let outputImageRef = context.createCGImage(outImage, from: outImage.extent) let editedImagePart = UIImage(cgImage: outputImageRef!) // attempt to merge output image with the original image (imageView.image) imageView.image?.drawImageInRect(inputImage: editedImagePart, inRect: overlay.frame) } } ... extension UIImage { // attempt to merge images func drawImageInRect(inputImage: UIImage, inRect imageRect: CGRect) -&gt; UIImage { UIGraphicsBeginImageContext(self.size) self.draw(in: CGRect(x: 0.0, y: 0.0, width: self.size.width, height: self.size.height)) inputImage.draw(in: imageRect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } // function to crop part of the original image func crop(size: CGSize, offset: CGPoint, scale: CGFloat = 1.0) -&gt; UIImage? { let rect = CGRect(x: offset.x * scale, y: offset.y * scale, width: size.width * scale, height: size.height * scale) if let cropped = self.cgImage?.cropping(to: rect) { return UIImage(cgImage: cropped) } return nil } }Does anyone have an idea, how I can do that?
Posted Last updated
.
Post marked as solved
6 Replies
831 Views
Hello experts,I want to create a small image modification app. Purpose is to replace a certain pixel color with another one by including a specific tolcerance value.The concrete 'Replace color' functionality looks like following: func replaceColor(color:SKColor, withColor:SKColor, image:UIImage, tolerance:CGFloat, alpha:CGFloat) -&gt; UIImage{ var result = UIImage() // This function expects to get source color(color which is supposed to be replaced) // and target color in RGBA color space, hence we expect to get 4 color components: r, g, b, a assert(color.cgColor.numberOfComponents == 4 &amp;&amp; withColor.cgColor.numberOfComponents == 4, "Must be RGBA colorspace") // Allocate bitmap in memory with the same width and size as source image let imageRef = image.cgImage let width = imageRef?.width let height = imageRef?.height let colorSpace = CGColorSpace(name: CGColorSpace.sRGB)! let bytesPerPixel = 4 let bytesPerRow = bytesPerPixel * width!; let bitsPerComponent = 8 let bitmapByteCount = bytesPerRow * height! let rawData = UnsafeMutablePointer.allocate(capacity: bitmapByteCount) let context = CGContext(data: rawData, width: width!, height: height!, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue) let rc = CGRect(x: 0, y: 0, width: width!, height: height!) // Draw source image on created context context?.draw(imageRef!, in: rc) // Get color components from replacement color let withColorComponents = withColor.cgColor.components let r2 = UInt8(withColorComponents![0] * 255) let g2 = UInt8(withColorComponents![1] * 255) let b2 = UInt8(withColorComponents![2] * 255) let a2 = UInt8(withColorComponents![3] * 255) // Prepare to iterate over image pixels var byteIndex = 0 while byteIndex &lt; bitmapByteCount { // Get color of current pixel let red:CGFloat = CGFloat(rawData[byteIndex + 0])/255 let green:CGFloat = CGFloat(rawData[byteIndex + 1])/255 let blue:CGFloat = CGFloat(rawData[byteIndex + 2])/255 let alpha:CGFloat = CGFloat(rawData[byteIndex + 3])/255 let currentColor = SKColor(red: red, green: green, blue: blue, alpha: alpha); // Compare two colors using given tolerance value if self.compareColor(color: color, withColor: currentColor , withTolerance: tolerance) { // If the're 'similar', then replace pixel color with given target color rawData[byteIndex + 0] = r2 rawData[byteIndex + 1] = g2 rawData[byteIndex + 2] = b2 rawData[byteIndex + 3] = a2 } byteIndex = byteIndex + 4; } // Retrieve image from memory context let imgref = context!.makeImage() result = UIImage(cgImage: imgref!) // Clean up a bit rawData.deinitialize() return result } func compareColor(color:SKColor, withColor:SKColor, withTolerance:CGFloat) -&gt; Bool { var r1: CGFloat = 0.0, g1: CGFloat = 0.0, b1: CGFloat = 0.0, a1: CGFloat = 0.0; var r2: CGFloat = 0.0, g2: CGFloat = 0.0, b2: CGFloat = 0.0, a2: CGFloat = 0.0; color.getRed(&amp;r1, green: &amp;g1, blue: &amp;b1, alpha: &amp;a1); withColor.getRed(&amp;r2, green: &amp;g2, blue: &amp;b2, alpha: &amp;a2); return fabs(r1 - r2) &lt;= withTolerance &amp;&amp; fabs(g1 - g2) &lt;= withTolerance &amp;&amp; fabs(b1 - b2) &lt;= withTolerance &amp;&amp; fabs(a1 - a2) &lt;= withTolerance; } Everything works so far. But depening on the image, I have a big problem - the time for processing the image. As you can see, the functionality is running within the main thread. I think, it definately makes sense to have a seperate thread for that. But even with threads, it took too much time. Is there any better way to run this function "pretty fast"?'replaceColor' is everytime called, when there is a value change on a UI slider object. For user experience, it is really not good. @objc func toleranceSlider_changed() { let deadlineTime = DispatchTime.now() + .seconds(3) DispatchQueue.main.asyncAfter(deadline: deadlineTime) { //Perform code here self.imageView.image = self.colorUtil.replaceColor(color: (self.COLOR_TO_BE_RELACED), withColor: (self.SELECTED_COLOR), image: (self.imageView.image!), tolerance: CGFloat((self.toleranceSlider.value)), alpha: CGFloat((self.alphaSlider.value))) } }Thanks in advance!
Posted Last updated
.