Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0 - swift

I am using below code in my project. After update to swift 4 and run my app crashed . How can I fix it?
Code:
func from(systemItem: UIBarButtonSystemItem)-> UIImage? {
let tempItem = UIBarButtonItem(barButtonSystemItem: systemItem, target: nil, action: nil)
// add to toolbar and render it
UIToolbar().setItems([tempItem], animated: false)
// got image from real uibutton
let itemView = tempItem.value(forKey: "view") as! UIView
for view in itemView.subviews {
if let button = view as? UIButton, let imageView = button.imageView {
return imageView.image
}
}
return nil
}
}
extension UITextView {
static let ScrollModeBottom = "UITextFieldScrollModeBottom"
static let ScrollModeUp = "UITextFieldScrollModeUp"
static let ScrollModeMiddle = "UITextFieldScrollModeMiddle"
func scrollToBotom() {
let range = NSMakeRange((text as NSString).length - 1, 1);
scrollRangeToVisible(range);
}
var scrollMode: String {
let scrollViewHeight: Float = Float(frame.size.height)
let scrollContentSizeHeight: Float = Float(contentSize.height)
let scrollOffset: Float = Float(contentOffset.y)
if scrollOffset == 0 {
return UITextView.ScrollModeUp
} else if scrollOffset + scrollViewHeight == scrollContentSizeHeight {
return UITextView.ScrollModeBottom
} else {
return UITextView.ScrollModeMiddle
}
}
}
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

// add to toolbar and render it
let bar = UIToolbar()
bar.setItems([tempItem],
animated: false)
bar.snapshotView(afterScreenUpdates: true)
//got image from real uibutton
let itemView = tempItem.value(forKey: "view") as! UIView
for view in itemView.subviews {
if let button = view as? UIButton,
let image = button.imageView?.image {
return image.withRenderingMode(.alwaysTemplate)
}
}
return nil
}
}

Related

Crash on creating an image from UIGraphicsImageRenderer

i'm getting a Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT) when
layer.render(in: context) is called
where i'm trying to create a snapshot image from a custom window
let renderer = UIGraphicsImageRenderer(bounds: bounds, format: .init(for: traitCollection))
return renderer.image { action in
let context = action.cgContext
layer.render(in: context)
}
Use can use bellow extension to get it
private var rendererKey: UInt8 = 0
extension UIView {
var renderer: UIGraphicsImageRenderer! {
get {
guard let rendererInstance = objc_getAssociatedObject(self, &rendererKey) as? UIGraphicsImageRenderer else {
self.renderer = UIGraphicsImageRenderer(bounds: bounds)
return self.renderer
}
return rendererInstance
}
set(newValue) {
objc_setAssociatedObject(self, &rendererKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
func snapImageView() -> UIImageView {
let img:UIImage = renderer.image { ctx in
DispatchQueue.main.async {
layer.render(in: ctx.cgContext)
}
}
let imageView:UIImageView = UIImageView(image: img)
imageView.frame = renderer.format.bounds
imageView.clipsToBounds = true
return imageView
}
}
// Generate image and image view of any view instance
let anImageView = yourView.snapImageView()

UIImage and UILabel disappearing after doing UIRefreshControl

UIImages and UILabel are disappearing in some collectionViews after refreshing inside my app.
here is my code. You can see what is wrong in the picture, the left image is before refreshing and there is a discount price label and after refreshing the simulator, it is gone. getProductLatestData(), getBestSellingData() are the Alamofire get request for API.
//For Refresh Controller
//----------------------
var player : AVAudioPlayer
var refreshController : UIRefreshControl = {
let refreshController = UIRefreshControl()
refreshController.addTarget(self, action: #selector(updateData), for: .valueChanged)
return refreshController
}()
//----------------------
//For Update Data when scroll to top
//----------------------------------
#objc func updateData(){
//To play sound when scroll top
//-----------------------------
let pathToSound = Bundle.main.path(forResource: "Flick", ofType: "wav")
let url = URL(fileURLWithPath: pathToSound!)
do {
player = try AVAudioPlayer(contentsOf: url)
player?.play()
} catch let error {
print(error.localizedDescription)
}
//-----------------------------
//Fetch Latest Products Data
getProductLatestData(pageNumber: 0, pageSize: 0)
//Fetch BestSelling Products Data
getBestSellingData(pageNumber: 0, pageSize: 0)
//Fetch Promotion Products Data
getPromotionProductsData(pageNumber: 0, pageSize: 0)
//Fetch Products By Category
getProductsByCat(pageNumber: 0, pageSize: 0)
//Fetch Reward Products
getRewardProducts()
self.refreshController.endRefreshing()
}
if collectionView == latestItemCV {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "latestItemCell", for: indexPath) as! PromotionItemsCollectionViewCell
if let percent = latestItemData[indexPath.row].promotePercent {
if percent != 0 {
cell.percentOff.isHidden = false
cell.percentOff.text = " \(percent) % Off "
cell.percentOffBackground.isHidden = false
}
else{
cell.percentOff.isHidden = true
cell.percentOffBackground.isHidden = true
}
}
if let name = latestItemData[indexPath.row].name {
cell.itemsName.text = name
}
else{
cell.itemsName.text = "-"
}
if let url = latestItemData[indexPath.row].url {
cell.itemsImageView.sd_setImage(with: URL(string: url), placeholderImage: UIImage(named: "placeholder"))
}
//----------------------------------
Thanks to #larme I recheck the code here is how I fixed it, I added those disappeared labels isHidden = false also as I did isHidden = true for them.
if let promotionPrice = latestItemData[indexPath.row].promotePrice {
if promotionPrice != 0 {
cell.originalItemsPrice.text = "\(setCommaForPrice(price: Int(promotionPrice))) Ks"
cell.promotionPercent.isHidden = false
cell.promotionLine.isHidden = false
}
else{
if let orPrice = latestItemData[indexPath.row].originalPrice {
cell.originalItemsPrice.text = "\(setCommaForPrice(price: Int(orPrice))) Ks"
}
cell.promotionPercent.isHidden = true
cell.promotionLine.isHidden = true
}
}

Replacing UISearchBar magnifying icon with UIActivityViewController

I've been searching all day but couldn't find out a fix for this code.
This extension replaces the UISearchField magnifying icon with UIActivityView (shows the loading icon when is true)
The extension was working fine on iOS 12, Xcode 10.3 but after I've changed into iOS 13, Xcode 11 Beta 4 it stopped working.
I've still made a workaround using this:
if let textFieldInsideSearchBar = searchBar.value(forKey: "searchField") as? UITextField {
let loadingIcon = UIActivityIndicatorView()
loadingIcon.style = .medium
loadingIcon.backgroundColor = UIColor.clear
loadingIcon.startAnimating()
textFieldInsideSearchBar.leftView = loadingIcon
}
But I can't understand the reason why the extension stopped working.
Also I've noticed that .flatMap was deprecated in iOS 13 and changed to .compactMap but as I understood there were no differences, and I've already tried to change the .flatMap to .compactMap but still didn't work.
Here is the extension:
extension UISearchBar {
private var textField: UITextField? {
let subViews = self.subviews.compactMap { $0.subviews }
return (subViews.filter { $0 is UITextField }).first as? UITextField
}
private var searchIcon: UIImage? {
let subViews = subviews.flatMap { $0.subviews }
return ((subViews.filter { $0 is UIImageView }).first as? UIImageView)?.image
}
private var activityIndicator: UIActivityIndicatorView? {
return textField?.leftView?.subviews.compactMap{ $0 as? UIActivityIndicatorView }.first
}
var isLoading: Bool {
get {
return activityIndicator != nil
} set {
let _searchIcon = searchIcon
if newValue {
if activityIndicator == nil {
let _activityIndicator = UIActivityIndicatorView()
_activityIndicator.style = .medium
_activityIndicator.startAnimating()
_activityIndicator.backgroundColor = UIColor.clear
self.setImage(UIImage(), for: .search, state: .normal)
textField?.leftView?.addSubview(_activityIndicator)
let leftViewSize = textField?.leftView?.frame.size ?? CGSize.zero
_activityIndicator.center = CGPoint(x: leftViewSize.width/2, y: leftViewSize.height/2)
}
} else {
self.setImage(_searchIcon, for: .search, state: .normal)
activityIndicator?.removeFromSuperview()
}
}
}
}
There have been some changes with iOS 13 in terms of UISearchBar, And you can use UISearchBar.searchTextField instead of searchBar.value(forKey: "searchField")
searchBar.searchTextField.backgroundColor = .red
Or if you want to keep it work with the extension, You can do this:
var searchTextField: UITextField? {
let subViews = self.subviews.first?.subviews.last?.subviews
return subViews?.first as? UITextField
}

Set Status bar in AppDelegate?

Previously, I had this code and it worked perfectly before Swift was updated. Now it says:
Setter for 'statusBarStyle' was deprecated in iOS 9.0: Use -[UIViewController preferredStatusBarStyle]
Now, I read that you have to override the statusBarStyle but I don't want to do this manually in each UIViewController but instead, control it via a struct and an extension of the UIApplication. Not sure how to do that though.
The code:
extension UIApplication {
var statusBarView: UIView? {
return value(forKey: "statusBar") as? UIView
}
}
struct StatusBar {
static func setStatusBar() {
let appDelegate = UIApplication.shared.delegate as? AppDelegate
if let currentVC = appDelegate?.window?.rootViewController?.getCurrentlyDisplayedVC() {
if currentVC is LoginVC || currentVC is SignUpVC || currentVC is SignUpVC {
UIApplication.shared.statusBarView?.backgroundColor = .clear
}
else {
UIApplication.shared.statusBarView?.backgroundColor = Colors.mainBlueColor
}
}
UIApplication.shared.statusBarStyle = .lightContent
}
}
extension UIViewController {
func getCurrentlyDisplayedVC() -> UIViewController {
if let presentedVC = presentedViewController {
return presentedVC.getCurrentlyDisplayedVC()
}
else if let split = self as? UISplitViewController, let last = split.viewControllers.last {
return last.getCurrentlyDisplayedVC()
}
else if let nav = self as? UINavigationController, let top = nav.topViewController {
return top.getCurrentlyDisplayedVC()
}
else if let tab = self as? UITabBarController {
if let selected = tab.selectedViewController {
return selected.getCurrentlyDisplayedVC()
}
}
return self
}
}

interactive viewController using pan Gesture

everyone i've been tearing out my hair trying to find a solution to an interactive view controller transition where you use the pan gesture in the downward direction to bring a full screen view controller from the top to the bottom. Has anyone run across or created any code like this. Below is my code. I already have the dismiss gesture down but cant figure out how to present the view controller by swiping down on the screen. PLEASE HELP!!!
import UIKit
class ViewController: UIViewController {
let interactor = Interactor()
var interactors:Interactor? = nil
let Mview = ModalViewController()
let mViewT: ModalViewController? = nil
var presentedViewControllers: UIViewController?
override func viewDidLoad() {
Mview.transitioningDelegate = self
Mview.modalPresentationStyle = .FullScreen
}
#IBAction func cameraSlide(sender: UIPanGestureRecognizer) {
let percentThreshold:CGFloat = 0.3
// convert y-position to downward pull progress (percentage)
let translation = sender.translationInView(Mview.view)
let verticalMovement = translation.y / UIScreen.mainScreen().bounds.height
let downwardMovement = fmaxf(Float(verticalMovement), 0.0)
let downwardMovementPercent = fminf(downwardMovement, 1.0)
let progress = CGFloat(downwardMovementPercent)
guard let interactor = interactors else { return }
switch sender.state {
case .Began:
interactor.hasStarted = true
self.presentViewController(Mview, animated: true, completion: nil)
case .Changed:
interactor.shouldFinish = progress > percentThreshold
interactor.updateInteractiveTransition(progress)
case .Cancelled:
interactor.hasStarted = false
interactor.cancelInteractiveTransition()
case .Ended:
interactor.hasStarted = false
if !interactor.shouldFinish {
interactor.cancelInteractiveTransition()
} else {
interactor.finishInteractiveTransition()
} default:
break
}
}
}
extension ViewController: UIViewControllerTransitioningDelegate {
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return DismissAnimator()
}
func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactor.hasStarted ? interactor : nil
}
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return PresentAnimator()
}
func interactionControllerForPresentation(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactor.hasStarted ? interactor : nil
}
}
class PresentAnimator: NSObject {
}
extension PresentAnimator: UIViewControllerAnimatedTransitioning
{
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 1.0
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
guard
let fromVC2 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey),
let toVC2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey),
let containerView2 = transitionContext.containerView() else {return}
let initialFrame = transitionContext.initialFrameForViewController(fromVC2)
toVC2.view.frame = initialFrame
toVC2.view.frame.origin.y = -initialFrame.height * 2
containerView2.addSubview(fromVC2.view)
containerView2.addSubview(toVC2.view)
let screenbounds = UIScreen.mainScreen().bounds
let Stage = CGPoint(x: 0, y: 0)
let finalFrame = CGRect(origin: Stage, size: screenbounds.size)
UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
toVC2.view.frame = finalFrame
}, completion: { _ in transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
}
)
}
}
class ModalViewController: UIViewController {
let interactors = Interactor()
var interactor:Interactor? = nil
#IBAction func close(sender: UIButton) {
dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func handleGesture(sender: UIPanGestureRecognizer) {
let percentThreshold:CGFloat = 0.3
// convert y-position to downward pull progress (percentage)
let translation = sender.translationInView(self.view)
let verticalMovement = translation.y / -view.bounds.height * 2
let downwardMovement = fmaxf(Float(verticalMovement), 0.0)
let downwardMovementPercent = fminf(downwardMovement, 1.0)
let progress = CGFloat(downwardMovementPercent)
guard let interactor = interactor else { return }
switch sender.state {
case .Began:
interactor.hasStarted = true
dismissViewControllerAnimated(true, completion: nil)
case .Changed:
interactor.shouldFinish = progress > percentThreshold
interactor.updateInteractiveTransition(progress)
case .Cancelled:
interactor.hasStarted = false
interactor.cancelInteractiveTransition()
case .Ended:
interactor.hasStarted = false
if !interactor.shouldFinish {
interactor.cancelInteractiveTransition()
} else {
interactor.finishInteractiveTransition()
} default:
break
}
}
}
import UIKit
class DismissAnimator: NSObject {
}
extension DismissAnimator : UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 1.0
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
guard
let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey),
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey),
let containerView = transitionContext.containerView()
else {
return
}
containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
let screenBounds = UIScreen.mainScreen().bounds
let topLeftCorner = CGPoint(x: 0, y: -screenBounds.height * 2)
let finalFrame = CGRect(origin: topLeftCorner, size: screenBounds.size)
UIView.animateWithDuration(
transitionDuration(transitionContext),animations: {fromVC.view.frame = finalFrame},
completion: { _ in transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
}
)
}
}
If you want a simple Pan Gesture to switch between UIViewControllers, you can check out this:
http://www.appcoda.com/custom-segue-animations/
If you want it to be interactive, as in you can go back and forth between VCs without having to complete the whole transition, I suggest you check out this:
https://www.youtube.com/watch?v=3jAlg5BnYUU
If you want to go even further and have a custom dismissing animation, then look no further than this:
https://www.raywenderlich.com/110536/custom-uiviewcontroller-transitions