Adding Google Maps to a View in Swift - swift

I have the following dilemma.
I created a 2 view in my project ( GMContainer and GMHolder). I want GMHolder inside all his view to display the Google Maps map.
Unfortunate I am just able to add a Google Maps view in the middle of my entire view and can't make it inside a view I created. Also I don't know what to add to the mapView as frame. I tried creating a new CGRect and adding the value of the view, adding my view, but id doesn't seem to do the trick...
Any tips on how to change the last rows to make Google Maps display on the entirety of GMHolder?
let GMContainer : UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.cornerRadius = 25
return view
}()
var GMHolder : UIView = {
let view = UIView()
view.layer.cornerRadius = 25
return view
}()
func setupUI() {
GMContainer.translatesAutoresizingMaskIntoConstraints = false
GMHolder.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(GMContainer)
view.addSubview(GMHolder)
GMContainer.topAnchor.constraint(equalTo: categoryBox.bottomAnchor , constant: 32).isActive = true
GMContainer.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 32).isActive = true
GMContainer.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -32).isActive = true
GMContainer.heightAnchor.constraint(equalToConstant: 450).isActive = true
GMHolder.topAnchor.constraint(equalTo: GMContainer.topAnchor , constant: 40).isActive = true
GMHolder.leadingAnchor.constraint(equalTo: GMContainer.leadingAnchor, constant: 24).isActive = true
GMHolder.trailingAnchor.constraint(equalTo: GMContainer.trailingAnchor, constant: -24).isActive = true
GMHolder.heightAnchor.constraint(equalToConstant: 300).isActive = true
//GM View Code
var mapView : GMSMapView
let camera = GMSCameraPosition.camera(withLatitude: 37.321262, longitude: -122.378945, zoom: 10)
mapView = GMSMapView.map(withFrame: CGRect(x: GMHolder.frame.origin.x, y: GMHolder.frame.origin.y , width: 300, height: 300), camera: camera)
// mapView.center = self.view.center
view.addSubview(mapView)
GMHolder = mapView
Photo of simulator as you can see, it isn't bounded to the view.

Init inside the GMHolder everything, it seems to do the trick.
var GMHolder : GMSMapView = {
GMSServices.provideAPIKey(API_KEY)
let camera = GMSCameraPosition.camera(withLatitude: 37.321262, longitude: -122.378945, zoom: 12)
let view = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
view.layer.cornerRadius = 25
view.backgroundColor = .black
return view
}()

Related

UIScrollView does not scroll + second scrollView shows nothing

it is unpleasant for me but I need some help with my UIScrollViews. They are both arranged subviews of a stackView on my MainVC.
The weird thing is that only one of them is showing content, although I used the same code for both scrollViews. The second problem is that they do not scroll, here is my code:
class HomeVC: UIViewController, UIScrollViewDelegate {
var views = [UIImageView]()
//StackView
let stackView = UIStackView()
let topView = UIScrollView()
let bottomView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = MyColors.soft_pink
prepare_data()
print(views.count)
}
//MARK: - GUI
func setUpStackView() {
view.addSubview(stackView)
stackView.alignment = .center
stackView.axis = .vertical
stackView.distribution = .equalCentering
stackView.spacing = 5
stackView.addArrangedSubview(topView)
stackView.addArrangedSubview(bottomView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -25).isActive = true
setUpTopView()
setUpBottomView()
}
func setUpTopView() {
topView.delegate = self
topView.layer.cornerRadius = 25
topView.layer.masksToBounds = true
topView.layer.borderWidth = 10
topView.layer.borderColor = UIColor.white.cgColor
topView.contentMode = .scaleAspectFit
topView.showsHorizontalScrollIndicator = false
topView.isPagingEnabled = true
topView.contentSize = CGSize(width: topView.frame.width * CGFloat(views.count),height: topView.frame.height)
for i in 0..<views.count {
topView.addSubview(views[i])
views[i].frame = CGRect(x: topView.frame.width * CGFloat(i), y: 0, width: 350, height: 250)
views[i].layer.cornerRadius = 25
}
topView.translatesAutoresizingMaskIntoConstraints = false
topView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 10).isActive = true
topView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -10).isActive = true
topView.heightAnchor.constraint(equalToConstant: 250).isActive = true
}
func setUpBottomView() {
bottomView.delegate = self
bottomView.layer.cornerRadius = 25
bottomView.layer.masksToBounds = true
bottomView.layer.borderWidth = 10
bottomView.layer.borderColor = UIColor.white.cgColor
bottomView.contentMode = .scaleAspectFit
bottomView.showsHorizontalScrollIndicator = false
bottomView.isPagingEnabled = true
bottomView.contentSize = CGSize(width: bottomView.frame.width * CGFloat(views.count),height: bottomView.frame.height)
for i in 0..<views.count {
bottomView.addSubview(views[i])
views[i].frame = CGRect(x: bottomView.frame.width * CGFloat(i), y: 0, width: 350, height: 250)
views[i].layer.cornerRadius = 25
}
bottomView.translatesAutoresizingMaskIntoConstraints = false
bottomView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 10).isActive = true
bottomView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -10).isActive = true
bottomView.heightAnchor.constraint(equalToConstant: 250).isActive = true
}
func prepare_data() {
for x in 1...6 {
let woman = UIImage(named: "woman\(x)")
let womanView = UIImageView(image: woman)
womanView.contentMode = .scaleAspectFill
views.append(womanView)
}
setUpStackView()
}
}
Could someone please be so kind and tell me what I have wrong? Thank you in advance!
Try to debug by printing values. bottomView.frame.width was zero at initialisation, so update subviews in viewDidLayoutSubviews. There are more ways you can look for frame update detection.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
DispatchQueue.main.async {
self.updateSubviewFrames()
}
}
func updateSubviewFrames() {
print(bottomView.frame)
bottomView.contentSize = CGSize(width: bottomView.frame.width * CGFloat(views.count),height: bottomView.frame.height)
for i in 0..<views.count {
views[i].frame = CGRect(x: bottomView.frame.width * CGFloat(i), y: 0, width: 350, height: 250)
views[i].layer.cornerRadius = 25
}
}
You've done a few things wrong...
First, because it's easy -- the reason you don't see anything in your Top scroll view is because you add your image views (from the views array) to topView, and then you add them to bottomView which removes them from topView!
So, you need one array of views for topView and an array of other views for bottomView.
Next, you are using auto-layout / constraints to size and position your stack view, then trying to use the frames of the stack view's arranged subviews -- for example:
bottomView.contentSize = CGSize(width: bottomView.frame.width * CGFloat(views.count),height: bottomView.frame.height)
but, that's all being done in functions called from viewDidLoad() when auto-layout has not yet configured the view frames.
You're also adding your scroll view's as arranged subviews of the stack view, but then constraining them to the stack view (which is not the way to do it):
topView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 10).isActive = true
As a side note: the easiest way to manage a paged scroll view is to embed the "pages" (your image views) in a horizontal stack view, setting the width of each view to the width of the scroll view's Frame Layout Guide (minus desired spacing).
Here's a modified version of your code to take a look at:
class HomeVC: UIViewController, UIScrollViewDelegate {
var topViews = [UIImageView]()
var botViews = [UIImageView]()
//StackView
let stackView = UIStackView()
let topView = UIScrollView()
let bottomView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemPink // MyColors.soft_pink
prepare_data()
setUpStackView()
setUpTopAndBottomViews()
}
func prepare_data() {
// create 6 image views
// for BOTH Top and Bottom scroll views
// I'll assume you have "woman" and "man" images
for x in 1...6 {
let woman = UIImage(named: "woman\(x)")
let man = UIImage(named: "man\(x)")
let womanView = UIImageView(image: woman)
womanView.contentMode = .scaleAspectFill
topViews.append(womanView)
let manView = UIImageView(image: man)
manView.contentMode = .scaleAspectFill
botViews.append(manView)
}
}
func setUpStackView() {
// setup stack view
view.addSubview(stackView)
// .alignment should be .fill, not .center
//stackView.alignment = .center
stackView.alignment = .fill
stackView.axis = .vertical
// let's use .fillEqually instead of .equalCentering
//stackView.distribution = .equalCentering
stackView.distribution = .fillEqually
stackView.spacing = 5
stackView.addArrangedSubview(topView)
stackView.addArrangedSubview(bottomView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -25).isActive = true
}
func setUpTopAndBottomViews() {
// setup both scroll views with the same properties
[topView, bottomView].forEach { v in
v.delegate = self
v.layer.cornerRadius = 25
v.layer.masksToBounds = true
v.layer.borderWidth = 10
v.layer.borderColor = UIColor.white.cgColor
v.showsHorizontalScrollIndicator = false
v.isPagingEnabled = true
}
// let's use auto-layout here
// if you want horizontal paged scrolling, easiest route is to
// use a horizontal stack view
// create a stack view
let topStack = UIStackView()
topStack.translatesAutoresizingMaskIntoConstraints = false
topStack.spacing = 10
// add stack view to topView
topView.addSubview(topStack)
for i in 0..<topViews.count {
topStack.addArrangedSubview(topViews[i])
topViews[i].layer.cornerRadius = 25
// set view width and height equal to
// topView's Frame Layout Guide
// allowing for 5-pts "padding" on the sides
topViews[i].widthAnchor.constraint(equalTo: topView.frameLayoutGuide.widthAnchor, constant: -10.0).isActive = true
topViews[i].heightAnchor.constraint(equalTo: topView.frameLayoutGuide.heightAnchor).isActive = true
}
// now we'll set constraints on the stack view to
// topView's Content Layout Guide
NSLayoutConstraint.activate([
topStack.topAnchor.constraint(equalTo: topView.contentLayoutGuide.topAnchor),
topStack.leadingAnchor.constraint(equalTo: topView.contentLayoutGuide.leadingAnchor, constant: 5.0),
topStack.trailingAnchor.constraint(equalTo: topView.contentLayoutGuide.trailingAnchor, constant: -5.0),
topStack.bottomAnchor.constraint(equalTo: topView.contentLayoutGuide.bottomAnchor),
])
// same thing with the bottom scroll view
// create a new stack view
let botStack = UIStackView()
botStack.translatesAutoresizingMaskIntoConstraints = false
botStack.spacing = 10
// add stack view to bottomView
bottomView.addSubview(botStack)
for i in 0..<botViews.count {
botStack.addArrangedSubview(botViews[i])
botViews[i].layer.cornerRadius = 25
// set view width and height equal to
// bottomView's Frame Layout Guide
// allowing for 5-pts "padding" on the sides
botViews[i].widthAnchor.constraint(equalTo: bottomView.frameLayoutGuide.widthAnchor, constant: -10.0).isActive = true
botViews[i].heightAnchor.constraint(equalTo: bottomView.frameLayoutGuide.heightAnchor).isActive = true
}
// now we'll set constraints on the stack view to
// bottomView's Content Layout Guide
NSLayoutConstraint.activate([
botStack.topAnchor.constraint(equalTo: bottomView.contentLayoutGuide.topAnchor),
botStack.leadingAnchor.constraint(equalTo: bottomView.contentLayoutGuide.leadingAnchor, constant: 5.0),
botStack.trailingAnchor.constraint(equalTo: bottomView.contentLayoutGuide.trailingAnchor, constant: -5.0),
botStack.bottomAnchor.constraint(equalTo: bottomView.contentLayoutGuide.bottomAnchor),
])
}
}

Why does getting my view's frame return the full screen, even tho the view has been run through the safeArea constraints?

Before presenting my GameScene I apply the safeAreaLayoutGuide to the initial view screen (code below is from GameViewController).
If at any point before I call view.presentScene(scene) I try to get the view's frame, I get back an empty set.
If however I get the frame, after presenting the scene, I get the rect for the full screen, even though the view does appear within the safeAreaLayoutGuide.
How can I get the view's safe adjusted screen coordinates? I should note I am doing this so I can apply the proper constraints to my GameScene, as I can only find how to do it for UIView.
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView?{
let screenWidthInPoints = UIScreen.main.bounds.width
let screenHeightInPoints = UIScreen.main.bounds.height
let imageView = UIView()
imageView.backgroundColor = .red
view.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
imageView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor , constant: -0).isActive = true
let scene = GameScene(size: CGSize(width: screenWidthInPoints,
height: screenHeightInPoints))
scene.backgroundColor = .black
scene.scaleMode = .aspectFit
view.presentScene(scene)
var myRect = view.frame
UIScreen.main.bounds represents the entire screen, not just the safe area. You will have to create a separate view constrained to safe area insets to get the proper safe area size. In your case, you already have an imageView placed within the safe area. Try printing out its size after adding the constraints and do it within a DispatchQueue.main.async to make sure that it gets called only after the current pass layout is complete.
let imageView = UIView()
imageView.backgroundColor = .red
view.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
imageView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor , constant: -0).isActive = true
DispatchQueue.main.async {
print("imageView.frame: ", imageView.frame)
let scene = GameScene(size: CGSize(width: imageView.frame.width,
height: imageView.frame.height))
scene.backgroundColor = .black
scene.scaleMode = .aspectFit
view.presentScene(scene)
}

Why UITextView scroll not working on tvOS?

I have UITextView:
private let descriptionView: UITextView = {
let view = UITextView()
view.translatesAutoresizingMaskIntoConstraints = false
view.bounces = true
view.textColor = UIColor.white
view.font = UIFont(name: Fonts.regular, size: 28)
view.isSelectable = true
view.isUserInteractionEnabled = true
view.panGestureRecognizer.allowedPressTypes = [NSNumber(value: UITouch.TouchType.indirect.rawValue)]
view.showsVerticalScrollIndicator = true
return view
}()
Added in another view
addSubview(descriptionView)
addConstraints([
descriptionView.leadingAnchor.constraint(equalTo: title.leadingAnchor),
descriptionView.topAnchor.constraint(equalTo: ratings.bottomAnchor, constant: 60),
descriptionView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -80),
descriptionView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
isSelectable = true,
view.isUserInteractionEnabled = true.
View focuses great, but not scrolling.
Content height > textview height
Similar questions did't help

Scroll view not scrolling horizontal

I have a custom view, i set it in parent like:
func setup(){
view.backgroundColor = .gray
view.addSubview(chartView)
chartView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
chartView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
chartView.topAnchor.constraint(equalTo: view.topAnchor, constant: statusAndNavigationBarHeight).isActive = true
chartView.heightAnchor.constraint(equalToConstant: Dimensions.chartHeight.value).isActive = true
}
Then in that view i tried to set up a scroll:
scroll = UIScrollView.init(frame: CGRect(x: 0.0, y: 0.0, width: scrollWidth(), // print 728.0
height: Double(Dimensions.chartHeight.value))) // print 400.0
scroll.isScrollEnabled = true
scroll.showsHorizontalScrollIndicator = true
addSubview(scroll)
}
And thats all, when i launch app i can't drag and scroll horizontally, in debug editor i can't see that it is scroll view here lying with large width.
The scrollView doesn't scroll with it's size , it needs a content that define it's content size for example
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let chartView = UIView()
chartView.translatesAutoresizingMaskIntoConstraints = false
chartView.backgroundColor = .red
view.backgroundColor = .gray
view.addSubview(chartView)
chartView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
chartView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
chartView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
chartView.heightAnchor.constraint(equalToConstant:200).isActive = true
let scroll = UIScrollView(frame: CGRect(x: 0.0,
y: 0.0,
width: UIScreen.main.bounds.size.width, // print 728.0
height: 200.0))
scroll.isScrollEnabled = true
scroll.showsHorizontalScrollIndicator = true
chartView.addSubview(scroll)
let www = UIView()
www.backgroundColor = .green
www.translatesAutoresizingMaskIntoConstraints = false
scroll.addSubview(www)
www.leftAnchor.constraint(equalTo: scroll.leftAnchor).isActive = true
www.rightAnchor.constraint(equalTo: scroll.rightAnchor).isActive = true
www.topAnchor.constraint(equalTo: scroll.topAnchor).isActive = true
www.bottomAnchor.constraint(equalTo: scroll.bottomAnchor).isActive = true
www.heightAnchor.constraint(equalToConstant:200).isActive = true
www.widthAnchor.constraint(equalTo: view.widthAnchor,multiplier:2.0).isActive = true
scroll.addSubview(www)
}
}

Adding constraints programmatically leads to unexpected behavior

I'm playing around with constraints, trying to learn how they work and to try and learn how to build a UI without IB, and I'm not getting the results I anticipated. In the code below, if I comment out the constraints at the end, I can see the purple view. If I uncomment them, all I get is an empty window where I would expect the view to be pinned to the left, topped right edges of the main view.
I've also tried doing a similar thing with the centerX and centerY properties to try and center the view in the middle of the window, again I get an empty window when those are activated.
Any help appreciated!
import Cocoa
class ViewController : NSViewController {
override func loadView() {
// NSMakeRect parameters do nothing?
let view = NSView(frame: NSMakeRect(0,0,400,2000))
view.wantsLayer = true
view.layer?.borderWidth = 5
view.layer?.borderColor = NSColor.gray.cgColor
self.view = view
}
override func viewWillAppear() {
super.viewWillAppear()
// Do any additional setup after loading the view.
createMasterView()
}
func makeView() -> NSView {
let view = NSView()
view.translatesAutoresizingMaskIntoConstraints = false
view.setFrameSize(NSSize(width: 600, height: 100))
view.wantsLayer = true
view.heightAnchor.constraint(equalToConstant: 1000)
return view
}
func createMasterView() {
let mainView = self.view
let headerView = makeView()
headerView.layer?.backgroundColor = NSColor.purple.cgColor
headerView.layer?.borderWidth = 5
headerView.layer?.borderColor = CGColor.black
mainView.translatesAutoresizingMaskIntoConstraints = false
mainView.addSubview(headerView)
headerView.topAnchor.constraint(equalTo: mainView.topAnchor).isActive = true
headerView.leadingAnchor.constraint(equalTo: mainView.leadingAnchor).isActive = true
headerView.trailingAnchor.constraint(equalTo: mainView.trailingAnchor).isActive = true
}
}
Edit: I'm also including my AppDelegate code below. I'm still very new to all this so the code is stuff I've cobbled together from various tutorials.
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
var windowController: NSWindowController!
var window: NSWindow!
var windowTitle = "Test App"
var customBGColor = NSColor(red: 1, green: 1, blue: 1, alpha: 1)
func applicationDidFinishLaunching(_ aNotification: Notification) {
createMainWindow()
}
func createMainWindow() {
window = NSWindow()
// window.alphaValue = 0.5
window.backgroundColor = customBGColor
window.title = windowTitle
window.styleMask = NSWindow.StyleMask(rawValue: 0xf)
window.backingType = .buffered
window.contentViewController = ViewController()
window.setFrame(NSRect(x: 700, y: 200, width: 1920, height: 1080), display: false)
windowController = NSWindowController()
windowController.contentViewController = window.contentViewController
windowController.window = window
windowController.showWindow(self)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
view.setFrameSize(NSSize(width: 600, height: 100))
You are overriding the height shortly afterwards with the heightAnchor.
Try setting width as well with an anchor
With auto layout, you don't touch the frame property of the view. When working programmatically, however, you have to with the view itself, but after that, all subviews can be sized and positioned using constraints. For clarity, I got rid of makeView():
func createMasterView() {
let headerView = NSView() // instantiate
headerView.layer?.backgroundColor = NSColor.purple.cgColor // style
headerView.layer?.borderWidth = 5
headerView.layer?.borderColor = CGColor.black
headerView.translatesAutoresizingMaskIntoConstraints = false // disable mask translating
view.addSubview(headerView) // add as a subview
// then configure constraints
// one possible setup
headerView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
headerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
headerView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
headerView.heightAnchor.constraint(equalToConstant: 100).isActive = true
// another possible setup
headerView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
headerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
headerView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
headerView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5).isActive = true
// another possible setup
headerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
headerView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
headerView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -50).isActive = true
headerView.heightAnchor.constraint(equalTo: view.heightAnchor, constant: -50).isActive = true
}