MacOS - How do I programatically position image views next to each other? - swift

Inside the main view of my MacOS app, I am trying to layout a bunch of image views next to each other.
The logic I am using is that the first sub view has its leadingAnchor set as the leadingAnchor of the main view, and every subsequent sub view has its leadingAnchor set as the trailingAnchor of the previous sub view.
let imgWidth :CGFloat = 100, imgHeight :CGFloat = 100
var leadingAnchor = self.view.leadingAnchor
for /*loop logic*/ {
let img: NSImage = funcToGetImage()
var imgView = NSImageView(frame: NSRect(origin: .zero, size: img.size))
imgView.image = img
self.view.addSubview(imgView)
imgView.widthAnchor.constraint(equalToConstant: imgWidth).isActive = true
imgView.heightAnchor.constraint(equalToConstant: imgHeight).isActive = true
imgView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
leadingAnchor = imgView.trailingAnchor
}
However I see only one image, and not a bunch of them positioned next to each other as I expected.
Am new to Swift/Xcode/MacOs development. What am I missing?
Edit:
If I replace the lines
var imgView = NSImageView(frame: NSRect(origin: .zero, size: img.size))
imgView.image = img
with
imgView = NSImageView(image: img)
I don't see anything at all.
Edit2:
Replacing
var imgView = NSImageView(frame: NSRect(origin: .zero, size: img.size))
with
var imgView = NSImageView(frame: NSRect(origin: CGPoint(x: x, y: 0), size: img.size))
x += 100
did the trick.

Based on El Tomato's comment, I replaced
var imgView = NSImageView(frame: NSRect(origin: .zero, size: img.size))
with
var imgView = NSImageView(frame: NSRect(origin: CGPoint(x: x, y: 0), size: img.size))
x += 100
That did the trick.

Related

How to dispose from UIView subviews on one ViewController when navigating away from it

I'm starting to learn Swift and decided to build an app but without storyboard.
My SceneDelegate scene function instantiates a TabBarController
window = UIWindow(frame: UIScreen.main.bounds)
let tb = TabBarController()
window?.rootViewController = tb
window?.makeKeyAndVisible()
window?.windowScene = windowSceme
I have a TabBarController that extends from UITabBarController which pretty much styles the tab bar and sets it all up
For each item of the Tabbar I have a ViewController. For the purpose of this question I'm going to specify the first "Home".
In Home, which extends ViewController, I have the following
let homePageView = HomePageView()
override func viewWillLayoutSubviews() {
navigationController?.isNavigationBarHidden = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
homePageView.setupHomePage()
view.addSubview(homePageView)
}
override func viewWillDisappear(_ animated: Bool) {
homePageView.dispose()
}
The Controller is pretty much only in charge of calling whatever is going to be displayed on the screen within HomePageView(), the main component. This last, holds two more views. One carrousel HomePageCarrousel() and one header HomePageSocialUp(). This view instantiates the two latter referred to and sets the layout programmatically and adds them as subviews. It also includes a dispose function that sets the instantiated classes to nil such as --> Instantiate - homePageCarrousel = HomePageCarrousel() and the dispose function has homePageCarrousel = nil
That works perfectly fine but when I navigate away from the current view controller via the tab bar and navigate back to it, now I have two instances of HomePageCarrousel() and HomePageSocialUp() within HomeView
I'm probably holding a strong reference somewhere but I can't figure out when. Could someone point me to where should I look to debug it or what might be that is creating the issue.
I'm also providing the code for the two views duplicated in case the issue is there
HomePageSocialUp
class HomePageSocialUp: UIView {
let logo = UIImage(named: "LogoSmiles")
let socialImages: [UIImage] = [UIImage(named: "tripadvisor")!, UIImage(named: "instagram")!, UIImage(named: "facebook")!, UIImage(named: "linkedin")!]
func setupHeaderCircle() {
guard let insetTop = UIApplication.shared.keyWindow?.safeAreaInsets.top else {return}
let circlePath = UIBezierPath(arcCenter: CGPoint(x: UIScreen.main.bounds.width / 2, y: insetTop + 60), radius: CGFloat(90), startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
shapeLayer.fillColor = UIColor.white.cgColor
layer.addSublayer(shapeLayer)
}
func setupSocialHeader() {
setupHeaderCircle()
layer.masksToBounds = true;
backgroundColor = UIColor.TintColor
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOpacity = 0.8
layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
layer.shadowRadius = 3.0
layer.masksToBounds = false
frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 280)
let imageView = UIImageView()
guard let logo = logo else {return}
guard let insetTop = UIApplication.shared.keyWindow?.safeAreaInsets.top else {return}
imageView.frame = CGRect(x: CGFloat(Int((UIScreen.main.bounds.width - (logo.size.width)))) / 2, y: 120 - (logo.size.width / 2), width: logo.size.width, height: logo.size.height)
imageView.image = logo
addSubview(imageView)
}
}
HomePageCarrousel
class HomePageCarrousel: UIScrollView {
var images: [UIImage]?
var originX = 0
var numberOfIterations = 0
var timer: Timer?
func setupCarrousel() {
let x = (Int(UIScreen.main.bounds.width) - (Int(UIScreen.main.bounds.width) - 60)) / 2
frame = CGRect(x: x, y: (Int(frame.origin.y) - 350) / 2, width: Int(UIScreen.main.bounds.width) - 60, height: 350)
let newImage = textToImage(drawText: "Creating Smiles in unique places.", frame: frame, inImage: UIImage(named: "smiles1")!, atPoint: CGPoint(x: frame.origin.x + 20, y: frame.height - 20))
images = [newImage, UIImage(named: "smiles2")!]
guard timer == nil else { return }
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(startScrolling), userInfo: nil, repeats: true)
guard let imageCount = images?.count, let images = images else { return }
contentSize = CGSize(width: frame.width * CGFloat(images.count), height: frame.height)
for image in 0..<imageCount {
let imageView = UIImageView()
imageView.image = images[image]
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
let xPosition = frame.width * CGFloat(image)
imageView.frame = CGRect(x: xPosition, y: 0, width: frame.width, height: frame.height)
addSubview(imageView)
}
timer?.fire()
}
func textToImage(drawText text: String, frame: CGRect, inImage image: UIImage, atPoint point: CGPoint) -> UIImage {
let textColor = UIColor.white
let textFont = UIFont(name: "Helvetica Bold" , size: 12)!
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(image.size, false, scale)
let textFontAttributes = [
NSAttributedString.Key.font: textFont,
NSAttributedString.Key.foregroundColor: textColor,
] as [NSAttributedString.Key : Any]
image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))
text.draw(in: frame, withAttributes: textFontAttributes)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
#objc func startScrolling() {
print(originX)
guard let images = images else { return }
if originX == images.count {
originX = 0
numberOfIterations += 1
}
if numberOfIterations > 2 {
timer?.invalidate()
timer = nil
numberOfIterations = 0
}
let x = CGFloat(originX) * frame.size.width
setContentOffset(CGPoint(x: x, y: 0), animated: true)
originX += 1
}
}
Thanks upfront
Did you tried to remove the subviews, something like
homePageView.subviews.forEach { (view) in
//taking appropriate action to whichever view you want
view.removeFromSuperview()//for removing views
}

Custom UIImageView Border

I'm trying to make a border like this:
With this code:
self.slot1.layer.cornerRadius = self.slot1.bounds.height / 2
self.slot1.layer.borderWidth = 1.5
self.slot1.layer.borderColor = UIColor.orange.cgColor
It produces:
How can i add a "spacing" between the border and the actual image?
You can achieve this using below extension
extension UIImage {
func imageWithInsets(insets: UIEdgeInsets) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(
CGSize(width: self.size.width + insets.left + insets.right,
height: self.size.height + insets.top + insets.bottom), false, self.scale)
let _ = UIGraphicsGetCurrentContext()
let origin = CGPoint(x: insets.left, y: insets.top)
self.draw(at: origin)
let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithInsets
}
}
Use below code to add space between image and border, i add here 20 you can give your own space
self.slot1.layer.cornerRadius = self.slot1.bounds.height / 2
self.slot1.layer.borderWidth = 1.5
self.slot1.layer.borderColor = UIColor.orange.cgColor
let image = self.slot1.image?.imageWithInsets(insets: UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20))
self.slot1.image = image
Hope this help!
Instead of trying to make space in between, take one UIView inside that put your UIImageView. UIImageView should have less width and height, then add corner radius to both but add border color to UIVIew only.
Your UI should be like this :
Run in playground
import PlaygroundSupport
import UIKit
let iv = UIImageView(frame: CGRect(x: 0.0, y: 0.0, width: 200, height: 200))
PlaygroundPage.current.liveView = iv
iv.backgroundColor = .blue
iv.layer.cornerRadius = 100
iv.layer.borderColor = UIColor.red.cgColor
iv.layer.borderWidth = 5
iv.layer.sublayers?.count
let layer2 = CAShapeLayer()
let newFrame = iv.bounds.insetBy(dx: 7, dy: 7)
layer2.path = UIBezierPath(roundedRect: newFrame, cornerRadius: newFrame.height / 2).cgPath
layer2.frame = iv.bounds
layer2.lineWidth = 5
layer2.strokeColor = UIColor.yellow.cgColor
layer2.fillColor = UIColor.clear.cgColor
iv.layer.addSublayer(layer2)

Cant set right frame for ImageView

I cant set my UIImageView as half size of my tableview.backgroundview in upper part of my controller.
i tried to set origin of avatar as origin view, but it doesnt work.
let avatar = UIImageView(frame: self.view.frame)
let imgageOfAvatar = UIImage(named: "avatar.jpg")
avatar.image = imgageOfAvatar
avatar.frame = CGRect(x: self.view.frame.origin, y: self.view.frame.origin , width: self.view.frame.width, height: self.view.frame.height / 2)
avatar.frame.origin = self.view.frame.origin
//avatar.contentMode = UIViewContentMode.scaleAspectFill
self.tableView.addSubview(avatar)
self.tableView.sendSubview(toBack: avatar)
https://pp.userapi.com/c847120/v847120175/19f9cf/128kU2yWq-E.jpg
Try this
avatar.frame.origin.x = self.view.frame.origin.x
avatar.frame.origin.y = self.view.frame.origin.y
avatar.frame = CGRect(origin:self.view.frame.origin, size: width: self.view.frame.width, height: self.view.frame.height / 2)
you should add it to view like this:
let avatar = UIImageView(frame: self.view.frame)
let imgageOfAvatar = UIImage(named: "avatar.jpg")
avatar.image = imgageOfAvatar
avatar.frame = CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y , width: self.view.frame.width, height: self.view.frame.height / 2)
avatar.frame.origin = self.view.frame.origin
self.view.insertSubview(avatar, at: 0)
I got the intended result
output
let avatar = UIImageView(frame: self.view.frame)
let imgageOfAvatar = UIImage(named: "dog.jpeg")
avatar.image = imgageOfAvatar
avatar.frame = CGRect(origin: self.view.frame.origin,
size: CGSize(width: self.view.frame.width,
height: self.view.frame.height / 2))
self.tableview.addSubview(avatar)
self.tableview.sendSubviewToBack(avatar)

Circular crop for photos from photo library when using UIImagePickerController

I am trying to change the square crop tool to circular one for the images selected from Photo Library in Swift 4. I tried many old codes available in here with no luck. Can somebody please help me.
There are mainly 2 issues in this code.
It doesn't hide the already existing square cropping area.
It shows the choose and cancel buttons under the old overlay, so cant tap on it.
Any help would be appreciated.
My code:
private func hideDefaultEditOverlay(view: UIView)
{
for subview in view.subviews
{
if let cropOverlay = NSClassFromString("PLCropOverlayCropView")
{
if subview.isKind(of: cropOverlay) {
subview.isHidden = true
break
}
else {
hideDefaultEditOverlay(view: subview)
}
}
}
}
private func addCircleOverlayToImageViewer(viewController: UIViewController) {
let circleColor = UIColor.clear
let maskColor = UIColor.black.withAlphaComponent(0.8)
let screenHeight = UIScreen.main.bounds.size.height
let screenWidth = UIScreen.main.bounds.size.width
hideDefaultEditOverlay(view: viewController.view)
let circleLayer = CAShapeLayer()
let circlePath = UIBezierPath(ovalIn: CGRect(x: 0, y: screenHeight - screenWidth, width: screenWidth, height: screenWidth))
circlePath.usesEvenOddFillRule = true
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = circleColor.cgColor
let maskPath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight), cornerRadius: 0)
maskPath.append(circlePath)
maskPath.usesEvenOddFillRule = true
let maskLayer = CAShapeLayer()
maskLayer.path = maskPath.cgPath
maskLayer.fillRule = kCAFillRuleEvenOdd
maskLayer.fillColor = maskColor.cgColor
viewController.view.layer.addSublayer(maskLayer)
// Move and Scale label
let label = UILabel(frame: CGRect(x: 0, y: 20, width: view.frame.width, height: 50))
label.text = "Move and Scale"
label.textAlignment = .center
label.textColor = UIColor.white
viewController.view.addSubview(label)
}

How to change the size of the titleView of Navigation Item? (Swift)

In my app I want to change the titleView of my Navigation Controller to a custom ImageView but I have a very strange problem. I basically just can`t set the frame of my ImageView.
This is my code:
func NavBarEinrichten(){
let logo = UIImage(named: "Notennamen für Griffe 1")!
let imageView = UIImageView(image:logo)
imageView.contentMode = .scaleAspectFit
//To show the problem better
imageView.backgroundColor = UIColor.red
let navController = navigationController!
let bannerWidth:CGFloat = 20
let bannerHeight:CGFloat = navController.navigationBar.frame.size.height
let bannerX = bannerWidth / 2 - logo.size.width / 2
let bannerY = bannerHeight / 2 - logo.size.height / 2
imageView.frame = CGRect(x: bannerX, y: bannerY, width: bannerWidth, height: bannerHeight)
self.navigationItem.titleView?.frame = CGRect(x: bannerX, y: bannerY, width: bannerWidth, height: bannerHeight)
navigationItem.titleView = imageView
print(imageView.frame, "imageView frame")
}
The print statement prints:
(0.0, 0.0, 357.0, 142.0) imageView frame
Which is right, because it looks like this when i run it, but not like it should be. Because I want the width to be 20.
In the simulator it looks like this:
I tried to call this method in viewDidLoad(), viewWillAppear()and viewDidLayoutSubviews()
Every time with the exact same outcome...
Can you help me?