<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Garima Bothra on Medium]]></title>
        <description><![CDATA[Stories by Garima Bothra on Medium]]></description>
        <link>https://medium.com/@garimabothra?source=rss-8954ff43cd93------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*reh2L9_6Rtu6OenF4Wh5-A@2x.jpeg</url>
            <title>Stories by Garima Bothra on Medium</title>
            <link>https://medium.com/@garimabothra?source=rss-8954ff43cd93------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 15:13:14 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@garimabothra/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Crafting Custom Shapes with SwiftUI]]></title>
            <link>https://medium.com/@garimabothra/crafting-custom-shapes-with-swiftui-2755139d880f?source=rss-8954ff43cd93------2</link>
            <guid isPermaLink="false">https://medium.com/p/2755139d880f</guid>
            <category><![CDATA[animation]]></category>
            <category><![CDATA[swiftui-animation]]></category>
            <category><![CDATA[swiftui]]></category>
            <dc:creator><![CDATA[Garima Bothra]]></dc:creator>
            <pubDate>Mon, 15 Jul 2024 06:03:51 GMT</pubDate>
            <atom:updated>2024-07-15T06:03:51.409Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*aLDUwnD22DRQHHd1" /><figcaption>Photo by <a href="https://unsplash.com/@koushikc?utm_source=medium&amp;utm_medium=referral">Koushik Chowdavarapu</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Introduction</h3><p>Recently, I came across Medidate – Mindfulness app. The intricate mandalas on the app looked like a work of art. This motivated me to dive into understanding how such complex structures can be created.</p><p>In this article, we’ll explore how to draw, customize, and animate shapes using SwiftUI and CoreAnimation. We’ll start by exploring the coordinate system, then create a flower petal, adjust its shape, assemble it into a flower, and finally animate it.</p><h3>Understanding the Coordinate System for Paths</h3><p>When drawing paths, understanding the coordinate system is critical. The origin (0,0) is at the <strong>top-left corner</strong> of the view, with the x-axis increasing to the right and the y-axis increasing downward. This system allows precise control over the placement of points and the creation of paths.</p><h3>Creating a Custom Shape</h3><p>To create custom shapes in SwiftUI, you can conform to the Shape protocol. This involves defining the path that outlines your shape. Here, we’ll draw a flower petal using the Shape protocol.</p><pre>struct FlowerPetalView: Shape {<br>    func path(in rect: CGRect) -&gt; Path {<br>        var path = Path()<br>        <br>        let width = rect.width<br>        let height = rect.height<br>        let controlHeight = height<br>        // 1. Start at the center of the shape<br>        path.move(to: CGPoint(x: width / 2, y: height / 2))<br>        <br>        // 2. Draw the top half of the petal using a quadratic curve<br>        path.addQuadCurve(<br>            to: CGPoint(x: 0, y: height / 2),<br>            control: CGPoint(x: width / 4, y: height / 2 - controlHeight)<br>        )<br>        <br>        // 3. Draw the bottom half of the petal using another quadratic curve<br>        path.addQuadCurve(<br>            to: CGPoint(x: width / 2, y: height / 2),<br>            control: CGPoint(x: width / 4, y: height / 2 + controlHeight)<br>        )<br><br>        return path<br>    }<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VG_anBmwzOFBJQIh_Ootsw.png" /><figcaption>Result of drawing the petal above with the red frame as rect</figcaption></figure><p>Note - In this example, we use a rectangular frame for the petal.</p><p>In this code snippet:</p><ol><li>We start by moving to the center of the rectangle.</li><li>We draw the first half of the petal with a quadratic curve, using a control point that extends upward.</li><li>We draw the second half of the petal with another quadratic curve, using a control point that extends downward.</li></ol><h3>Adjusting the Shape</h3><p>Now that we’ve created a basic flower petal shape, we can further modify the petal by adjusting the control points. By changing the the control points, we can create variations in the petal’s appearance. For example, if the y-coordinate of the control point is closer to the baseline, the petal will appear skinnier. Some examples:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Bp-UEjnlrHCk8Sr4yHhQGg.png" /><figcaption>Some examples of changes in petal when changing control position</figcaption></figure><p>Now, we try to draw two petals in this rectangle. Another petal on the right side matching the one on the left:</p><pre>struct FlowerPetalView: Shape {<br>    func path(in rect: CGRect) -&gt; Path {<br>        var path = Path()<br>        <br>        let width = rect.width<br>        let height = rect.height<br>        let controlWidth = width / 4<br>        let controlHeight = height / 2.5<br>        <br>        // Move to the center of the rectangle<br>        path.move(to: CGPoint(x: width / 2, y: height / 2))<br>        <br>        // 1. Draw the left petal<br>        path.addQuadCurve(<br>            to: CGPoint(x: 0, y: height / 2),<br>            control: CGPoint(x: width / 2 - controlWidth, y: height / 2 - controlHeight)<br>        )<br>        path.addQuadCurve(<br>            to: CGPoint(x: width / 2, y: height / 2),<br>            control: CGPoint(x: width / 2 - controlWidth, y: height / 2 + controlHeight)<br>        )<br>        <br>        // 2. Draw the right petal<br>        path.addQuadCurve(<br>            to: CGPoint(x: width, y: height / 2),<br>            control: CGPoint(x: width / 2 + controlWidth, y: height / 2 - controlHeight)<br>        )<br>        path.addQuadCurve(<br>            to: CGPoint(x: width / 2, y: height / 2),<br>            control: CGPoint(x: width / 2 + controlWidth, y: height / 2 + controlHeight)<br>        )<br>        <br>        return path<br>    }<br>}</pre><p>controlWidth and controlHeight are the distances used to define the control points for the quadratic curves.</p><p>Notice, for right petal the x postion is width/2 + controlWidth contrary to width/2 — controlWidth .</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*punjmlMRDGxdomPokMQMcQ.png" /><figcaption>Drawing two petals — Left and right to the centre</figcaption></figure><h3>Assembling the Super Shape</h3><p>To create a complete flower shape in SwiftUI by assembling multiple petals around a central point, we can utilize the Flower shape. This shape combines multiple instances of a petalPath rotated around the center of a rectangle. Here’s how it works:</p><pre>struct Flower: Shape {<br>    <br>    func path(in rect: CGRect) -&gt; Path {<br>        var path = Path()<br>        let iterationAngle = Double.pi / 3<br>        <br>        // Iterate through each petal angle<br>        for angle in stride(from: 0, to: Double.pi, by: iterationAngle) {<br>            // Create a rotation transformation for the petal<br>            let rotation = CGAffineTransform(rotationAngle: angle)<br>            <br>            // Apply translation to center the petal in the rect<br>            let translation = CGAffineTransform(translationX: rect.width / 2, y: rect.height / 2)<br>            <br>            // Combine rotation and translation<br>            let transform = rotation.concatenating(translation)<br>            <br>            // Apply the transformation to the petal path<br>            let petal = petalPath(in: rect).applying(transform)<br>            <br>            // Add the rotated petal path to the main path<br>            path.addPath(petal)<br>        }<br>        <br>        return path<br>    }<br>    <br>    func petalPath(in rect: CGRect) -&gt; Path {<br>        var path = Path()<br>        <br>        let width = rect.width<br>        let height = rect.height<br>        <br>        path.move(to: CGPoint(x: width / 2, y: height / 2))<br>        <br>        path.addQuadCurve(to: CGPoint(x: 0, y: height / 2),<br>                          control: CGPoint(x: width / 4, y: height / 2 - height / 6))<br>        <br>        path.addQuadCurve(to: CGPoint(x: width / 2, y: height / 2),<br>                          control: CGPoint(x: width / 4, y: height / 2 + height / 6))<br>        <br>        path.addQuadCurve(to: CGPoint(x: width, y: height / 2),<br>                          control: CGPoint(x: width * 3 / 4, y: height / 2 - height / 6))<br>        <br>        path.addQuadCurve(to: CGPoint(x: width / 2, y: height / 2),<br>                          control: CGPoint(x: width * 3 / 4, y: height / 2 + height / 6))<br>        <br>        return path<br>    }<br>}</pre><p><strong>Flower Shape (</strong><strong>path(in rect: CGRect) -&gt; Path)</strong>:</p><ul><li><strong>Iteration</strong>: The for loop iterates through angles (angle) from 0 to 2 * π (360 degrees) in steps of π / 3 (60 degrees), which positions each petal around the center of the rectangle.</li><li><strong>Rotation Transformation</strong>: For each angle, a rotation transformation (rotation) is created using CGAffineTransform(rotationAngle: angle).</li><li><strong>Translation</strong>: A translation transformation (translation) centers the petal in the rect using CGAffineTransform(translationX: rect.width / 2, y: rect.height / 2).</li><li><strong>Combining Transformations</strong>: The rotation and translation transformations are combined (concatenating) into a single transformation (transform).</li><li><strong>Applying Transformations</strong>: The combined transformation (transform) is applied (applying) to the petalPath to rotate and position each petal around the center.</li><li><strong>Adding to Main Path</strong>: The rotated and positioned petalPath is added (path.addPath(petal)) to the main paththat represents the complete flower shape.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3ydwq0hoJYIUcXHjej8MHg.png" /><figcaption>Flower assembled using subpaths with rotation and combinations</figcaption></figure><p>The number of petals in this pattern is 2*(number of times loop is run) . Thus, if iterationAngle is updated to pi/2 , the number of petals would be 2*2 = 4 . After a cetain number of petals, the rotations would lead to overlapping petals.</p><h3>Animating the Shape</h3><p>Animating shapes in SwiftUI involves leveraging SwiftUI’s declarative syntax and animation modifiers to create dynamic and engaging visual effects.</p><pre>struct FlowerView: View {<br>    //1<br>    @State private var visiblePetalTrim = 0.0<br>    <br>    var body: some View {<br>        ZStack {<br>            RandomlyMovingShapesView()<br>                .frame(width: 400, height: 400)<br>            //2<br>            Flower()<br>                .trim(from: 0.0, to: visiblePetalTrim)<br>                .stroke(.cyan, lineWidth: 2)<br>                .frame(width: 300, height: 300)<br>                .onAppear() {<br>                    //3<br>                    withAnimation(.easeInOut(duration: 9)) {<br>                        visiblePetalTrim = 1.0<br>                    }<br>                }<br>        }<br>    }<br>}</pre><ol><li><strong>State Declaration</strong>: This state variable controls how much of the flower’s petals are visible. It starts at 0.0 (fully hidden) and will animate to 1.0 (fully visible).</li><li><strong>Flower Shape</strong>: We use 12 petals in Flower for this example. iterationAngle is set as pi/6 for the same.<br>trim(from: 0.0, to: visiblePetalTrim): This modifier animates the drawing of the flower shape from 0.0(start) to visiblePetalTrim (end), controlled by the state variable.</li><li><strong>Animation Block</strong>: Initiates an animation block with a specified animation type (here, easeInOut) and duration (9 seconds).</li></ol><p>Inside the animation block, visiblePetalTrim = 1.0 gradually changes visiblePetalTrim from 0.0 to 1.0 over the duration of the animation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*osZm4k3DWHCJNx5sDP0GWQ.gif" /><figcaption>Result of animating the Shape</figcaption></figure><h3>Conclusion</h3><p>In this article, we explored the fascinating world of creating custom shapes and animations in SwiftUI, highlighting its powerful capabilities and ease of use. Here are the key takeaways:</p><ol><li><strong>Creating Custom Shapes</strong>: We learned how to define intricate shapes using SwiftUI’s Shape protocol and its various path-drawing methods. From basic polygons to complex floral patterns, SwiftUI empowers developers to craft visually compelling UI elements effortlessly.</li><li><strong>Flexibility and Creativity</strong>: SwiftUI’s approach to creating and animating shapes is remarkably flexible, allowing for seamless integration with other SwiftUI views and gestures. This flexibility encourages creativity in UI design, enabling developers to innovate and deliver unique user experiences.</li></ol><p>Have you used custom shapes in your app? I would love to hear about your experiences and any creative ways you have found to implement it! Reach out to me on X (formerly Twitter) on @<a href="https://x.com/garimabothraa/">garimabothraa</a>!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2755139d880f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Streamlining Menu Browsing with OCR Text Recognition]]></title>
            <link>https://medium.com/swiggy-bytes/streamlining-menu-browsing-with-ocr-text-recognition-71e89f5bda28?source=rss-8954ff43cd93------2</link>
            <guid isPermaLink="false">https://medium.com/p/71e89f5bda28</guid>
            <category><![CDATA[vision-kit]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[ocr]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[swiggy]]></category>
            <dc:creator><![CDATA[Garima Bothra]]></dc:creator>
            <pubDate>Thu, 11 Jul 2024 07:53:18 GMT</pubDate>
            <atom:updated>2024-08-13T07:17:08.023Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SwR7D7Q0qakHxNr9lgrY8Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*emtDkCh9vA5qUqTlpXIQVQ.png" /><figcaption>Image credits: Getty Images</figcaption></figure><p>In the vibrant world of modern dining, platforms like Swiggy’s Dineout offer endless culinary adventures. However, the excitement often wanes when navigating extensive restaurant menus becomes cumbersome. Searching for a favorite dish, like pizza, amidst a lengthy menu can be time-consuming and frustrating, leading to a sub-par user experience.</p><p>A solution is required to swiftly and precisely extract and search items from these menus, facilitating customers in effortlessly locating specific dishes. With Optical Character Recognition (OCR) technology, users can now effortlessly type in their desired dish and be swiftly directed to relevant menu sections with their preferences highlighted. This article delves into the iOS implementation of OCR, showcasing its potential to transform menu navigation and enhance the user experience.</p><h4>What’s the Problem?</h4><p>In our quest to reimagine menu item navigation, we tried to target one major problem with the existing menus:</p><ul><li><strong>Tedious Search</strong>: Searching for specific dishes like pizza can be time-consuming, and important items might be overlooked.</li></ul><h4><strong>The Vision Behind the Magic:</strong></h4><p>To simplify menu navigation, we utilized Apple’s <a href="https://developer.apple.com/documentation/visionkit"><strong>VisionKit</strong></a>. This powerful framework provides a range of solutions for image analysis, including text recognition, which we have leveraged for our OCR process.</p><h4><strong>Unlocking the Power of on-device OCR:</strong></h4><p>We utilise VisionKit’s <strong>OCR</strong> capabilities to extract text from images. On-device OCR processes images directly on the user’s device. VisionKit analyzes pixel information and isolates important data and extract text without the need for internet connectivity, ensuring privacy and real-time performance.</p><p>This is a crucial step as it transforms the visual data from the images into a format that can be processed and analysed in real-time. The extracted text is then indexed and made searchable, thus, enabling users to easily look for specific items.</p><h4><strong>Seamless Search, Sensational Results:</strong></h4><p>Following text extraction, a <strong>search functionality</strong> allows users to input their desired item, <strong>highlighting these items</strong> within the menu. This simplifies the process of finding specific dishes or beverages, enhancing the overall user experience by making navigation more intuitive and less time-consuming.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wLhRtLcOsS2c8Ht44zc3Tg.png" /><figcaption>Flow for searching on menu</figcaption></figure><p>The end result of this implementation is a more <strong>user-friendly</strong> solution for browsing restaurant menus. This is particularly beneficial for menus that span multiple pages, as it highlights the search results on all pages.</p><p>Now when you crave the same authentic Italian Margherita pizza, simply input “Margherita”, and like magic, the app dynamically highlights appropriate pizza options within the menu and takes you to the response.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1XhddtyxHSbVNWuV962dlA.gif" /><figcaption>Search Experience on Dineout Menu</figcaption></figure><p>In conclusion, our solution significantly improves the<strong> ease and efficiency of menu navigation</strong>, providing a more enjoyable and <strong>convenient pre-dining experience</strong> for all users.</p><h4><strong>Implementation:</strong></h4><p>Ready to peek under the hood? In this section, we’ll dive into the technical magic behind our OCR integration. Discover how we transformed searching menus from a chore into a delightful experience.</p><p>We have created a function — fetchTexts to fetch the text from the given image and draw highlights around the text blocks identified when isRender is true else we call the function didFinishDetection with the string results. This function is explained in detail below:</p><pre>/// Fetches the text from the given image and calls the appropriate method based on the `isRender` flag.<br>///<br>/// - Parameters:<br>///   - cgImage: The CGImage representation of the image.<br>///   - isRender: A Boolean value that determines whether to render the image or not.<br>///   - image: The UIImage representation of the image.<br>///   - searchText: The text to search for in the image.<br>    <br>    fileprivate func fetchTexts(_ cgImage: CGImage, _ isRender: Bool, _ image: UIImage, _ searchText: String?) {<br>        // 1<br>        let requestHandler = VNImageRequestHandler(cgImage: cgImage)<br>        // 2<br>        let request = VNRecognizeTextRequest { (request, _) in<br>            // 3<br>            guard let observations = request.results as? [VNRecognizedTextObservation] else {<br>                return<br>            }<br>            if let searchText, isRender {<br>                let boundingRects: [(frame: CGRect, string: String)] = observations.compactMap { observation in<br>                    <br>                    // 4<br>                    guard let candidate = observation.topCandidates(1).first else { return (.zero, &quot;&quot;) }<br>                    <br>                    // 5<br>                    let stringRange = candidate.string.startIndex..&lt;candidate.string.endIndex<br>                    let boxObservation = try? candidate.boundingBox(for: stringRange)<br>                    <br>                    // 6<br>                    let boundingBox = boxObservation?.boundingBox ?? .zero<br>                    let frame = self.getConvertedRect(boundingBox: boundingBox, inImage: image.size, containedIn: self.scaledSize)<br>                    return (frame, candidate.string)<br>                }<br>                for item in boundingRects {<br>                    // 7<br>                    self.drawBounds(item.frame, text: item.string, searchText: searchText)<br>                }<br>            } else {<br>                let mappedStrings: [String] = observations.compactMap { observation in<br>                    guard let candidate = observation.topCandidates(1).first else { return nil }<br>                    return candidate.string<br>                }<br>                // 8<br>                didFinishDetection(mappedStrings)<br>            }<br>        }<br>        // 9<br>        request.recognitionLevel = .accurate<br>        <br>        do {<br>            // 10<br>            try requestHandler.perform([request])<br>        } catch {<br>            print(&quot;Unable to perform the requests: \(error).&quot;)<br>        }<br>    }<br>}<br><br>// Steps 1 - 10 are explained below</pre><p>This method utilizes VisionKit’s text recognition capabilities to extract text from a given image represented as a CGImage. It accepts parameters such as isRender to determine whether to render the image, image representing the UIImage representation of the image, and searchText specifying the text to search for in the image.</p><p><strong>Text Recognition Process</strong>: Within the fetchTexts method, a VNRecognizeTextRequest is created to perform text recognition on the provided image. Detected text observations are obtained from the request results, and based on the isRender flag, either bounding rectangles and strings are extracted for rendering highlights on the image, or the recognized strings are mapped and passed to the didFinishDetection method. Step-by-step explanation of how the code works:</p><ol><li>Create image request handler for searching in the image</li><li>Create a VisionKit request to convert the image to smaller blocks with text</li><li>Extract the result of OCR as array of smaller text blocks</li><li>Find the top observation — i.e. most probable result</li><li>Find the bounding-box observation for the string range.</li><li>Get the normalized CGRect value for the frame of observation.</li><li>Draw the highlights for the searched text around the frames from last step. (Implement a drawBounds function for this)</li><li>Call completion handler for detected texts with the array of strings identified</li><li>The recognition level for image request handler is set as accurate. There are two recognition levels provided for the same: `accurate` and fast</li><li>Perform the text-recognition request in try catch block</li></ol><h4>Menu Mastery: Impact and Beyond</h4><p>The adoption of OCR technology heralds a new era of dining discovery on Swiggy Dineout, with far-reaching implications for users and restaurateurs alike. The impact extends beyond mere convenience, encompassing:</p><ul><li><strong>Informed Decision Making</strong>: Users can effortlessly search for their favorite items amidst extensive menu listings.</li><li><strong>Intuitive Interface</strong>: Our solution offers a visually engaging and user-friendly experience.</li></ul><h4><strong>Conclusion</strong></h4><p>As the culinary landscape continues to evolve, our commitment to innovation remains unwavering. With OCR technology as our guiding light, we’ve embarked on a journey to redefine the way users discover and savor the flavors of the world. Together, let us unlock the menu magic and embark on a culinary adventure like never before. Welcome to a world where every dish tells a story, and every bite is a revelation — welcome to the future of dining discovery on Swiggy Dineout.</p><p>The potential of VisionKit extends beyond Swiggy Dineout. Other apps can also leverage its powerful text recognition capabilities to enhance user experiences across various domains, from document scanning to real-time translation. As for Swiggy, the possibilities with VisionKit are vast. Imagine using it to detect allergens, suggest popular dishes, or even provide nutritional information at a glance. By continuing to explore and innovate with VisionKit, Swiggy can further revolutionize the way we interact with food, making dining not only more convenient but also more informed and enjoyable.</p><p>Special thanks to <a href="https://medium.com/u/94206509fe3b">Darshita Golchha</a>, <a href="https://medium.com/u/828c8792b774">Priyam Dutta</a>, <a href="https://medium.com/u/ede4f93130a7">Agam Mahajan</a>, <a href="https://medium.com/u/1bf9bdb89775">Raj Gohil</a>, <a href="https://medium.com/u/400467f4ffab">Tushar Tayal</a>, <a href="https://medium.com/u/881f7762391">Preetichandak</a> and <a href="https://medium.com/u/afd6d52c92c2">Saptarshi Prakash</a> for the execution of this project.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=71e89f5bda28" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swiggy-bytes/streamlining-menu-browsing-with-ocr-text-recognition-71e89f5bda28">Streamlining Menu Browsing with OCR Text Recognition</a> was originally published in <a href="https://medium.com/swiggy-bytes">Swiggy Bytes — Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>