I have an array with separate UIImages and I am displaying them using an UIImageView, how do I add an identifier to them so that I can switch to the view controller with the specific data related to that image.
Below is the code:
class Orders2ViewController: UIViewController, OrdersBaseCoordinated {
var orderList: [OrderInfo] = [OrderInfo( itemImage: UIImage(named: "printer.jpeg")!, itemSKU: 12567), OrderInfo(itemImage: UIImage(named: "ipad.jpeg")!, itemSKU: 34521), OrderInfo( itemImage: UIImage(named: "hoodie.jpeg")!, itemSKU: 93620)]
lazy var someImageView: UIImageView = {
let theImageView = UIImageView()
let tap = UITapGestureRecognizer(target: self, action: #selector(Orders2ViewController.tappedMe))
theImageView.addGestureRecognizer(tap)
theImageView.isUserInteractionEnabled = true
theImageView.image = orderList[selectedIndex].itemImage
theImageView.translatesAutoresizingMaskIntoConstraints = false
return theImageView
}()
#objc func tappedMe(sender: UITapGestureRecognizer)
{
coordinator?.passImageData(j: )
}
I want to pass an identifier in my tappedMe function to recognize which image was tapped on, I did find other answers on SO that mentioned gesture.view.tag but I don't want to create another view, rather navigate to the next controller with an identifier. Is there a way to do this?
Since UIImageView inherits from UIView, you can use it’s parameter called tag. In your example you can set theImageView.tag = orderedList[selectedIndex].itemSKU. Then each time tap was recognized, you can just use this itemSKU to do move to the next screen.
However it seems that you have only one UIImageView on the screen and you use selectedIndex to determine which image you need. So you can just do like that:
#objc func tappedMe(sender: UITapGestureRecognizer) {
coordinator?.passImageData(j: orderList[selectedIndex].itemImage)
}
I passed the itemImage just to show you how it can be done. You can use whatever you need.
Related
I'm using BulletinBoard (BLTNBoard) to create dialogs in my iOS app. There's an option to embed image inside it. I would like to extend it's functionality and allow user to manipulate this image using tap gesture. But eventually when I assign a gesture to it's imageView using addGestureRecognizer nothing happens.
Here's how I initiliaze bulletin and add gesture to the image:
class ViewController: UIViewController {
lazy var bulletinManager: BLTNItemManager = {
let rootItem: BLTNPageItem = BLTNPageItem(title: "")
return BLTNItemManager(rootItem: rootItem)
}()
override func viewDidLoad() {
//etc code
let bulletinManager: BLTNItemManager = {
let item = BLTNPageItem(title: "Welcome")
item.descriptionText = "Pleas welcome to my app"
item.actionButtonTitle = "Go"
item.alternativeButtonTitle = "Try to tap here"
item.requiresCloseButton = false
item.isDismissable = false
item.actionHandler = { item in
self.bulletinManager.dismissBulletin()
}
item.alternativeHandler = { item in
//do nothing by now
}
//
item.image = UIImage(named: "welcome")
//adding gesture to its imageView
item.imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: Selector("tapTap:"))
item.imageView?.addGestureRecognizer(tap)
return BLTNItemManager(rootItem: item)
}()
}
#objc func tapTap(gestureRecognizer: UITapGestureRecognizer) {
print("TAPTAP!!!!!!")
}
}
and nothing happens at all (no message printed in console).
However if I assign action inside alternative button it works as expected:
item.alternativeHandler = { item in
item.imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: Selector("tapTap:"))
item.imageView?.addGestureRecognizer(tap)
}
I guess the only thing which can prevent me to assign the tap event to it properly is that imageView becomes available much later than the bulletin is created (for example only when it is shown on the screen).
Could you please help and correct my code. Thanks
upd.
Ok, based on Philipp's answer I have the following solution:
class myPageItem: BLTNPageItem {
override func makeContentViews(with interfaceBuilder: BLTNInterfaceBuilder) -> [UIView] {
let contentViews = super.makeContentViews(with: interfaceBuilder)
let imageView=super.imageView
imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: #selector(tapTap))
imageView?.addGestureRecognizer(tap)
return contentViews
}
#objc func tapTap(gestureRecognizer: UITapGestureRecognizer) {
print("TAPTAP!!!!!!")
}
}
When you're working with an open source library, it's easy to check out the source code to find the answer.
As you can see here, image setter doesn't initiate the image view.
Both makeContentViews makeArrangedSubviews (which are responsible for views initializing) doesn't have any finish notification callbacks.
Usually in such cases I had to fork the repo and add functionality by myself - then I'll make a pull request if I think this functionality may be needed by someone else.
But luckily for you the BLTNPageItem is marked open, so you can just subclass it. Override makeContentViews and add your logic there, something like this:
class YourOwnPageItem: BLTNPageItem {
override func makeContentViews(with interfaceBuilder: BLTNInterfaceBuilder) -> [UIView] {
let contentViews = super.makeContentViews(with: interfaceBuilder)
// configure the imageView here
return contentViews
}
}
I am working on my project app where I want to allow my user to change the background wallpaper to be same on all screens. I have a settings screen where I am adding that. I have added ImageViews to all view controllers and I have some view controllers that have UIscrollview so I added Imageview to slides template. Now my dilemma is how can I allow the user to pick the preview wallpaper so it changes the Imageview image on every view controller. I already created such #IBOutlets as shown below.
#IBOutlet weak var slideBackground: UIImageView!
#IBOutlet weak var homeScreenBackground: UIImageView!
#IBOutlet weak var settingsBackground: UIImageView!
You should use the system wide NotificationCenter.
Simply put, you can have objects subscribe to the default NotificationCenter and specify a selector (method) to execute when a notification is posted.
You can also post custom notifications that represent the wallpaper change event.
I have used this in an app I built to achieve a system wide 'dark mode' transition.
To post:
#objc func postWallpaperChangeNotification() {
NotificationCenter.default.post(name: .wallpaperChanged, object: nil)
}
To subscribe:
NotificationCenter.default.addObserver(self, selector: #selector(someMethodToRunWhenWallpaperChanges(_:)), name: . wallpaperChanged, object: nil)
You also need to remove the observer in deinit().
This is approximate code to give you a flavour, any questions hmu.
You have a few options.. what option is best depends on your needs really.
As Woodstock mentioned in his answer, you could use NotificationCentre, I wont re-explain that.
Another option would be to store the image name in UserDefaults that you are going to use for the background image.
any time you change the background... set the value
UserDefaults.standard.set(imageName, forKey "backgroundImageName")
Then in each view controller, just load the value on viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated: animated)
if let backgroundImageName = UserDefaults.standard.string(forKey: "backgroundImageName") {
self.backgroundImageView.image = UIImage(named: backgroundImageName)
}
}
Both of these options are not very easy to unit test though. You could use Mocks to test it.
A bonus of this method is that it will 'remember' the setting inbetween the user restarting the app.
An example of how you might reduce code repetition using subclassing:
class WallpaperedViewController: UIViewController {
lazy private var imageView: UIImageView = {
let imageView = UIImageView()
view.addSubview(imageView)
view.sendSubview(toBack: imageView)
return imageView
}()
private func setWallPaperImage() {
let imageName = UserDefaults.standard.string(forKey: "backgroundImageName")
let image = UIImage(named: imageName!)
imageView.image = image
//imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
imageView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
imageView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.setWallPaperImage()
}
}
Then for each ViewController that has this background image wallpaper just extend this class:
class SomeViewController: WallpaperedViewController {}
I'm making an extremely simple iOS app. I press a button and the image changes. The problem I'm running into is every time I press the button, it's like the button goes to the bottom of the phone simulator and lays on top of the button I originally pressed.
Also the image only stays for a split second before disappearing. (I'm assuming these actions go hand in hand).
Here is the code if anyone is able to help.
import UIKit
class ViewController: UIViewController {
#IBAction func generateHero(_ sender: UIButton) {
//list of Images in array
let image : NSArray = [ UIImage(named: "batman.jpg")!,
UIImage(named: "the-flash.jpg")!,
UIImage(named: "Deadpool.jpg")!,
UIImage(named: "green-arrow.jpg")!,
UIImage(named: "iron-man.jpg")!]
//random image generating method
let imagerange: UInt32 = UInt32(image.count)
let randomimage = Int(arc4random_uniform(imagerange))
let generatedimage: AnyObject = image.object(at: randomimage) as AnyObject
self.heroImage.image = generatedimage as? UIImage
}
#IBOutlet weak var heroImage: UIImageView!
}
You are doing a lot of unnecessary conversions in random image generation, if you have used Array instead of NSArray you could use .randomElement() function that returns random element of array. So you would just do
self.heroImage.image = image.randomElement()
How Do I add multipleImages for the top part and have it scrollable without moving the rest of the content? Also, how do I have the little dots at the bottom to indicate which image I'm on? My code for the image is down below.
import SDWebImage
#IBOutlet weak var headerImage: UIImageView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let imageUrl:NSURL? = NSURL(string: headerPhoto!)
if let url = imageUrl {
headerImage.sd_setImage(with: url as URL!)
}
}
You are looking for something called image slide show or image slider.
Instead of creating them by yourself which requires lots of effort, here is a GitHub library that is easy to use.
Basicly the way slider works is that it is a horizontal scroll view and each cell in the scroll view is an image so that you can scroll. Then, put a fixed page index element on the bottom of the scroll view to tell you which image you are currently at.
To use it, create a UIView in your viewcontroller and set both class and module to ImageSlideshow. Then connect it to your ViewController.swift as IBOutLet.
Then create an array of image urls
let alamofireSource = [AlamofireSource(urlString: "imgurl1")!, AlamofireSource(urlString: "imgurl2")!, AlamofireSource(urlString: "imgurl3")!]
And finally in your viewDidLoad() function:
override func viewDidLoad() {
super.viewDidLoad()
slideshow.backgroundColor = UIColor.white
slideshow.slideshowInterval = 5.0
slideshow.pageControlPosition = PageControlPosition.underScrollView
slideshow.pageControl.currentPageIndicatorTintColor = UIColor.lightGray
slideshow.pageControl.pageIndicatorTintColor = UIColor.black
slideshow.contentScaleMode = UIViewContentMode.scaleAspectFill
slideshow.setImageInputs(alamofireSource)
}
How can one still background image in multiple view controllers be implemented?
With self.view.backgroundColor = UIColor(patternImage: UIImage(named: "file.png")) background would move along with UIViewController while screen switching.
Is it possible to place image on separate layer under view controllers?
Solution 1
Create a class called MyBackground:
class MyBackground {
class func addBackgroundImage() {
UIGraphicsBeginImageContext(self.view.frame.size)
UIImage(named: "file")?.drawInRect(self.view.bounds)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
view.backgroundColor = UIColor(patternImage: image)
}
}
If you want to call it in your AViewController, simply call MyBackground.addBackgroundImage()
Solution 2
Create a ViewController called MyViewController:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
addBackgroundImage()
}
func addBackgroundImage() {
UIGraphicsBeginImageContext(self.view.frame.size)
UIImage(named: "file")?.drawInRect(self.view.bounds)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
view.backgroundColor = UIColor(patternImage: image)
}
}
If you AViewController want to set this image, simply do like this:
AViewController: MyViewController //AViewController is a subclass of MyViewController
For example: I have an image call background, and I will set it as a background image of my ViewController:
If I just add it in one line as #startupthekid said:
If I add it as I said:
i think you can set background in your container view controllers, such as navi controller, tab controller, then set all your view controller's background to clear color.
A better answer is to use UIAppearance. If you're not familiar with it, UIAppearance is a proxy protocol that you send messages to and it modifies later instances of that class.
For example if I have a custom button class RoundButton:
RoundButton.appearance().titleFont = Font.Heavy(20)
And in my RoundButton class:
dynamic var titleFont: UIFont? {
get { return self.titleLabel?.font }
set { self.titleLabel?.font = newValue }
}
The dynamic keywords is incredibly important. It's used for objective-c compatibility and setting properties via UIAppearance that don't use it will cause a crash.
You can do as many custom properties as you want and there's a bunch already default in the os.
In your case you can do something like:
UIImageView.appearanceWhenContainedInInstancesOfClasses([MyCustomViewController.self)].customImage = UIImage(...)
You can of course get away with just a custom view controller but in my opinion, UIApperance keeps your UI code separate from your controller logic.
One way you could implement the behavior you want is a UIViewController subclass:
class MyCustomViewController: UIViewController {
private let backgroundImageView = UIImageView()
dynamic var backgroundImage: UIImage? {
get { self.backgroundImageView.image }
set { self.backgroundImageView.image = newValue }
}
}
and then modify the backgroundImage property on the appearance instance of MyCustomViewController.
MyCustomViewController.appearance().backgroundImage = ...
EDIT: Given that your question was misleading and that you instead want a shared image that never moves throughout all your view controllers, you can simply insert an image view into the application window:
if let window = UIApplication.sharedApplication().windows.first {
let imageView = UIImageView(image: UIImage(named: "face-mask"))
window.addSubview(imageView)
imageView.frame = window.bounds
}
If you like Autolayout, like I do, then I'd set constraints to pin the image view to the window edges.
you should set the image in viewDidload of every viewController
Update :
either you should set in every view controller or you can set to windows . Second thing if your background changes dynamically with different images then you can use prepareforsegue method to pass images to nextviewcontroller if images are different for each viewcontroller. if same image for every viewcontroller then you can set it to window.
:)